<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * Description:
 *     a simple version of meta state service for development
 *
 * Revision history:
 *     2015-11-04, @imzhenyu (Zhenyu.Guo@microsoft.com), setup the sketch
 *     2015-11-11, Tianyi WANG, first version done
 *     xxxx-xx-xx, author, fix bug about xxx
 */

# include "meta_state_service_simple.h"
# include <dsn/internal/task.h>

# include <stack>
# include <utility>

namespace dsn
{
    namespace dist
    {
        // path: /, /n1/n2, /n1/n2/, /n2/n2/n3
        std::string meta_state_service_simple::normalize_path(const std::string& s)
        {
            if (s.empty() || s[0] != '/')
                return "";
            if (s.length() > 1 && *s.rbegin() == '/')
                return s.substr(0, s.length() - 1);
            return s;
        }

        error_code meta_state_service_simple::extract_name_parent_from_path(
            const std::string& s,
            /*out*/ std::string& name,
            /*out*/ std::string& parent
            )
        {
            auto pos = s.find_last_of('/');
            if (pos == std::string::npos)
                return ERR_INVALID_PARAMETERS;

            name = s.substr(pos + 1);
            if (pos > 0)
                parent = s.substr(0, pos);
            else
                parent = "/";
            return ERR_OK;
        }

        static void __err_cb_bind_and_enqueue(
            task_ptr lock_task,
            error_code err,
            int delay_milliseconds = 0
            )
        {
            auto t = dynamic_cast<safe_late_task<meta_state_service::err_callback>*>(lock_task.get());

            t->bind_and_enqueue(
                [&](meta_state_service::err_callback& cb)
                {
                    return bind(cb, err);
                },
                delay_milliseconds
                );
        }

        void meta_state_service_simple::write_log(blob&& log_blob, std::function<error_code()> internal_operation, task_ptr task)
        {
            _log_lock.lock();
            uint64_t log_offset = _offset;
            _offset += log_blob.length();
            auto continuation_task = std::unique_ptr<operation>(new operation(false, [=](bool log_succeed)
            {
                dassert(log_succeed, "we cannot handle logging failure now");
                __err_cb_bind_and_enqueue(task, internal_operation(), 0);
            }));
            auto continuation_task_ptr = continuation_task.get();
            _task_queue.emplace(move(continuation_task));
            _log_lock.unlock();

            file::write(
                _log,
                log_blob.buffer_ptr(),
                log_blob.length(),
                log_offset,
                LPC_META_STATE_SERVICE_SIMPLE_INTERNAL,
                this,
                [=](error_code err, size_t bytes)
                {
                    dassert(err == ERR_OK && bytes == log_blob.length(), "we cannot handle logging failure now");
                    _log_lock.lock();
                    continuation_task_ptr->done = true;
                    while (!_task_queue.empty())
                    {
                        if (!_task_queue.front()->done)
                        {
                            break;
                        }
                        _task_queue.front()->cb(true);
                        _task_queue.pop();
                    }
                    _log_lock.unlock();
                }
                );
        }

        error_code meta_state_service_simple::create_node_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it != _quick_map.end())
                return ERR_NODE_ALREADY_EXIST;

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            if (parent_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;

            state_node* n = new state_node(name, parent_it->second, value);
            parent_it->second->children.insert(quick_map::value_type(name, n));
            _quick_map.insert(quick_map::value_type(path, n));
            return ERR_OK;
        }

        error_code meta_state_service_simple::delete_node_internal(const std::string& node, bool recursive)
        {
            auto path = normalize_path(node);
            if (path == "/")
                return ERR_INVALID_PARAMETERS; // cannot delete root
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            if (!recursive && !me_it->second->children.empty())
                return ERR_INVALID_PARAMETERS;

            struct delete_state
            {
                std::string path;
                state_node *node;
                decltype(state_node::children)::iterator next_child_to_delete;
            };
            std::stack<delete_state> delete_stack;
            delete_stack.push({ path, me_it->second, me_it->second->children.begin() });
            for (; !delete_stack.empty();)
            {
                auto &node_pair = delete_stack.top();
                if (node_pair.node->children.end() == node_pair.next_child_to_delete)
                {
                    auto delnum = _quick_map.erase(node_pair.path);
                    dassert(delnum == 1, "inconsistent state between quick map and tree");
                    delete node_pair.node;
                    delete_stack.pop();
                }
                else
                {
                    auto child_it = node_pair.next_child_to_delete;
                    delete_stack.push({ node_pair.path + "/" + child_it->second->name, child_it->second, child_it->second->children.begin() });
                    ++node_pair.next_child_to_delete;
                }
            }

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            dassert(parent_it != _quick_map.end(), "unable to find parent node");
            //XXX we cannot delete root, right?

            auto erase_num = parent_it->second->children.erase(name);
            dassert(erase_num == 1, "inconsistent state between quick map and tree");
            return ERR_OK;
        }

        error_code meta_state_service_simple::set_data_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto it = _quick_map.find(path);
            if (it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            it->second->data = value;
            return ERR_OK;
        }

        error_code meta_state_service_simple::initialize(const char* dir)
        {
            _offset = 0;
            std::string log_path = dsn::utils::filesystem::path_combine(dir, "meta_state_service.log");
            if (utils::filesystem::file_exists(log_path))
            {
                if (FILE* fd = fopen(log_path.c_str(), "rb"))
                {
                    for (;;)
                    {
                        log_header header;
                        if (fread(&header, sizeof(log_header), 1, fd) != 1)
                        {
                            break;
                        }
                        if (header.magic != log_header::default_magic)
                        {
                            break;
                        }
                        std::shared_ptr<char> buffer(new char[header.size]);
                        if (fread(buffer.get(), header.size, 1, fd) != 1)
                        {
                            break;
                        }
                        _offset += sizeof(header) + header.size;
                        blob blob_wrapper(buffer, (int)header.size);
                        binary_reader reader(blob_wrapper);
                        int op_type;
                        unmarshall(reader, op_type);
                        switch (static_cast<operation_type>(op_type))
                        {
                        case operation_type::create_node:
                        {
                            std::string node;
                            blob data;
                            create_node_log::parse(reader, node, data);
                            create_node_internal(node, data).end_tracking();
                            break;
                        }
                        case operation_type::delete_node:
                        {
                            std::string node;
                            bool recursively_delete;
                            delete_node_log::parse(reader, node, recursively_delete);
                            delete_node_internal(node, recursively_delete).end_tracking();
                            break;
                        }
                        case operation_type::set_data:
                        {
                            std::string node;
                            blob data;
                            set_data_log::parse(reader, node, data);
                            set_data_internal(node, data).end_tracking();
                            break;
                        }
                        default:
                            //The log is complete but its content is modified by cosmic ray. This is unacceptable
                            dassert(false, "meta state server log corrupted");
                        }
                    }
                    fclose(fd);
                }
            }

            _log = dsn_file_open(log_path.c_str(), O_RDWR | O_CREAT | O_BINARY, 0666);
            if (!_log)
            {
                derror("open file failed: %s", log_path.c_str());
                return ERR_FILE_OPERATION_FAILED;
            }
            return ERR_OK;
        }

        task_ptr meta_state_service_simple::create_node(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_create,
            const blob& value,
            clientlet* tracker )
        {
            auto task = tasking::create_late_task(cb_code, cb_create, 0, tracker);
            write_log(
                create_node_log::get_log(node, value),
                [=]{
                    return create_node_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::delete_node(
            const std::string& node,
            bool recursively_delete,
            task_code cb_code,
            const err_callback& cb_delete,
            clientlet* tracker)
        {
            auto task = tasking::create_late_task(cb_code, cb_delete, 0, tracker);
            write_log(
                delete_node_log::get_log(node, recursively_delete),
                [=] {
                    return delete_node_internal(node, recursively_delete);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::node_exist(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_exist,
            clientlet* tracker)
        {
            error_code err;
            {
                zauto_lock _(_state_lock);
                err = _quick_map.find(normalize_path(node)) != _quick_map.end() ? ERR_OK : ERR_PATH_NOT_FOUND;
            }
            return tasking::enqueue(
                cb_code,
                tracker,
                [=]()
                {
                    cb_exist(err);
                }
                );
        }

        task_ptr meta_state_service_simple::get_data(
            const std::string& node,
            task_code cb_code,
            const err_value_callback& cb_get_data,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_data(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                auto data_copy = me_it->second->data;
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_data(ERR_OK, std::move(data_copy));
                    }
                    );
            }
        }

        task_ptr meta_state_service_simple::set_data(
            const std::string& node,
            const blob& value,
            task_code cb_code,
            const err_callback& cb_set_data,
            clientlet* tracker)
        {
            auto task = tasking::create_late_task(cb_code, cb_set_data, 0, tracker);
            write_log(
                set_data_log::get_log(node, value),
                [=] {
                    return set_data_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::get_children(
            const std::string& node,
            task_code cb_code,
            const err_stringv_callback& cb_get_children,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_children(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                std::vector<std::string> result;
                for (auto &child_pair : me_it->second->children)
                {
                    result.push_back(child_pair.first);
                }
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_children(ERR_OK, move(result));
                    }
                    );
            }
        }

        meta_state_service_simple::~meta_state_service_simple()
        {
            dsn_file_close(_log);
        }
    }
}
<commit_msg>fix bug: new created task should be received into ptr, or it may be destroyed before return<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * Description:
 *     a simple version of meta state service for development
 *
 * Revision history:
 *     2015-11-04, @imzhenyu (Zhenyu.Guo@microsoft.com), setup the sketch
 *     2015-11-11, Tianyi WANG, first version done
 *     xxxx-xx-xx, author, fix bug about xxx
 */

# include "meta_state_service_simple.h"
# include <dsn/internal/task.h>

# include <stack>
# include <utility>

namespace dsn
{
    namespace dist
    {
        // path: /, /n1/n2, /n1/n2/, /n2/n2/n3
        std::string meta_state_service_simple::normalize_path(const std::string& s)
        {
            if (s.empty() || s[0] != '/')
                return "";
            if (s.length() > 1 && *s.rbegin() == '/')
                return s.substr(0, s.length() - 1);
            return s;
        }

        error_code meta_state_service_simple::extract_name_parent_from_path(
            const std::string& s,
            /*out*/ std::string& name,
            /*out*/ std::string& parent
            )
        {
            auto pos = s.find_last_of('/');
            if (pos == std::string::npos)
                return ERR_INVALID_PARAMETERS;

            name = s.substr(pos + 1);
            if (pos > 0)
                parent = s.substr(0, pos);
            else
                parent = "/";
            return ERR_OK;
        }

        static void __err_cb_bind_and_enqueue(
            task_ptr lock_task,
            error_code err,
            int delay_milliseconds = 0
            )
        {
            auto t = dynamic_cast<safe_late_task<meta_state_service::err_callback>*>(lock_task.get());

            t->bind_and_enqueue(
                [&](meta_state_service::err_callback& cb)
                {
                    return bind(cb, err);
                },
                delay_milliseconds
                );
        }

        void meta_state_service_simple::write_log(blob&& log_blob, std::function<error_code()> internal_operation, task_ptr task)
        {
            _log_lock.lock();
            uint64_t log_offset = _offset;
            _offset += log_blob.length();
            auto continuation_task = std::unique_ptr<operation>(new operation(false, [=](bool log_succeed)
            {
                dassert(log_succeed, "we cannot handle logging failure now");
                __err_cb_bind_and_enqueue(task, internal_operation(), 0);
            }));
            auto continuation_task_ptr = continuation_task.get();
            _task_queue.emplace(move(continuation_task));
            _log_lock.unlock();

            file::write(
                _log,
                log_blob.buffer_ptr(),
                log_blob.length(),
                log_offset,
                LPC_META_STATE_SERVICE_SIMPLE_INTERNAL,
                this,
                [=](error_code err, size_t bytes)
                {
                    dassert(err == ERR_OK && bytes == log_blob.length(), "we cannot handle logging failure now");
                    _log_lock.lock();
                    continuation_task_ptr->done = true;
                    while (!_task_queue.empty())
                    {
                        if (!_task_queue.front()->done)
                        {
                            break;
                        }
                        _task_queue.front()->cb(true);
                        _task_queue.pop();
                    }
                    _log_lock.unlock();
                }
                );
        }

        error_code meta_state_service_simple::create_node_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it != _quick_map.end())
                return ERR_NODE_ALREADY_EXIST;

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            if (parent_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;

            state_node* n = new state_node(name, parent_it->second, value);
            parent_it->second->children.insert(quick_map::value_type(name, n));
            _quick_map.insert(quick_map::value_type(path, n));
            return ERR_OK;
        }

        error_code meta_state_service_simple::delete_node_internal(const std::string& node, bool recursive)
        {
            auto path = normalize_path(node);
            if (path == "/")
                return ERR_INVALID_PARAMETERS; // cannot delete root
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            if (!recursive && !me_it->second->children.empty())
                return ERR_INVALID_PARAMETERS;

            struct delete_state
            {
                std::string path;
                state_node *node;
                decltype(state_node::children)::iterator next_child_to_delete;
            };
            std::stack<delete_state> delete_stack;
            delete_stack.push({ path, me_it->second, me_it->second->children.begin() });
            for (; !delete_stack.empty();)
            {
                auto &node_pair = delete_stack.top();
                if (node_pair.node->children.end() == node_pair.next_child_to_delete)
                {
                    auto delnum = _quick_map.erase(node_pair.path);
                    dassert(delnum == 1, "inconsistent state between quick map and tree");
                    delete node_pair.node;
                    delete_stack.pop();
                }
                else
                {
                    auto child_it = node_pair.next_child_to_delete;
                    delete_stack.push({ node_pair.path + "/" + child_it->second->name, child_it->second, child_it->second->children.begin() });
                    ++node_pair.next_child_to_delete;
                }
            }

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            dassert(parent_it != _quick_map.end(), "unable to find parent node");
            //XXX we cannot delete root, right?

            auto erase_num = parent_it->second->children.erase(name);
            dassert(erase_num == 1, "inconsistent state between quick map and tree");
            return ERR_OK;
        }

        error_code meta_state_service_simple::set_data_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto it = _quick_map.find(path);
            if (it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            it->second->data = value;
            return ERR_OK;
        }

        error_code meta_state_service_simple::initialize(const char* dir)
        {
            _offset = 0;
            std::string log_path = dsn::utils::filesystem::path_combine(dir, "meta_state_service.log");
            if (utils::filesystem::file_exists(log_path))
            {
                if (FILE* fd = fopen(log_path.c_str(), "rb"))
                {
                    for (;;)
                    {
                        log_header header;
                        if (fread(&header, sizeof(log_header), 1, fd) != 1)
                        {
                            break;
                        }
                        if (header.magic != log_header::default_magic)
                        {
                            break;
                        }
                        std::shared_ptr<char> buffer(new char[header.size]);
                        if (fread(buffer.get(), header.size, 1, fd) != 1)
                        {
                            break;
                        }
                        _offset += sizeof(header) + header.size;
                        blob blob_wrapper(buffer, (int)header.size);
                        binary_reader reader(blob_wrapper);
                        int op_type;
                        unmarshall(reader, op_type);
                        switch (static_cast<operation_type>(op_type))
                        {
                        case operation_type::create_node:
                        {
                            std::string node;
                            blob data;
                            create_node_log::parse(reader, node, data);
                            create_node_internal(node, data).end_tracking();
                            break;
                        }
                        case operation_type::delete_node:
                        {
                            std::string node;
                            bool recursively_delete;
                            delete_node_log::parse(reader, node, recursively_delete);
                            delete_node_internal(node, recursively_delete).end_tracking();
                            break;
                        }
                        case operation_type::set_data:
                        {
                            std::string node;
                            blob data;
                            set_data_log::parse(reader, node, data);
                            set_data_internal(node, data).end_tracking();
                            break;
                        }
                        default:
                            //The log is complete but its content is modified by cosmic ray. This is unacceptable
                            dassert(false, "meta state server log corrupted");
                        }
                    }
                    fclose(fd);
                }
            }

            _log = dsn_file_open(log_path.c_str(), O_RDWR | O_CREAT | O_BINARY, 0666);
            if (!_log)
            {
                derror("open file failed: %s", log_path.c_str());
                return ERR_FILE_OPERATION_FAILED;
            }
            return ERR_OK;
        }

        task_ptr meta_state_service_simple::create_node(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_create,
            const blob& value,
            clientlet* tracker )
        {
            task_ptr task = tasking::create_late_task(cb_code, cb_create, 0, tracker);
            write_log(
                create_node_log::get_log(node, value),
                [=]{
                    return create_node_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::delete_node(
            const std::string& node,
            bool recursively_delete,
            task_code cb_code,
            const err_callback& cb_delete,
            clientlet* tracker)
        {
            task_ptr task = tasking::create_late_task(cb_code, cb_delete, 0, tracker);
            write_log(
                delete_node_log::get_log(node, recursively_delete),
                [=] {
                    return delete_node_internal(node, recursively_delete);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::node_exist(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_exist,
            clientlet* tracker)
        {
            error_code err;
            {
                zauto_lock _(_state_lock);
                err = _quick_map.find(normalize_path(node)) != _quick_map.end() ? ERR_OK : ERR_PATH_NOT_FOUND;
            }
            return tasking::enqueue(
                cb_code,
                tracker,
                [=]()
                {
                    cb_exist(err);
                }
                );
        }

        task_ptr meta_state_service_simple::get_data(
            const std::string& node,
            task_code cb_code,
            const err_value_callback& cb_get_data,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_data(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                auto data_copy = me_it->second->data;
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_data(ERR_OK, std::move(data_copy));
                    }
                    );
            }
        }

        task_ptr meta_state_service_simple::set_data(
            const std::string& node,
            const blob& value,
            task_code cb_code,
            const err_callback& cb_set_data,
            clientlet* tracker)
        {
            task_ptr task = tasking::create_late_task(cb_code, cb_set_data, 0, tracker);
            write_log(
                set_data_log::get_log(node, value),
                [=] {
                    return set_data_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::get_children(
            const std::string& node,
            task_code cb_code,
            const err_stringv_callback& cb_get_children,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_children(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                std::vector<std::string> result;
                for (auto &child_pair : me_it->second->children)
                {
                    result.push_back(child_pair.first);
                }
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_children(ERR_OK, move(result));
                    }
                    );
            }
        }

        meta_state_service_simple::~meta_state_service_simple()
        {
            dsn_file_close(_log);
        }
    }
}
<|endoftext|>


<commit_before>#ifndef ENGINE_COMMON_UTILITY_CONTAINER_SYNC_QUEUE_HPP
#define ENGINE_COMMON_UTILITY_CONTAINER_SYNC_QUEUE_HPP

#include <queue>
#include <mutex>

#pragma once

namespace engine
{
	template<class T> class sync_queue_t final
	{

	public:

		bool is_empty()
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			return queue.empty();
		}

		auto size()
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			return queue.size();
		}

		T pop()
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			T front = std::move(queue.front());
			queue.pop();

			return std::move(front);
		}

		T pop_check(const T && if_empty)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			if (queue.empty()) return std::move(if_empty);

			T front = std::move(queue.front());
			queue.pop();

			return std::move(front);
		}

		void push(const T & value)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			queue.push(value);
		}

		void push(T && value)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			queue.push(std::move(value));
		}

		template< class... Args > void emplace(Args&&... args)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			queue.emplace(std::forward<Args>(args)...);
		}

	private:

		std::recursive_mutex mutex;
		std::queue<T> queue;

	};
}

#endif<commit_msg>Fixed function signature for pop_check<commit_after>#ifndef ENGINE_COMMON_UTILITY_CONTAINER_SYNC_QUEUE_HPP
#define ENGINE_COMMON_UTILITY_CONTAINER_SYNC_QUEUE_HPP

#include <queue>
#include <mutex>

#pragma once

namespace engine
{
	template<class T> class sync_queue_t final
	{

	public:

		bool is_empty()
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			return queue.empty();
		}

		auto size()
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			return queue.size();
		}

		T pop()
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			T front = std::move(queue.front());
			queue.pop();

			return std::move(front);
		}

		T pop_check(T if_empty)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			if (queue.empty()) return std::move(if_empty);

			T front = std::move(queue.front());
			queue.pop();

			return std::move(front);
		}

		void push(const T & value)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			queue.push(value);
		}

		void push(T && value)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			queue.push(std::move(value));
		}

		template< class... Args > void emplace(Args&&... args)
		{
			std::lock_guard<std::recursive_mutex> guard(mutex);

			queue.emplace(std::forward<Args>(args)...);
		}

	private:

		std::recursive_mutex mutex;
		std::queue<T> queue;

	};
}

#endif<|endoftext|>


<commit_before>/*
    Copyright 2008 Brain Research Institute, Melbourne, Australia

    Written by J-Donald Tournier, 27/06/08.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "app.h"
#include "progressbar.h"
#include "image/buffer.h"
#include "image/voxel.h"
#include "image/axis.h"
#include "image/threaded_copy.h"
#include "image/adapter/extract.h"
#include "image/adapter/permute_axes.h"
#include "dwi/gradient.h"

MRTRIX_APPLICATION

using namespace MR;
using namespace App;

void usage ()
{
  DESCRIPTION
  + "perform conversion between different file types and optionally "
  "extract a subset of the input image."

  + "If used correctly, this program can be a very useful workhorse. "
  "In addition to converting images between different formats, it can "
  "be used to extract specific studies from a data set, extract a "
  "specific region of interest, or flip the images.";

  ARGUMENTS
  + Argument ("input", "the input image.").type_image_in ()
  + Argument ("ouput", "the output image.").type_image_out ();

  OPTIONS
  + Option ("coord",
            "extract data from the input image only at the coordinates specified.")
  .allow_multiple()
  + Argument ("axis").type_integer (0, 0, std::numeric_limits<int>::max())
  + Argument ("coord").type_sequence_int()

  + Option ("vox",
            "change the voxel dimensions of the output image. The new sizes should "
            "be provided as a comma-separated list of values. Only those values "
            "specified will be changed. For example: 1,,3.5 will change the voxel "
            "size along the x & z axes, and leave the y-axis voxel size unchanged.")
  + Argument ("sizes").type_sequence_float()

  + Option ("stride",
            "specify the strides of the output data in memory, as a comma-separated list. "
            "The actual strides produced will depend on whether the output image "
            "format can support it.")
  + Argument ("spec").type_sequence_int()

  + Option ("axes",
            "specify the axes from the input image that will be used to form the output "
            "image. This allows the permutation, ommission, or addition of axes into the "
            "output image. The axes should be supplied as a comma-separated list of axes. "
            "Any ommitted axes must have dimension 1. Axes can be inserted by supplying "
            "-1 at the corresponding position in the list.")
  + Argument ("axes").type_sequence_int()

  + Option ("prs",
            "assume that the DW gradients are specified in the PRS frame (Siemens DICOM only).")

  + DataType::options()

  + DWI::GradOption

  + OptionGroup ("for complex input images")
  + Option ("real",
      "return the real part of each voxel value")
  + Option ("imag",
      "return the imaginary part of each voxel value")
  + Option ("phase",
      "return the phase of each voxel value")
  + Option ("magnitude",
      "return the magnitude of each voxel value");
}

template <class InfoType>
inline std::vector<int> set_header (
  Image::Header& header,
  const InfoType& input)
{
  header.info() = input.info();

  header.intensity_offset() = 0.0;
  header.intensity_scale() = 1.0;

  Options opt = get_options ("axes");
  std::vector<int> axes;
  if (opt.size()) {
    axes = opt[0][0];
    header.set_ndim (axes.size());
    for (size_t i = 0; i < axes.size(); ++i) {
      if (axes[i] >= static_cast<int> (input.ndim()))
        throw Exception ("axis supplied to option -axes is out of bounds");
      header.dim(i) = axes[i] < 0 ? 1 : input.dim (axes[i]);
    }
  }

  opt = get_options ("vox");
  if (opt.size()) {
    std::vector<float> vox = opt[0][0];
    if (vox.size() > header.ndim())
      throw Exception ("too many axes supplied to -vox option");
    for (size_t n = 0; n < vox.size(); ++n) {
      if (std::isfinite (vox[n]))
        header.vox(n) = vox[n];
    }
  }

  opt = get_options ("stride");
  if (opt.size()) {
    std::vector<int> strides = opt[0][0];
    if (strides.size() > header.ndim())
      throw Exception ("too many axes supplied to -stride option");
    for (size_t n = 0; n < strides.size(); ++n)
      header.stride(n) = strides[n];
  }

  opt = get_options ("prs");
  if (opt.size() &&
      header.DW_scheme().rows() &&
      header.DW_scheme().columns()) {
    Math::Matrix<float>& M (header.DW_scheme());
    for (size_t row = 0; row < M.rows(); ++row) {
      float tmp = M (row, 0);
      M (row, 0) = M (row, 1);
      M (row, 1) = tmp;
      M (row, 2) = -M (row, 2);
    }
  }

  return axes;
}

  template <typename OutputValueType, class InputVoxelType>
inline void copy_permute (InputVoxelType& in, Image::Header& header_out, const std::string& output_filename)
{
  std::vector<int> axes = set_header (header_out, in);
  Image::Buffer<OutputValueType> buffer_out (output_filename, header_out);
  typename Image::Buffer<OutputValueType>::voxel_type out (buffer_out);

  if (axes.size()) {
    Image::Adapter::PermuteAxes<InputVoxelType> perm (in, axes);
    Image::threaded_copy_with_progress (perm, out, 2);
  }
  else
    Image::threaded_copy_with_progress (in, out, 2);
}

template <typename ValueType, class FunctionType, class VoxelType>
class VoxelValueModified : public Image::Adapter::Voxel<VoxelType> {
  public:
    VoxelValueModified (const VoxelType& parent, FunctionType function) :
      Image::Adapter::Voxel<VoxelType> (parent),
      func (function) { }
    typedef ValueType value_type;
    value_type value () const { return func (this->parent_vox.value()); }
    FunctionType func;
};

template <typename InputValueType, typename OutputValueType, class FunctionType>
void copy_extract (const Image::Header& header_in, const std::string& output_filename, FunctionType function)
{
  Image::Buffer<InputValueType> buffer_in (header_in);
  typedef VoxelValueModified<OutputValueType, FunctionType, typename Image::Buffer<InputValueType>::voxel_type> InputVoxelType;
  InputVoxelType in (buffer_in, function);

  Image::Header header_out (header_in);
  DataType requested_datatype = DataType::from_command_line ();

  if (requested_datatype.undefined())
    header_out.datatype() = DataType::from<OutputValueType>();
  else {
    bool is_complex = requested_datatype.is_complex();
    bool should_be_complex = DataType::from<OutputValueType>().is_complex();
    if (is_complex && !should_be_complex)
      throw Exception ("datatype must be real");
    if (!is_complex && should_be_complex )
      throw Exception ("datatype must be complex");
    header_out.datatype() = requested_datatype;
  }

  try {
    header_out.DW_scheme() = DWI::get_DW_scheme<float> (header_in);
  }
  catch (...) {
    inform ("no valid diffusion encoding found - ignoring");
  }

  Options opt = get_options ("coord");
  if (opt.size()) {
    std::vector<std::vector<int> > pos (buffer_in.ndim());
    for (size_t n = 0; n < opt.size(); n++) {
      int axis = opt[n][0];
      if (pos[axis].size())
        throw Exception ("\"coord\" option specified twice for axis " + str (axis));
      pos[axis] = parse_ints (opt[n][1], buffer_in.dim(axis)-1);
    }

    for (size_t n = 0; n < buffer_in.ndim(); ++n) {
      if (pos[n].empty()) {
        pos[n].resize (buffer_in.dim (n));
        for (size_t i = 0; i < pos[n].size(); i++)
          pos[n][i] = i;
      }
    }

    Image::Adapter::Extract<InputVoxelType> extract (in, pos);
    copy_permute<OutputValueType> (extract, header_out, output_filename);
  }
  else
    copy_permute<OutputValueType> (in, header_out, output_filename);

}

typedef float RealValueType;
typedef cfloat ComplexValueType;

inline RealValueType func_no_op_real (RealValueType x) { return x; }
inline ComplexValueType func_no_op_complex (ComplexValueType x) { return x; }
inline RealValueType func_real (ComplexValueType x) { return x.real(); }
inline RealValueType func_imag (ComplexValueType x) { return x.imag(); }
inline RealValueType func_phase (ComplexValueType x) { return std::arg (x); }
inline RealValueType func_abs (ComplexValueType x) { return std::abs (x); }

void run ()
{
  Image::Header header_in (argument[0]);

  if (header_in.datatype().is_complex()) {
    if (get_options ("real").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_real);
    else if (get_options ("imag").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_imag);
    else if (get_options ("phase").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_phase);
    else if (get_options ("magnitude").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_abs);
    else
      copy_extract<ComplexValueType,ComplexValueType> (header_in, argument[1], func_no_op_complex);
  }
  else
    copy_extract<RealValueType,RealValueType> (header_in, argument[1], func_no_op_real);
}

<commit_msg>fix minor bug in mrconvert that caused datatype changes to be ignored<commit_after>/*
    Copyright 2008 Brain Research Institute, Melbourne, Australia

    Written by J-Donald Tournier, 27/06/08.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "app.h"
#include "progressbar.h"
#include "image/buffer.h"
#include "image/voxel.h"
#include "image/axis.h"
#include "image/threaded_copy.h"
#include "image/adapter/extract.h"
#include "image/adapter/permute_axes.h"
#include "dwi/gradient.h"

MRTRIX_APPLICATION

using namespace MR;
using namespace App;

void usage ()
{
  DESCRIPTION
  + "perform conversion between different file types and optionally "
  "extract a subset of the input image."

  + "If used correctly, this program can be a very useful workhorse. "
  "In addition to converting images between different formats, it can "
  "be used to extract specific studies from a data set, extract a "
  "specific region of interest, or flip the images.";

  ARGUMENTS
  + Argument ("input", "the input image.").type_image_in ()
  + Argument ("ouput", "the output image.").type_image_out ();

  OPTIONS
  + Option ("coord",
            "extract data from the input image only at the coordinates specified.")
  .allow_multiple()
  + Argument ("axis").type_integer (0, 0, std::numeric_limits<int>::max())
  + Argument ("coord").type_sequence_int()

  + Option ("vox",
            "change the voxel dimensions of the output image. The new sizes should "
            "be provided as a comma-separated list of values. Only those values "
            "specified will be changed. For example: 1,,3.5 will change the voxel "
            "size along the x & z axes, and leave the y-axis voxel size unchanged.")
  + Argument ("sizes").type_sequence_float()

  + Option ("stride",
            "specify the strides of the output data in memory, as a comma-separated list. "
            "The actual strides produced will depend on whether the output image "
            "format can support it.")
  + Argument ("spec").type_sequence_int()

  + Option ("axes",
            "specify the axes from the input image that will be used to form the output "
            "image. This allows the permutation, ommission, or addition of axes into the "
            "output image. The axes should be supplied as a comma-separated list of axes. "
            "Any ommitted axes must have dimension 1. Axes can be inserted by supplying "
            "-1 at the corresponding position in the list.")
  + Argument ("axes").type_sequence_int()

  + Option ("prs",
            "assume that the DW gradients are specified in the PRS frame (Siemens DICOM only).")

  + DataType::options()

  + DWI::GradOption

  + OptionGroup ("for complex input images")
  + Option ("real",
      "return the real part of each voxel value")
  + Option ("imag",
      "return the imaginary part of each voxel value")
  + Option ("phase",
      "return the phase of each voxel value")
  + Option ("magnitude",
      "return the magnitude of each voxel value");
}

template <class InfoType>
inline std::vector<int> set_header (
  Image::Header& header,
  const InfoType& input)
{
  header.info() = input.info();

  header.intensity_offset() = 0.0;
  header.intensity_scale() = 1.0;

  Options opt = get_options ("axes");
  std::vector<int> axes;
  if (opt.size()) {
    axes = opt[0][0];
    header.set_ndim (axes.size());
    for (size_t i = 0; i < axes.size(); ++i) {
      if (axes[i] >= static_cast<int> (input.ndim()))
        throw Exception ("axis supplied to option -axes is out of bounds");
      header.dim(i) = axes[i] < 0 ? 1 : input.dim (axes[i]);
    }
  }

  opt = get_options ("vox");
  if (opt.size()) {
    std::vector<float> vox = opt[0][0];
    if (vox.size() > header.ndim())
      throw Exception ("too many axes supplied to -vox option");
    for (size_t n = 0; n < vox.size(); ++n) {
      if (std::isfinite (vox[n]))
        header.vox(n) = vox[n];
    }
  }

  opt = get_options ("stride");
  if (opt.size()) {
    std::vector<int> strides = opt[0][0];
    if (strides.size() > header.ndim())
      throw Exception ("too many axes supplied to -stride option");
    for (size_t n = 0; n < strides.size(); ++n)
      header.stride(n) = strides[n];
  }

  opt = get_options ("prs");
  if (opt.size() &&
      header.DW_scheme().rows() &&
      header.DW_scheme().columns()) {
    Math::Matrix<float>& M (header.DW_scheme());
    for (size_t row = 0; row < M.rows(); ++row) {
      float tmp = M (row, 0);
      M (row, 0) = M (row, 1);
      M (row, 1) = tmp;
      M (row, 2) = -M (row, 2);
    }
  }

  return axes;
}

  template <typename OutputValueType, class InputVoxelType>
inline void copy_permute (InputVoxelType& in, Image::Header& header_out, const std::string& output_filename)
{
  DataType datatype = header_out.datatype();
  std::vector<int> axes = set_header (header_out, in);
  header_out.datatype() = datatype;
  Image::Buffer<OutputValueType> buffer_out (output_filename, header_out);
  typename Image::Buffer<OutputValueType>::voxel_type out (buffer_out);

  if (axes.size()) {
    Image::Adapter::PermuteAxes<InputVoxelType> perm (in, axes);
    Image::threaded_copy_with_progress (perm, out, 2);
  }
  else
    Image::threaded_copy_with_progress (in, out, 2);
}

template <typename ValueType, class FunctionType, class VoxelType>
class VoxelValueModified : public Image::Adapter::Voxel<VoxelType> {
  public:
    VoxelValueModified (const VoxelType& parent, FunctionType function) :
      Image::Adapter::Voxel<VoxelType> (parent),
      func (function) { }
    typedef ValueType value_type;
    value_type value () const { return func (this->parent_vox.value()); }
    FunctionType func;
};

template <typename InputValueType, typename OutputValueType, class FunctionType>
void copy_extract (const Image::Header& header_in, const std::string& output_filename, FunctionType function)
{
  Image::Buffer<InputValueType> buffer_in (header_in);
  typedef VoxelValueModified<OutputValueType, FunctionType, typename Image::Buffer<InputValueType>::voxel_type> InputVoxelType;
  InputVoxelType in (buffer_in, function);

  Image::Header header_out (header_in);
  DataType requested_datatype = DataType::from_command_line ();

  if (requested_datatype.undefined())
    header_out.datatype() = DataType::from<OutputValueType>();
  else {
    bool is_complex = requested_datatype.is_complex();
    bool should_be_complex = DataType::from<OutputValueType>().is_complex();
    if (is_complex && !should_be_complex)
      throw Exception ("datatype must be real");
    if (!is_complex && should_be_complex )
      throw Exception ("datatype must be complex");
    header_out.datatype() = requested_datatype;
  }

  try {
    header_out.DW_scheme() = DWI::get_DW_scheme<float> (header_in);
  }
  catch (...) {
    inform ("no valid diffusion encoding found - ignoring");
  }

  Options opt = get_options ("coord");
  if (opt.size()) {
    std::vector<std::vector<int> > pos (buffer_in.ndim());
    for (size_t n = 0; n < opt.size(); n++) {
      int axis = opt[n][0];
      if (pos[axis].size())
        throw Exception ("\"coord\" option specified twice for axis " + str (axis));
      pos[axis] = parse_ints (opt[n][1], buffer_in.dim(axis)-1);
    }

    for (size_t n = 0; n < buffer_in.ndim(); ++n) {
      if (pos[n].empty()) {
        pos[n].resize (buffer_in.dim (n));
        for (size_t i = 0; i < pos[n].size(); i++)
          pos[n][i] = i;
      }
    }

    Image::Adapter::Extract<InputVoxelType> extract (in, pos);
    copy_permute<OutputValueType> (extract, header_out, output_filename);
  }
  else
    copy_permute<OutputValueType> (in, header_out, output_filename);

}

typedef float RealValueType;
typedef cfloat ComplexValueType;

inline RealValueType func_no_op_real (RealValueType x) { return x; }
inline ComplexValueType func_no_op_complex (ComplexValueType x) { return x; }
inline RealValueType func_real (ComplexValueType x) { return x.real(); }
inline RealValueType func_imag (ComplexValueType x) { return x.imag(); }
inline RealValueType func_phase (ComplexValueType x) { return std::arg (x); }
inline RealValueType func_abs (ComplexValueType x) { return std::abs (x); }

void run ()
{
  Image::Header header_in (argument[0]);

  if (header_in.datatype().is_complex()) {
    if (get_options ("real").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_real);
    else if (get_options ("imag").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_imag);
    else if (get_options ("phase").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_phase);
    else if (get_options ("magnitude").size())
      copy_extract<ComplexValueType,RealValueType> (header_in, argument[1], func_abs);
    else
      copy_extract<ComplexValueType,ComplexValueType> (header_in, argument[1], func_no_op_complex);
  }
  else
    copy_extract<RealValueType,RealValueType> (header_in, argument[1], func_no_op_real);
}

<|endoftext|>


<commit_before>#include "OpenGLTexture.h"

#include "TextureRef.h"

#define GL_GLEXT_PROTOTYPES

#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __WXMAC__
#include "OpenGL/gl.h"
#else
#include <GL/gl.h>
#endif

#ifdef _WIN32
#include "glext.h"
#else
#include <GL/glext.h>
#endif

#include <cassert>

using namespace std;
using namespace canvas;

size_t OpenGLTexture::total_textures = 0;
vector<unsigned int> OpenGLTexture::freed_textures;

static GLenum getOpenGLFilterType(FilterMode mode) {
  switch (mode) {
  case NEAREST: return GL_NEAREST;
  case LINEAR: return GL_LINEAR;
  case LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
  }
  return 0;
}

void
OpenGLTexture::updateData(const void * buffer) {
  assert(buffer);

  if (!texture_id) {
    loaded_width = loaded_height = 0;
    if (!freed_textures.empty()) {
      texture_id = freed_textures.back();
      freed_textures.pop_back();
    } else {
      glGenTextures(1, &texture_id);
    }
    if (texture_id) total_textures++;
  }
  assert(texture_id);

  // glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, texture_id);

  // glGenerateMipmap(GL_TEXTURE_2D);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  if (loaded_width != getWidth() || loaded_height != getHeight()) {
    loaded_width = getWidth();
    loaded_height = getHeight();

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getOpenGLFilterType(getMinFilter()) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getOpenGLFilterType(getMagFilter()) );
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, getMinFilter() == LINEAR_MIPMAP_LINEAR ? GL_TRUE : GL_FALSE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
  } else {
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, getWidth(), getHeight(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
  }

  glBindTexture(GL_TEXTURE_2D, 0);
}

void
OpenGLTexture::updateData(const void * buffer, unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
  assert(buffer);

  if (!texture_id) {
    loaded_width = loaded_height = 0;
    if (!freed_textures.empty()) {
      texture_id = freed_textures.back();
      freed_textures.pop_back();
    } else {
      glGenTextures(1, &texture_id);
    }
    if (texture_id) total_textures++;
  }
  assert(texture_id);

  glBindTexture(GL_TEXTURE_2D, texture_id);

  // glGenerateMipmap(GL_TEXTURE_2D);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  if (loaded_width != getWidth() || loaded_height != getHeight()) {
    loaded_width = getWidth();
    loaded_height = getHeight();

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getOpenGLFilterType(getMinFilter()) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getOpenGLFilterType(getMagFilter()) );
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, getMinFilter() == LINEAR_MIPMAP_LINEAR ? GL_TRUE : GL_FALSE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0);
  }

  glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);

  glBindTexture(GL_TEXTURE_2D, 0);
}

void
OpenGLTexture::releaseTextures() {
  // cerr << "DELETING TEXTURES: " << OpenGLTexture::getFreedTextures().size() << "/" << OpenGLTexture::getNumTextures() << endl;

  for (vector<unsigned int>::const_iterator it = freed_textures.begin(); it != freed_textures.end(); it++) {
    GLuint texid = *it;
    glDeleteTextures(1, &texid);
  }
  freed_textures.clear();
}

TextureRef
OpenGLTexture::createTexture(unsigned int width, unsigned int height, FilterMode min_filter, FilterMode mag_filter) {
  return TextureRef(width, height, new OpenGLTexture(width, height, min_filter, mag_filter));
}
<commit_msg>switch to modern mipmap generation<commit_after>#include "OpenGLTexture.h"

#include "TextureRef.h"

#ifdef WIN32
#include <GL/glew.h>
#endif

#define GL_GLEXT_PROTOTYPES

#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __WXMAC__
#include "OpenGL/gl.h"
#else
#include <GL/gl.h>
#endif

#ifdef _WIN32
#include "glext.h"
#else
#include <GL/glext.h>
#endif

#include <cassert>

using namespace std;
using namespace canvas;

size_t OpenGLTexture::total_textures = 0;
vector<unsigned int> OpenGLTexture::freed_textures;

static GLenum getOpenGLFilterType(FilterMode mode) {
  switch (mode) {
  case NEAREST: return GL_NEAREST;
  case LINEAR: return GL_LINEAR;
  case LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
  }
  return 0;
}

void
OpenGLTexture::updateData(const void * buffer) {
  assert(buffer);

  if (!texture_id) {
    loaded_width = loaded_height = 0;
    if (!freed_textures.empty()) {
      texture_id = freed_textures.back();
      freed_textures.pop_back();
    } else {
      glGenTextures(1, &texture_id);
    }
    if (texture_id) total_textures++;
  }
  assert(texture_id);

  glPushAttrib(GL_ENABLE_BIT);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, texture_id);

  // glGenerateMipmap(GL_TEXTURE_2D);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  if (loaded_width != getWidth() || loaded_height != getHeight()) {
    loaded_width = getWidth();
    loaded_height = getHeight();

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getOpenGLFilterType(getMinFilter()) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getOpenGLFilterType(getMagFilter()) );
    // glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, getMinFilter() == LINEAR_MIPMAP_LINEAR ? GL_TRUE : GL_FALSE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
  } else {
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, getWidth(), getHeight(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
  }

  glGenerateMipmap(GL_TEXTURE_2D);
  glPopAttrib();

  glBindTexture(GL_TEXTURE_2D, 0);
}

void
OpenGLTexture::updateData(const void * buffer, unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
  assert(buffer);

  if (!texture_id) {
    loaded_width = loaded_height = 0;
    if (!freed_textures.empty()) {
      texture_id = freed_textures.back();
      freed_textures.pop_back();
    } else {
      glGenTextures(1, &texture_id);
    }
    if (texture_id) total_textures++;
  }
  assert(texture_id);

  glBindTexture(GL_TEXTURE_2D, texture_id);

  // glGenerateMipmap(GL_TEXTURE_2D);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  if (loaded_width != getWidth() || loaded_height != getHeight()) {
    loaded_width = getWidth();
    loaded_height = getHeight();

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getOpenGLFilterType(getMinFilter()) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getOpenGLFilterType(getMagFilter()) );
    // glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, getMinFilter() == LINEAR_MIPMAP_LINEAR ? GL_TRUE : GL_FALSE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0);
  }

  glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
  glGenerateMipmap(GL_TEXTURE_2D);

  glBindTexture(GL_TEXTURE_2D, 0);
}

void
OpenGLTexture::releaseTextures() {
  // cerr << "DELETING TEXTURES: " << OpenGLTexture::getFreedTextures().size() << "/" << OpenGLTexture::getNumTextures() << endl;

  for (vector<unsigned int>::const_iterator it = freed_textures.begin(); it != freed_textures.end(); it++) {
    GLuint texid = *it;
    glDeleteTextures(1, &texid);
  }
  freed_textures.clear();
}

TextureRef
OpenGLTexture::createTexture(unsigned int width, unsigned int height, FilterMode min_filter, FilterMode mag_filter) {
  return TextureRef(width, height, new OpenGLTexture(width, height, min_filter, mag_filter));
}
<|endoftext|>


<commit_before>#include <signal.h>
#include <unistd.h>

#include <common/buffer.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

static void signal_reload(int);
static void signal_stop(int);

EventSystem::EventSystem(void)
: log_("/event/system"),
  queue_(),
  reload_(),
  stop_(),
  interest_queue_(),
  timeout_queue_(),
  poll_()
{
	INFO(log_) << "Starting event system.";

	signal(SIGHUP, signal_reload);
	signal(SIGINT, signal_stop);
}

EventSystem::~EventSystem()
{
}

Action *
EventSystem::poll(const EventPoll::Type& type, int fd, EventCallback *cb)
{
	Action *a = poll_.poll(type, fd, cb);
	return (a);
}

Action *
EventSystem::register_interest(const EventInterest& interest, Callback *cb)
{
	Action *a = interest_queue_[interest].append(cb);
	return (a);
}

Action *
EventSystem::schedule(Callback *cb)
{
	Action *a = queue_.append(cb);
	return (a);
}

Action *
EventSystem::timeout(unsigned secs, Callback *cb)
{
	Action *a = timeout_queue_.append(secs, cb);
	return (a);
}

void
EventSystem::start(void)
{
	for (;;) {
		/*
		 * If we have been told to stop, fire all shutdown events.
		 */
		if (stop_ && !interest_queue_[EventInterestStop].empty()) {
			INFO(log_) << "Running stop handlers.";
			if (interest_queue_[EventInterestStop].drain())
				ERROR(log_) << "Stop handlers added other stop handlers.";
			INFO(log_) << "Stop handlers have been run.";
		}

		/*
		 * If we have been told to reload, fire all shutdown events.
		 */
		if (reload_ && !interest_queue_[EventInterestReload].empty()) {
			INFO(log_) << "Running reload handlers.";
			interest_queue_[EventInterestReload].drain();
			INFO(log_) << "Reload handlers have been run.";

			reload_ = false;
			signal(SIGHUP, signal_reload);
		}

		/*
		 * If there are time-triggered events whose time has come,
		 * run them.
		 */
		if (!timeout_queue_.empty()) {
			while (timeout_queue_.ready())
				timeout_queue_.perform();
		}

		/*
		 * And then run a pending callback.
		 */
		while (!queue_.empty() && !timeout_queue_.ready()) {
			queue_.perform();
			if (stop_ || reload_)
				break;
			if (!poll_.idle())
				poll_.poll();
		}

		/*
		 * Do a quick poll if necessary.
		 */
		if (!queue_.empty() || timeout_queue_.ready())
			continue;

		/*
		 * But if there are no pending callbacks, and no timers or
		 * file descriptors being polled, stop.
		 */
		if (timeout_queue_.empty() && poll_.idle())
			break;
		/*
		 * If there are timers ticking down, then let the poll module
		 * block until a timer will be ready.
		 * XXX Maybe block 1 second less, just in case?  We'll never
		 * be a realtime system though, so any attempt at pretending
		 * to be one, beyond giving time events priority at the top of
		 * this loop, is probably a mistake.
		 */
		if (!timeout_queue_.empty()) {
			poll_.wait(timeout_queue_.interval());
			continue;
		}
		/*
		 * If, however, there's nothing sensitive to time, but someone
		 * has a file descriptor that can be blocked on, just block on it
		 * indefinitely.
		 */
		poll_.wait();
	}
}

void
EventSystem::stop(void)
{
	signal(SIGINT, SIG_DFL);
	INFO(log_) << "Stopping event system.";
	stop_ = true;
}

void
EventSystem::reload(void)
{
	signal(SIGHUP, SIG_IGN);
	INFO(log_) << "Running reload events.";
	reload_ = true;
}

static void
signal_reload(int)
{
	EventSystem::instance()->reload();
}

static void
signal_stop(int)
{
	EventSystem::instance()->stop();
}
<commit_msg>Update comments.<commit_after>#include <signal.h>
#include <unistd.h>

#include <common/buffer.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

static void signal_reload(int);
static void signal_stop(int);

EventSystem::EventSystem(void)
: log_("/event/system"),
  queue_(),
  reload_(),
  stop_(),
  interest_queue_(),
  timeout_queue_(),
  poll_()
{
	INFO(log_) << "Starting event system.";

	signal(SIGHUP, signal_reload);
	signal(SIGINT, signal_stop);
}

EventSystem::~EventSystem()
{
}

Action *
EventSystem::poll(const EventPoll::Type& type, int fd, EventCallback *cb)
{
	Action *a = poll_.poll(type, fd, cb);
	return (a);
}

Action *
EventSystem::register_interest(const EventInterest& interest, Callback *cb)
{
	Action *a = interest_queue_[interest].append(cb);
	return (a);
}

Action *
EventSystem::schedule(Callback *cb)
{
	Action *a = queue_.append(cb);
	return (a);
}

Action *
EventSystem::timeout(unsigned secs, Callback *cb)
{
	Action *a = timeout_queue_.append(secs, cb);
	return (a);
}

void
EventSystem::start(void)
{
	for (;;) {
		/*
		 * If we have been told to stop, fire all shutdown events.
		 */
		if (stop_ && !interest_queue_[EventInterestStop].empty()) {
			INFO(log_) << "Running stop handlers.";
			if (interest_queue_[EventInterestStop].drain())
				ERROR(log_) << "Stop handlers added other stop handlers.";
			INFO(log_) << "Stop handlers have been run.";
		}

		/*
		 * If we have been told to reload, fire all shutdown events.
		 */
		if (reload_ && !interest_queue_[EventInterestReload].empty()) {
			INFO(log_) << "Running reload handlers.";
			interest_queue_[EventInterestReload].drain();
			INFO(log_) << "Reload handlers have been run.";

			reload_ = false;
			signal(SIGHUP, signal_reload);
		}

		/*
		 * If there are time-triggered events whose time has come,
		 * run them.
		 */
		if (!timeout_queue_.empty()) {
			while (timeout_queue_.ready())
				timeout_queue_.perform();
		}

		/*
		 * And then run a pending callback.
		 */
		while (!queue_.empty() && !timeout_queue_.ready()) {
			queue_.perform();
			if (stop_ || reload_)
				break;
			/*
			 * Quickly poll in between callbacks in case there are
			 * a lot of callbacks or an infinite number of
			 * callbacks, so that we can still handle I/O.
			 */
			if (!poll_.idle())
				poll_.poll();
		}

		/*
		 * If there are any pending callbacks, go back to the start
		 * of the loop.
		 */
		if (!queue_.empty() || timeout_queue_.ready())
			continue;

		/*
		 * But if there are no pending callbacks, and no timers or
		 * file descriptors being polled, stop.
		 */
		if (timeout_queue_.empty() && poll_.idle())
			break;
		/*
		 * If there are timers ticking down, then let the poll module
		 * block until a timer will be ready.
		 * XXX Maybe block 1 second less, just in case?  We'll never
		 * be a realtime system though, so any attempt at pretending
		 * to be one, beyond giving time events priority at the top of
		 * this loop, is probably a mistake.
		 */
		if (!timeout_queue_.empty()) {
			poll_.wait(timeout_queue_.interval());
			continue;
		}
		/*
		 * If, however, there's nothing sensitive to time, but someone
		 * has a file descriptor that can be blocked on, just block on it
		 * indefinitely.
		 */
		poll_.wait();
	}
}

void
EventSystem::stop(void)
{
	signal(SIGINT, SIG_DFL);
	INFO(log_) << "Stopping event system.";
	stop_ = true;
}

void
EventSystem::reload(void)
{
	signal(SIGHUP, SIG_IGN);
	INFO(log_) << "Running reload events.";
	reload_ = true;
}

static void
signal_reload(int)
{
	EventSystem::instance()->reload();
}

static void
signal_stop(int)
{
	EventSystem::instance()->stop();
}
<|endoftext|>


<commit_before>/**
 * \file
 * \brief CallOnceOperationsTestCase class implementation
 *
 * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "CallOnceOperationsTestCase.hpp"

#include "SequenceAsserter.hpp"
#include "waitForNextTick.hpp"

#include "distortos/callOnce.hpp"
#include "distortos/DynamicThread.hpp"
#include "distortos/statistics.hpp"
#include "distortos/ThisThread.hpp"

namespace distortos
{

namespace test
{

#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// sleepFor() duration used in function() passed to callOnce()
constexpr auto sleepForDuration = TickClock::duration{10};

/// size of stack for test thread, bytes
constexpr size_t testThreadStackSize {512};

/// number of test threads
constexpr size_t totalThreads {10};

/// expected number of context switches in waitForNextTick(): main -> idle -> main
constexpr decltype(statistics::getContextSwitchCount()) waitForNextTickContextSwitchCount {2};

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// pair of sequence points
using SequencePoints = std::pair<unsigned int, unsigned int>;

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Test function passed to callOnce().
 *
 * This function marks first sequence point, executes ThisThread::sleepFor() and marks second sequence point.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 */

void function(SequenceAsserter& sequenceAsserter)
{
	sequenceAsserter.sequencePoint(1);
	ThisThread::sleepFor(sleepForDuration);
	sequenceAsserter.sequencePoint(totalThreads + 1);
}

/**
 * \brief Test thread function
 *
 * This function marks first sequence point, passes function() to callOnce() and marks second sequence point.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance
 * \param [in] onceFlag is a reference to OnceFlag shared object
 */

void thread(SequenceAsserter& sequenceAsserter, const SequencePoints sequencePoints, OnceFlag& onceFlag)
{
	sequenceAsserter.sequencePoint(sequencePoints.first);
	callOnce(onceFlag, function, sequenceAsserter);
	sequenceAsserter.sequencePoint(sequencePoints.second);
}

/**
 * \brief Builder of test threads
 *
 * \param [in] priority is the thread's priority
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance
 * \param [in] onceFlag is a reference to OnceFlag shared object
 *
 * \return constructed DynamicThread object
 */

DynamicThread makeTestThread(const uint8_t priority, SequenceAsserter& sequenceAsserter,
		const SequencePoints sequencePoints, OnceFlag& onceFlag)
{
	return makeDynamicThread({testThreadStackSize, priority}, thread, std::ref(sequenceAsserter), sequencePoints,
			std::ref(onceFlag));
}

}	// namespace

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool CallOnceOperationsTestCase::run_() const
{
#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	const auto contextSwitchCount = statistics::getContextSwitchCount();

	SequenceAsserter sequenceAsserter;
	OnceFlag onceFlag;

	std::array<DynamicThread, totalThreads> threads
	{{
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{0, 12}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{2, 13}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{3, 14}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{4, 15}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{5, 16}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{6, 17}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{7, 18}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{8, 19}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{9, 20}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{10, 21}, onceFlag),
	}};

	waitForNextTick();
	const auto start = TickClock::now();

	for (auto& thread : threads)
		thread.start();

	ThisThread::setPriority(testCasePriority_ - 2);

	bool invalidState {};
	for (size_t i = 1; i < threads.size(); ++i)
		if (threads[i].getState() != ThreadState::BlockedOnOnceFlag)
			invalidState = true;

	for (auto& thread : threads)
		thread.join();

	if (invalidState != false)
		return false;

	if (TickClock::now() - start != sleepForDuration + decltype(sleepForDuration){1})
		return false;

	if (sequenceAsserter.assertSequence(totalThreads * 2 + 2) == false)
		return false;

	constexpr auto totalContextSwitches = 2 * totalThreads + 3 + waitForNextTickContextSwitchCount;
	if (statistics::getContextSwitchCount() - contextSwitchCount != totalContextSwitches)
		return false;

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	return true;
}

}	// namespace test

}	// namespace distortos
<commit_msg>Increase size of stack in CallOnceOperationsTestCase<commit_after>/**
 * \file
 * \brief CallOnceOperationsTestCase class implementation
 *
 * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "CallOnceOperationsTestCase.hpp"

#include "SequenceAsserter.hpp"
#include "waitForNextTick.hpp"

#include "distortos/callOnce.hpp"
#include "distortos/DynamicThread.hpp"
#include "distortos/statistics.hpp"
#include "distortos/ThisThread.hpp"

namespace distortos
{

namespace test
{

#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// sleepFor() duration used in function() passed to callOnce()
constexpr auto sleepForDuration = TickClock::duration{10};

/// size of stack for test thread, bytes
constexpr size_t testThreadStackSize {768};

/// number of test threads
constexpr size_t totalThreads {10};

/// expected number of context switches in waitForNextTick(): main -> idle -> main
constexpr decltype(statistics::getContextSwitchCount()) waitForNextTickContextSwitchCount {2};

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// pair of sequence points
using SequencePoints = std::pair<unsigned int, unsigned int>;

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Test function passed to callOnce().
 *
 * This function marks first sequence point, executes ThisThread::sleepFor() and marks second sequence point.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 */

void function(SequenceAsserter& sequenceAsserter)
{
	sequenceAsserter.sequencePoint(1);
	ThisThread::sleepFor(sleepForDuration);
	sequenceAsserter.sequencePoint(totalThreads + 1);
}

/**
 * \brief Test thread function
 *
 * This function marks first sequence point, passes function() to callOnce() and marks second sequence point.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance
 * \param [in] onceFlag is a reference to OnceFlag shared object
 */

void thread(SequenceAsserter& sequenceAsserter, const SequencePoints sequencePoints, OnceFlag& onceFlag)
{
	sequenceAsserter.sequencePoint(sequencePoints.first);
	callOnce(onceFlag, function, sequenceAsserter);
	sequenceAsserter.sequencePoint(sequencePoints.second);
}

/**
 * \brief Builder of test threads
 *
 * \param [in] priority is the thread's priority
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance
 * \param [in] onceFlag is a reference to OnceFlag shared object
 *
 * \return constructed DynamicThread object
 */

DynamicThread makeTestThread(const uint8_t priority, SequenceAsserter& sequenceAsserter,
		const SequencePoints sequencePoints, OnceFlag& onceFlag)
{
	return makeDynamicThread({testThreadStackSize, priority}, thread, std::ref(sequenceAsserter), sequencePoints,
			std::ref(onceFlag));
}

}	// namespace

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool CallOnceOperationsTestCase::run_() const
{
#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	const auto contextSwitchCount = statistics::getContextSwitchCount();

	SequenceAsserter sequenceAsserter;
	OnceFlag onceFlag;

	std::array<DynamicThread, totalThreads> threads
	{{
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{0, 12}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{2, 13}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{3, 14}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{4, 15}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{5, 16}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{6, 17}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{7, 18}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{8, 19}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{9, 20}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{10, 21}, onceFlag),
	}};

	waitForNextTick();
	const auto start = TickClock::now();

	for (auto& thread : threads)
		thread.start();

	ThisThread::setPriority(testCasePriority_ - 2);

	bool invalidState {};
	for (size_t i = 1; i < threads.size(); ++i)
		if (threads[i].getState() != ThreadState::BlockedOnOnceFlag)
			invalidState = true;

	for (auto& thread : threads)
		thread.join();

	if (invalidState != false)
		return false;

	if (TickClock::now() - start != sleepForDuration + decltype(sleepForDuration){1})
		return false;

	if (sequenceAsserter.assertSequence(totalThreads * 2 + 2) == false)
		return false;

	constexpr auto totalContextSwitches = 2 * totalThreads + 3 + waitForNextTickContextSwitchCount;
	if (statistics::getContextSwitchCount() - contextSwitchCount != totalContextSwitches)
		return false;

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	return true;
}

}	// namespace test

}	// namespace distortos
<|endoftext|>