<commit_before>//
// @file types.hpp
// @brief Header for ydk entity
//
// YANG Development Kit
// Copyright 2016 Cisco Systems. All rights reserved
//
////////////////////////////////////////////////////////////////
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
//////////////////////////////////////////////////////////////////

#include "../src/types.hpp"
#include "../src/entity_util.hpp"
#include "catch.hpp"
#include <iostream>

using namespace ydk;
using namespace std;

class TestEntity:public Entity
{
  public:
    TestEntity()
        : name{YType::str, "name"}, enabled{YType::boolean, "enabled"}, bits_field{YType::bits, "bits-field"}, child(make_shared<TestEntity::Child>())
    {
        yang_name = "test"; yang_parent_name = "";
    }

    ~TestEntity()
    {
    }

  public:
    bool has_data() const
    {
        return name.is_set || enabled.is_set;
    }

    bool has_operation() const
    {
        return false;
    }

    std::string get_segment_path() const
    {
        return "test";
    }

    std::vector<std::pair<std::string, LeafData> > get_name_leaf_data() const
    {
        return {name.get_name_leafdata(), enabled.get_name_leafdata(), bits_field.get_name_leafdata()};
    }

    std::shared_ptr<Entity> get_child_by_name(const std::string & child_path, const string & u)
    {
        if(child_path == child->get_segment_path())
        {
            if(child != nullptr)
            {
                return child;
            }
            else
            {
                child = make_shared<TestEntity::Child>();
                child->parent = this;
                return child;
            }
        }
        return nullptr;
    }

    bool has_leaf_or_child_of_name(const std::string & name) const override
    {
        return false;
    }

    void set_filter(const std::string & name1, YFilter yfilter)
    {
    }

    void set_value(const std::string & name1, const std::string & value, const std::string & value1, const std::string & value2)
    {
        if(name1 == "name")
        {
            name = value;
        }
        else if(name1 == "enabled")
        {
            enabled = value;
        }
        else if(name1 == "bits-field")
        {
            bits_field[value] = true;
        }
    }

    std::map<std::string, std::shared_ptr<Entity>> get_children() const
    {
        std::map<std::string, std::shared_ptr<Entity>> children;
        return children;
    }

    class Child:public Entity
    {
      public:
        Child ()
          : child_val{YType::int32, "child-val"}
          {
            yang_name = "child"; yang_parent_name = "test";
          }

        bool has_data() const
        {
            return child_val.is_set;
        }

        bool has_leaf_or_child_of_name(const std::string & name) const override
        {
            return false;
        }

        bool has_operation() const
        {
            return false;
        }

        std::string get_segment_path() const
        {
            return "child";
        }

        std::vector<std::pair<std::string, LeafData> > get_name_leaf_data() const
        {
            return {child_val.get_name_leafdata()};
        }

        std::shared_ptr<Entity> get_child_by_name(const std::string & child_path, const string & u)
        {
            if(child_path == "multi-child")
            {
                for(auto const& ch : multi_child)
                {
                    string segment = ch->get_segment_path();
                    if(u == segment)
                    {
                        return ch;
                    }
                }
                auto ch = make_shared<TestEntity::Child::MultiChild>();
                ch->parent = this;
                multi_child.push_back(ch);
                return multi_child.back();
            }
            return nullptr;
        }

        std::map<std::string, std::shared_ptr<Entity>> get_children() const
        {
            std::map<std::string, std::shared_ptr<Entity>> children;
            return children;
        }

        void set_filter(const std::string & name1, YFilter yfilter)
        {
        }

        void set_value(const std::string & leaf_name, const std::string & value, const std::string & value1, const std::string & value2)
        {
            if(leaf_name == "child-val")
            {
                child_val = value;
            }
        }

        YLeaf child_val;

        class MultiChild : public Entity
        {
          public:
            MultiChild ()
              : child_key{YType::str, "child-key"}
              {
                yang_name = "multi-child"; yang_parent_name = "child";
              }

            bool has_data() const
            {
                return child_key.is_set;
            }

            bool has_operation() const
            {
                return false;
            }

            bool has_leaf_or_child_of_name(const std::string & name) const override
            {
                return false;
            }

            std::string get_segment_path() const
            {
                return "multi-child[child-key='"+child_key.get()+"']";
            }

            std::vector<std::pair<std::string, LeafData> > get_name_leaf_data() const
            {
                return {child_key.get_name_leafdata()};
            }

            std::shared_ptr<Entity> get_child_by_name(const std::string & child_path, const string & u)
            {
                return nullptr;
            }

            std::map<std::string, std::shared_ptr<Entity>> get_children() const
            {
                std::map<std::string, std::shared_ptr<Entity>> children;
                return children;
            }

            void set_filter(const std::string & name1, YFilter yfilter)
            {
            }

            void set_value(const std::string& leaf_name, const std::string & value, const std::string & value2, const std::string & value3)
            {
                if(leaf_name == "child-key")
                {
                    child_key = value;
                }
            }

            YLeaf child_key;
        };

        vector<shared_ptr<TestEntity::Child::MultiChild> > multi_child;
    };

  YLeaf name;
  YLeaf enabled;
  YLeaf bits_field;
  shared_ptr<TestEntity::Child> child;
};

TEST_CASE("test_create")
{
    TestEntity test{};
    string test_value = "value for test";
    std::vector<std::pair<std::string, LeafData> > d = {{"name", {test_value, YFilter::not_set, true, "", ""}},
                             {"enabled", {"true", YFilter::not_set, true, "", ""}},
                             {"bits-field", {"bit1 bit2", YFilter::not_set, true, "", ""}}};
    EntityPath expected {"test",
                            d};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.name = test_value;
    test.enabled = true;

    test.bits_field["bit1"] = true;
    test.bits_field["bit2"] = true;

    REQUIRE(test.has_data() == true);
    REQUIRE(get_entity_path(test, nullptr) == expected);
}

TEST_CASE("test_unequal")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;

    TestEntity test2{};

    test2.name = "different";
    test2.enabled = false;
    test2.bits_field["bit1"] = true;
    test2.bits_field["bit2"] = false;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);
    REQUIRE(test1 != test2);
}

TEST_CASE("test_unequal_child")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;
    test1.child->child_val = 23;

    TestEntity test2{};

    test2.name = "value for test";
    test2.enabled = true;
    test2.bits_field["bit1"] = true;
    test2.bits_field["bit2"] = false;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);

    cout<<test1<<endl;
    cout<<test2<<endl;
    REQUIRE(test1 != test2);
}

TEST_CASE("test_equal")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;

    TestEntity test2{};

    test2.name = "value for test";
    test2.enabled = true;
    test2.bits_field["bit1"] = false;
    test2.bits_field["bit2"] = true;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);
    REQUIRE(test1 == test2);
}

TEST_CASE("test_equal_child")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;
    test1.child->child_val = 23;

    TestEntity test2{};

    test2.name = "value for test";
    test2.enabled = true;
    test2.bits_field["bit1"] = false;
    test2.bits_field["bit2"] = true;
    test2.child->child_val = 23;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);

    cout<<test1<<endl;
    cout<<test2<<endl;
    REQUIRE(test1 == test2);
}
TEST_CASE("test_read")
{
    TestEntity test{};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.set_value("name", "test test", "", "");
    REQUIRE(test.has_data() == true);

    auto child = test.get_child_by_name("child", "");
    REQUIRE(child != nullptr);

    child->set_value("child-val", "123", "", "");
    REQUIRE(child->has_data() == true);
}

TEST_CASE("test_multi_create")
{
    TestEntity test{};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.set_value("name", "test test", "", "");
    REQUIRE(test.has_data() == true);

    auto mchild = test.child->get_child_by_name("multi-child", "");
    REQUIRE(mchild != nullptr);
}

TEST_CASE("test_multi_read")
{
    TestEntity test{};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.set_value("name", "test test", "", "");
    REQUIRE(test.has_data() == true);

    auto mchild = make_shared<TestEntity::Child::MultiChild>();
    mchild->parent = test.child.get();
    test.child->multi_child.push_back(mchild);

    auto m = test.child->get_child_by_name("multi-child", "multi-child[multi-key='abc']");
    REQUIRE(m != nullptr);
}

static string vector_to_string(vector<string> & string_vector)
{
    ostringstream buf;
    for (auto item : string_vector) {
        if (buf.str().length() > 0)
            buf << ", ";
        buf << "\"" << item << "\"";
    }
    return buf.str();
}

TEST_CASE("test_ylist")
{
    TestEntity* list_holder = new TestEntity();

    YList ylist_0 = YList(list_holder, {});
    YList ylist_1 = YList(list_holder, {"name"});
    YList ylist_2 = YList(list_holder, {"first", "second"});

    auto test1 = std::make_shared<TestEntity>();
    test1->name = "test1";
    test1->enabled = true;
    test1->bits_field["bit1"] = false;
    test1->bits_field["bit2"] = true;
    test1->child->child_val = 23;

    auto test2 = std::make_shared<TestEntity>();
    test2->name = "test2";
    test2->enabled = true;
    test2->bits_field["bit1"] = false;
    test2->bits_field["bit2"] = true;
    test2->child->child_val = 23;

    ylist_0.extend({test1, test2});
    REQUIRE(ylist_0.len() == 2);

    auto keys = ylist_0.keys();
    REQUIRE(vector_to_string(keys) == R"("1000000", "1000001")");

    auto ep = ylist_0.pop(1);
    REQUIRE(ep != nullptr);
    auto test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test2->name);
    REQUIRE(ylist_0.len() == 1);

    ep = ylist_0.pop(0);
    REQUIRE(ep != nullptr);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test1->name);
    REQUIRE(ylist_0.len() == 0);

    ylist_1.extend({test1, test2});
    REQUIRE(ylist_1.len() == 2);

    keys = ylist_1.keys();
    REQUIRE(vector_to_string(keys) == R"("test1", "test2")");

    ep = ylist_1[0];
    REQUIRE(ep != nullptr);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test1->name);

    ep = ylist_1["test2"];
    REQUIRE(ep != nullptr);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test2->name);

    ep = ylist_1.pop("test1");
    REQUIRE(ep != nullptr);
    REQUIRE(ylist_1.len() == 1);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test1->name);

    ep = ylist_1.pop("test1");
    REQUIRE(ep == nullptr);
}

//TODO Test for issue #800 to be resolved
//TEST_CASE("test_ylist_race")
//{
//	TestEntity* list_holder = new TestEntity();
//
//    YList ylist = YList(list_holder, {"name"});
//
//    // Append test1 to the YList before key value is defined
//    auto test1 = std::make_shared<TestEntity>();
//    ylist.append(test1);
//
//    ylist[0]->name = "test1";
//    ylist[0]->enabled = true;
//
//    auto keys = ylist.keys();
//    REQUIRE(vector_to_string(keys) == R"("test1")");
//
//    auto ep = ylist["test1"];
//    REQUIRE(ep != nullptr);
//}
<commit_msg>Resolved issue #854 for C++ and Python<commit_after>//
// @file types.hpp
// @brief Header for ydk entity
//
// YANG Development Kit
// Copyright 2016 Cisco Systems. All rights reserved
//
////////////////////////////////////////////////////////////////
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
//////////////////////////////////////////////////////////////////

#include "../src/types.hpp"
#include "../src/entity_util.hpp"
#include "catch.hpp"
#include <iostream>

using namespace ydk;
using namespace std;

class TestEntity:public Entity
{
  public:
    TestEntity()
        : name{YType::str, "name"}, enabled{YType::boolean, "enabled"}, bits_field{YType::bits, "bits-field"}, child(make_shared<TestEntity::Child>())
    {
        yang_name = "test"; yang_parent_name = "";
    }

    ~TestEntity()
    {
    }

  public:
    bool has_data() const
    {
        return name.is_set || enabled.is_set;
    }

    bool has_operation() const
    {
        return false;
    }

    std::string get_segment_path() const
    {
        return "test";
    }

    std::vector<std::pair<std::string, LeafData> > get_name_leaf_data() const
    {
        return {name.get_name_leafdata(), enabled.get_name_leafdata(), bits_field.get_name_leafdata()};
    }

    std::shared_ptr<Entity> get_child_by_name(const std::string & child_path, const string & u)
    {
        if(child_path == child->get_segment_path())
        {
            if(child != nullptr)
            {
                return child;
            }
            else
            {
                child = make_shared<TestEntity::Child>();
                child->parent = this;
                return child;
            }
        }
        return nullptr;
    }

    bool has_leaf_or_child_of_name(const std::string & name) const override
    {
        return false;
    }

    void set_filter(const std::string & name1, YFilter yfilter)
    {
    }

    void set_value(const std::string & name1, const std::string & value, const std::string & value1, const std::string & value2)
    {
        if(name1 == "name")
        {
            name = value;
        }
        else if(name1 == "enabled")
        {
            enabled = value;
        }
        else if(name1 == "bits-field")
        {
            bits_field[value] = true;
        }
    }

    std::map<std::string, std::shared_ptr<Entity>> get_children() const
    {
        std::map<std::string, std::shared_ptr<Entity>> children;
        return children;
    }

    class Child:public Entity
    {
      public:
        Child ()
          : child_val{YType::int32, "child-val"}
          {
            yang_name = "child"; yang_parent_name = "test";
          }

        bool has_data() const
        {
            return child_val.is_set;
        }

        bool has_leaf_or_child_of_name(const std::string & name) const override
        {
            return false;
        }

        bool has_operation() const
        {
            return false;
        }

        std::string get_segment_path() const
        {
            return "child";
        }

        std::vector<std::pair<std::string, LeafData> > get_name_leaf_data() const
        {
            return {child_val.get_name_leafdata()};
        }

        std::shared_ptr<Entity> get_child_by_name(const std::string & child_path, const string & u)
        {
            if(child_path == "multi-child")
            {
                for(auto const& ch : multi_child)
                {
                    string segment = ch->get_segment_path();
                    if(u == segment)
                    {
                        return ch;
                    }
                }
                auto ch = make_shared<TestEntity::Child::MultiChild>();
                ch->parent = this;
                multi_child.push_back(ch);
                return multi_child.back();
            }
            return nullptr;
        }

        std::map<std::string, std::shared_ptr<Entity>> get_children() const
        {
            std::map<std::string, std::shared_ptr<Entity>> children;
            return children;
        }

        void set_filter(const std::string & name1, YFilter yfilter)
        {
        }

        void set_value(const std::string & leaf_name, const std::string & value, const std::string & value1, const std::string & value2)
        {
            if(leaf_name == "child-val")
            {
                child_val = value;
            }
        }

        YLeaf child_val;

        class MultiChild : public Entity
        {
          public:
            MultiChild ()
              : child_key{YType::str, "child-key"}
              {
                yang_name = "multi-child"; yang_parent_name = "child";
              }

            bool has_data() const
            {
                return child_key.is_set;
            }

            bool has_operation() const
            {
                return false;
            }

            bool has_leaf_or_child_of_name(const std::string & name) const override
            {
                return false;
            }

            std::string get_segment_path() const
            {
                return "multi-child[child-key='"+child_key.get()+"']";
            }

            std::vector<std::pair<std::string, LeafData> > get_name_leaf_data() const
            {
                return {child_key.get_name_leafdata()};
            }

            std::shared_ptr<Entity> get_child_by_name(const std::string & child_path, const string & u)
            {
                return nullptr;
            }

            std::map<std::string, std::shared_ptr<Entity>> get_children() const
            {
                std::map<std::string, std::shared_ptr<Entity>> children;
                return children;
            }

            void set_filter(const std::string & name1, YFilter yfilter)
            {
            }

            void set_value(const std::string& leaf_name, const std::string & value, const std::string & value2, const std::string & value3)
            {
                if(leaf_name == "child-key")
                {
                    child_key = value;
                }
            }

            YLeaf child_key;
        };

        vector<shared_ptr<TestEntity::Child::MultiChild> > multi_child;
    };

  YLeaf name;
  YLeaf enabled;
  YLeaf bits_field;
  shared_ptr<TestEntity::Child> child;
};

TEST_CASE("test_create")
{
    TestEntity test{};
    string test_value = "value for test";
    std::vector<std::pair<std::string, LeafData> > d = {{"name", {test_value, YFilter::not_set, true, "", ""}},
                             {"enabled", {"true", YFilter::not_set, true, "", ""}},
                             {"bits-field", {"bit1 bit2", YFilter::not_set, true, "", ""}}};
    EntityPath expected {"test",
                            d};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.name = test_value;
    test.enabled = true;

    test.bits_field["bit1"] = true;
    test.bits_field["bit2"] = true;

    REQUIRE(test.has_data() == true);
    REQUIRE(get_entity_path(test, nullptr) == expected);
}

TEST_CASE("test_unequal")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;

    TestEntity test2{};

    test2.name = "different";
    test2.enabled = false;
    test2.bits_field["bit1"] = true;
    test2.bits_field["bit2"] = false;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);
    REQUIRE(test1 != test2);
}

TEST_CASE("test_unequal_child")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;
    test1.child->child_val = 23;

    TestEntity test2{};

    test2.name = "value for test";
    test2.enabled = true;
    test2.bits_field["bit1"] = true;
    test2.bits_field["bit2"] = false;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);

    cout<<test1<<endl;
    cout<<test2<<endl;
    REQUIRE(test1 != test2);
}

TEST_CASE("test_equal")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;

    TestEntity test2{};

    test2.name = "value for test";
    test2.enabled = true;
    test2.bits_field["bit1"] = false;
    test2.bits_field["bit2"] = true;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);
    REQUIRE(test1 == test2);
}

TEST_CASE("test_equal_child")
{
    TestEntity test1{};

    test1.name = "value for test";
    test1.enabled = true;
    test1.bits_field["bit1"] = false;
    test1.bits_field["bit2"] = true;
    test1.child->child_val = 23;

    TestEntity test2{};

    test2.name = "value for test";
    test2.enabled = true;
    test2.bits_field["bit1"] = false;
    test2.bits_field["bit2"] = true;
    test2.child->child_val = 23;

    REQUIRE(test1.has_data() == true);
    REQUIRE(test2.has_data() == true);

    cout<<test1<<endl;
    cout<<test2<<endl;
    REQUIRE(test1 == test2);
}
TEST_CASE("test_read")
{
    TestEntity test{};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.set_value("name", "test test", "", "");
    REQUIRE(test.has_data() == true);

    auto child = test.get_child_by_name("child", "");
    REQUIRE(child != nullptr);

    child->set_value("child-val", "123", "", "");
    REQUIRE(child->has_data() == true);
}

TEST_CASE("test_multi_create")
{
    TestEntity test{};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.set_value("name", "test test", "", "");
    REQUIRE(test.has_data() == true);

    auto mchild = test.child->get_child_by_name("multi-child", "");
    REQUIRE(mchild != nullptr);
}

TEST_CASE("test_multi_read")
{
    TestEntity test{};

    REQUIRE(get_entity_path(test, nullptr).path == "test");
    REQUIRE(test.has_data() == false);

    test.set_value("name", "test test", "", "");
    REQUIRE(test.has_data() == true);

    auto mchild = make_shared<TestEntity::Child::MultiChild>();
    mchild->parent = test.child.get();
    test.child->multi_child.push_back(mchild);

    auto m = test.child->get_child_by_name("multi-child", "multi-child[multi-key='abc']");
    REQUIRE(m != nullptr);
}

static string vector_to_string(vector<string> & string_vector)
{
    ostringstream buf;
    for (auto item : string_vector) {
        if (buf.str().length() > 0)
            buf << ", ";
        buf << "\"" << item << "\"";
    }
    return buf.str();
}

TEST_CASE("test_ylist")
{
    TestEntity* list_holder = new TestEntity();

    YList ylist_0 = YList(list_holder, {});
    YList ylist_1 = YList(list_holder, {"name"});
    YList ylist_2 = YList(list_holder, {"first", "second"});

    auto test1 = std::make_shared<TestEntity>();
    test1->name = "test1";
    test1->enabled = true;
    test1->bits_field["bit1"] = false;
    test1->bits_field["bit2"] = true;
    test1->child->child_val = 23;

    auto test2 = std::make_shared<TestEntity>();
    test2->name = "test2";
    test2->enabled = true;
    test2->bits_field["bit1"] = false;
    test2->bits_field["bit2"] = true;
    test2->child->child_val = 23;

    ylist_0.extend({test1, test2});
    REQUIRE(ylist_0.len() == 2);

    auto keys = ylist_0.keys();
    REQUIRE(vector_to_string(keys) == R"("1000001", "1000002")");

    auto ep = ylist_0.pop(1);
    REQUIRE(ep != nullptr);
    auto test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test2->name);
    REQUIRE(ylist_0.len() == 1);

    ep = ylist_0.pop(0);
    REQUIRE(ep != nullptr);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test1->name);
    REQUIRE(ylist_0.len() == 0);

    ylist_1.extend({test1, test2});
    REQUIRE(ylist_1.len() == 2);

    keys = ylist_1.keys();
    REQUIRE(vector_to_string(keys) == R"("test1", "test2")");

    ep = ylist_1[0];
    REQUIRE(ep != nullptr);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test1->name);

    ep = ylist_1["test2"];
    REQUIRE(ep != nullptr);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test2->name);

    ep = ylist_1.pop("test1");
    REQUIRE(ep != nullptr);
    REQUIRE(ylist_1.len() == 1);
    test_entity = dynamic_cast<TestEntity*> (ep.get());
    REQUIRE(test_entity->name == test1->name);

    ep = ylist_1.pop("test1");
    REQUIRE(ep == nullptr);
}

//TODO Test for issue #800 to be resolved
//TEST_CASE("test_ylist_race")
//{
//	TestEntity* list_holder = new TestEntity();
//
//    YList ylist = YList(list_holder, {"name"});
//
//    // Append test1 to the YList before key value is defined
//    auto test1 = std::make_shared<TestEntity>();
//    ylist.append(test1);
//
//    ylist[0]->name = "test1";
//    ylist[0]->enabled = true;
//
//    auto keys = ylist.keys();
//    REQUIRE(vector_to_string(keys) == R"("test1")");
//
//    auto ep = ylist["test1"];
//    REQUIRE(ep != nullptr);
//}
<|endoftext|>


<commit_before>
#pragma once

#include <list>
#include <unordered_map>
#include <string>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <functional>
#include <unistd.h>

/**
 * @namespace testy
 * Simple tool to easly manage unit tests.
 */
namespace testy {

/**
 * @macro
 * The macro used to create the test suite.
 */
#define Suite(name, code) \
  int main() { \
    testy::TestSuite testSuite; \
    testSuite.displayTitle("Testy - test module "#name); \
    code \
    return testSuite.run(); \
  }

/**
 * @macro
 * Describe one entity of the suite.
 */
#define describe(name, ...) \
  testSuite.addUnit(name, { \
    __VA_ARGS__ \
  });

/**
 * @macro
 * Test one action of the suite.
 */
#define it(text, code) \
  {text, [] () { \
      code \
      return true; \
    }}

/**
 * @macro test
 * Test if the given expr is true.
 */
#define test(expr) if (!expr) return false;

/**
 * @macro testRange
 * Test if the given expr is in the given range.
 */
#define testRange(expr, begin, end) \
  { \
    auto const &val = expr; \
    if (val > end || val < begin) \
      return false; \
  }

/**
 * @macro testThrow
 * Test if the given `expr` throw the given `except`.
 */
#define testThrow(expr, except) try { expr } catch (except) {}

/**
 * TestSuite
 * Simple test suite in which you can register units and run all unit tests.
 */
class TestSuite {
 public:
  /**
   * @typedef Unit
   * Describe a unit to test.
   */
  typedef std::list<std::pair<std::string, std::function<bool()>>> Unit;
  TestSuite() {}
  ~TestSuite() {}
  inline void displayTitle(std::string const &title);
  inline void displaySubtitle(std::string const &subTitle);
  inline void addUnit(std::string const &desc, Unit const &unit);
  inline int run();
 private:
  std::list<std::pair<std::string, Unit>> _units;
};

inline void TestSuite::displayTitle(std::string const &title) {
    std::cout << std::endl << "  \e[1m" << title << std::endl;
    std::cout << "  ";
    for (size_t i = 0; i < title.size(); ++i)
      std::cout << "=";
    std::cout << "\e[m"<< std::endl;
}

inline void TestSuite::displaySubtitle(std::string const &subTitle) {
  std::cout << "  \e[m## " << subTitle << std::endl << std::endl;
}

/**
 * Register a new unit test.
 * @param desc The description of this unit.
 * @param unit The unit which contains all tests.
 */
inline void TestSuite::addUnit(std::string const &desc,
                               TestSuite::Unit const &unit) {
  _units.push_back(std::make_pair(desc, unit));
}

/**
 * Return all unit tests.
 * @return Returns the number of failures or 0 on success.
 */
inline int TestSuite::run() {
  std::chrono::steady_clock::time_point prev;
  int fail = 0;
  int total = 0;
  size_t duration = 0;

  std::cout << std::endl;
  for (auto &unit : _units) {

    // Display the test description
    displaySubtitle(unit.first);

    for (auto &test : unit.second) {

      // Run the test and display the fail or success message

      prev = std::chrono::steady_clock::now();
      std::cout << "    ";
      try {
        if (!test.second()) { // false if callback return false
          ++fail;
          std::cout << "\e[0;31m✗ ";
        } else { // succeed otherwise
          std::cout << "\e[0;32m✓ \e[1;30m";
        }
      } catch (...) { // fail if an unexpected exception is thrown.
        ++fail;
        std::cout << "\e[0;31m✗ ";
      }

      // and duration of the function call

      size_t delta = (std::chrono::steady_clock::now() - prev).count() / 1000;
      duration += delta;
      std::cout << test.first << " ("
                << std::fixed << delta
                << "ms)" << std::endl;
      ++total;
    }
    std::cout << std::endl;
  }

  // Display the result of the tests

  if (fail) {
    std::cout << "  \e[0;31m✗ " << fail << " of "
              << total << " tests failed "
              << "(" << duration << "ms)";
  } else {
    std::cout << "  \e[0;32m✓ " << total
              << " tests completed "
              << "(" << duration << "ms)";
  }
  std::cout << "\e[m" << std::endl << std::endl;
  return fail;
}

}
<commit_msg>Add cin.get() for windows console<commit_after>
#pragma once

#include <list>
#include <unordered_map>
#include <string>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <functional>
#include <unistd.h>

/**
 * @namespace testy
 * Simple tool to easly manage unit tests.
 */
namespace testy {

/**
 * @macro
 * The macro used to create the test suite.
 */
#define Suite(name, code) \
  int main() { \
    testy::TestSuite testSuite; \
    testSuite.displayTitle("Testy - test module "#name); \
    code \
    return testSuite.run(); \
  }

/**
 * @macro
 * Describe one entity of the suite.
 */
#define describe(name, ...) \
  testSuite.addUnit(name, { \
    __VA_ARGS__ \
  });

/**
 * @macro
 * Test one action of the suite.
 */
#define it(text, code) \
  {text, [] () { \
      code \
      return true; \
    }}

/**
 * @macro test
 * Test if the given expr is true.
 */
#define test(expr) if (!expr) return false;

/**
 * @macro testRange
 * Test if the given expr is in the given range.
 */
#define testRange(expr, begin, end) \
  { \
    auto const &val = expr; \
    if (val > end || val < begin) \
      return false; \
  }

/**
 * @macro testThrow
 * Test if the given `expr` throw the given `except`.
 */
#define testThrow(expr, except) try { expr } catch (except) {}

/**
 * TestSuite
 * Simple test suite in which you can register units and run all unit tests.
 */
class TestSuite {
 public:
  /**
   * @typedef Unit
   * Describe a unit to test.
   */
  typedef std::list<std::pair<std::string, std::function<bool()>>> Unit;
  TestSuite() {}
  ~TestSuite() {}
  inline void displayTitle(std::string const &title);
  inline void displaySubtitle(std::string const &subTitle);
  inline void addUnit(std::string const &desc, Unit const &unit);
  inline int run();
 private:
  std::list<std::pair<std::string, Unit>> _units;
};

inline void TestSuite::displayTitle(std::string const &title) {
    std::cout << std::endl << "  \e[1m" << title << std::endl;
    std::cout << "  ";
    for (size_t i = 0; i < title.size(); ++i)
      std::cout << "=";
    std::cout << "\e[m"<< std::endl;
}

inline void TestSuite::displaySubtitle(std::string const &subTitle) {
  std::cout << "  \e[m## " << subTitle << std::endl << std::endl;
}

/**
 * Register a new unit test.
 * @param desc The description of this unit.
 * @param unit The unit which contains all tests.
 */
inline void TestSuite::addUnit(std::string const &desc,
                               TestSuite::Unit const &unit) {
  _units.push_back(std::make_pair(desc, unit));
}

/**
 * Return all unit tests.
 * @return Returns the number of failures or 0 on success.
 */
inline int TestSuite::run() {
  std::chrono::steady_clock::time_point prev;
  int fail = 0;
  int total = 0;
  size_t duration = 0;

  std::cout << std::endl;
  for (auto &unit : _units) {

    // Display the test description
    displaySubtitle(unit.first);

    for (auto &test : unit.second) {

      // Run the test and display the fail or success message

      prev = std::chrono::steady_clock::now();
      std::cout << "    ";
      try {
        if (!test.second()) { // false if callback return false
          ++fail;
          std::cout << "\e[0;31m✗ ";
        } else { // succeed otherwise
          std::cout << "\e[0;32m✓ \e[1;30m";
        }
      } catch (...) { // fail if an unexpected exception is thrown.
        ++fail;
        std::cout << "\e[0;31m✗ ";
      }

      // and duration of the function call

      size_t delta = (std::chrono::steady_clock::now() - prev).count() / 1000;
      duration += delta;
      std::cout << test.first << " ("
                << std::fixed << delta
                << "ms)" << std::endl;
      ++total;
    }
    std::cout << std::endl;
  }

  // Display the result of the tests

  if (fail) {
    std::cout << "  \e[0;31m✗ " << fail << " of "
              << total << " tests failed "
              << "(" << duration << "ms)";
  } else {
    std::cout << "  \e[0;32m✓ " << total
              << " tests completed "
              << "(" << duration << "ms)";
  }
  std::cout << "\e[m" << std::endl << std::endl;

// Wait for keyboard press on windows to keep the output console opened.
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
  std::cout << "  [ Press any key to quit ]" << std::endl << std::endl;
  std::cin.get();
#endif

  return fail;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "Class.hxx"
#include "GetHandler.hxx"
#include "pool/pool.hxx"
#include "util/Cancellable.hxx"

#include <assert.h>

inline
Stock::Waiting::Waiting(Stock &_stock, struct pool &_pool, void *_info,
                        StockGetHandler &_handler,
                        CancellablePointer &_cancel_ptr) noexcept
    :stock(_stock), pool(_pool), info(_info),
     handler(_handler),
     cancel_ptr(_cancel_ptr)
{
    pool_ref(&pool);
    cancel_ptr = *this;
}

inline void
Stock::Waiting::Destroy() noexcept
{
    pool_unref(&pool);
    delete this;
}

void
Stock::FadeAll() noexcept
{
    for (auto &i : busy)
        i.fade = true;

    ClearIdle();
    ScheduleCheckEmpty();

    // TODO: restart the "num_create" list?
}

/*
 * The "empty()" handler method.
 *
 */

void
Stock::CheckEmpty() noexcept
{
    if (IsEmpty() && handler != nullptr)
        handler->OnStockEmpty(*this);
}

void
Stock::ScheduleCheckEmpty() noexcept
{
    if (IsEmpty() && handler != nullptr)
        empty_event.Schedule();
}

/*
 * cleanup
 *
 */

void
Stock::CleanupEventCallback() noexcept
{
    assert(idle.size() > max_idle);

    /* destroy one third of the idle items */

    for (unsigned i = (idle.size() - max_idle + 2) / 3; i > 0; --i)
        idle.pop_front_and_dispose(DeleteDisposer());

    /* schedule next cleanup */

    if (idle.size() > max_idle)
        ScheduleCleanup();
    else
        CheckEmpty();
}

/*
 * wait operation
 *
 */

void
Stock::Waiting::Cancel() noexcept
{
    auto &list = stock.waiting;
    const auto i = list.iterator_to(*this);
    list.erase_and_dispose(i, [](Stock::Waiting *w){ w->Destroy(); });
}

void
Stock::RetryWaiting() noexcept
{
    if (limit == 0)
        /* no limit configured, no waiters possible */
        return;

    /* first try to serve existing idle items */

    while (!idle.empty()) {
        const auto i = waiting.begin();
        if (i == waiting.end())
            return;

        auto &w = *i;

        waiting.erase(i);

        if (GetIdle(w.handler))
            w.Destroy();
        else
            /* didn't work (probably because borrowing the item has
               failed) - re-add to "waiting" list */
            waiting.push_front(w);
    }

    /* if we're below the limit, create a bunch of new items */

    for (unsigned i = limit - busy.size() - num_create;
         busy.size() + num_create < limit && i > 0 && !waiting.empty();
         --i) {
        auto &w = waiting.front();
        waiting.pop_front();

        GetCreate(w.pool, w.info,
                  w.handler,
                  w.cancel_ptr);
        w.Destroy();
    }
}

void
Stock::ScheduleRetryWaiting() noexcept
{
    if (limit > 0 && !waiting.empty() &&
        busy.size() - num_create < limit)
        retry_event.Schedule();
}

/*
 * clear after 60 seconds idle
 *
 */

void
Stock::ClearIdle() noexcept
{
    logger.Format(5, "ClearIdle num_idle=%zu num_busy=%zu",
                  idle.size(), busy.size());

    if (idle.size() > max_idle)
        UnscheduleCleanup();

    idle.clear_and_dispose(DeleteDisposer());
}

void
Stock::ClearEventCallback() noexcept
{
    logger.Format(6, "ClearEvent may_clear=%d", may_clear);

    if (may_clear)
        ClearIdle();

    may_clear = true;
    ScheduleClear();
    CheckEmpty();
}

/*
 * constructor
 *
 */

Stock::Stock(EventLoop &event_loop, StockClass &_cls,
             const char *_name, unsigned _limit, unsigned _max_idle,
             StockHandler *_handler) noexcept
    :cls(_cls),
     name(_name),
     limit(_limit), max_idle(_max_idle),
     handler(_handler),
     logger(name),
     retry_event(event_loop, BIND_THIS_METHOD(RetryWaiting)),
     empty_event(event_loop, BIND_THIS_METHOD(CheckEmpty)),
     cleanup_event(event_loop, BIND_THIS_METHOD(CleanupEventCallback)),
     clear_event(event_loop, BIND_THIS_METHOD(ClearEventCallback))
{
    assert(max_idle > 0);

    ScheduleClear();
}

Stock::~Stock() noexcept
{
    assert(num_create == 0);

    /* must not delete the Stock when there are busy items left */
    assert(busy.empty());

    retry_event.Cancel();
    empty_event.Cancel();
    cleanup_event.Cancel();
    clear_event.Cancel();

    ClearIdle();
}

bool
Stock::GetIdle(StockGetHandler &get_handler) noexcept
{
    unsigned retry_unclean = idle.size();

    auto i = idle.begin();
    const auto end = idle.end();
    while (i != end) {
        StockItem &item = *i;
        assert(item.is_idle);

        i = idle.erase(i);

        if (item.unclean && retry_unclean > 0) {
            /* postpone reusal of this item until it's "clean" */
            // TODO: replace this kludge
            --retry_unclean;
            idle.push_back(item);
            continue;
        }

        if (idle.size() == max_idle)
            UnscheduleCleanup();

        if (item.Borrow()) {
#ifndef NDEBUG
            item.is_idle = false;
#endif

            busy.push_front(item);

            get_handler.OnStockItemReady(item);
            return true;
        }

        delete &item;
    }

    ScheduleCheckEmpty();
    return false;
}

void
Stock::GetCreate(struct pool &caller_pool, void *info,
                 StockGetHandler &get_handler,
                 CancellablePointer &cancel_ptr) noexcept
{
    ++num_create;

    try {
        cls.Create({*this, get_handler},
                   info, caller_pool, cancel_ptr);
    } catch (...) {
        ItemCreateError(get_handler, std::current_exception());
    }
}

void
Stock::Get(struct pool &caller_pool, void *info,
           StockGetHandler &get_handler,
           CancellablePointer &cancel_ptr) noexcept
{
    may_clear = false;

    if (GetIdle(get_handler))
        return;

    if (limit > 0 && busy.size() + num_create >= limit) {
        /* item limit reached: wait for an item to return */
        auto w = new Waiting(*this, caller_pool, info,
                             get_handler, cancel_ptr);
        waiting.push_front(*w);
        return;
    }

    GetCreate(caller_pool, info, get_handler, cancel_ptr);
}

StockItem *
Stock::GetNow(struct pool &caller_pool, void *info)
{
    struct NowRequest final : public StockGetHandler {
#ifndef NDEBUG
        bool created = false;
#endif
        StockItem *item;
        std::exception_ptr error;

        /* virtual methods from class StockGetHandler */
        void OnStockItemReady(StockItem &_item) noexcept override {
#ifndef NDEBUG
            created = true;
#endif

            item = &_item;
        }

        void OnStockItemError(std::exception_ptr ep) noexcept override {
#ifndef NDEBUG
            created = true;
#endif

            error = ep;
        }
    };

    NowRequest data;
    CancellablePointer cancel_ptr;

    /* cannot call this on a limited stock */
    assert(limit == 0);

    Get(caller_pool, info, data, cancel_ptr);
    assert(data.created);

    if (data.error)
        std::rethrow_exception(data.error);

    return data.item;
}

void
Stock::ItemCreateSuccess(StockItem &item) noexcept
{
    assert(num_create > 0);
    --num_create;

    busy.push_front(item);

    item.handler.OnStockItemReady(item);
}

void
Stock::ItemCreateError(StockItem &item, std::exception_ptr ep) noexcept
{
    ItemCreateError(item.handler, ep);
    delete &item;
}

void
Stock::ItemCreateAborted(StockItem &item) noexcept
{
    ItemCreateAborted();
    delete &item;
}

void
Stock::ItemCreateError(StockGetHandler &get_handler,
                       std::exception_ptr ep) noexcept
{
    assert(num_create > 0);
    --num_create;

    get_handler.OnStockItemError(ep);

    ScheduleCheckEmpty();
    ScheduleRetryWaiting();
}

void
Stock::ItemCreateAborted() noexcept
{
    assert(num_create > 0);
    --num_create;

    ScheduleCheckEmpty();
    ScheduleRetryWaiting();
}

void
Stock::Put(StockItem &item, bool destroy) noexcept
{
    assert(!item.is_idle);
    assert(&item.stock == this);

    may_clear = false;

    assert(!busy.empty());

    busy.erase(busy.iterator_to(item));

    if (destroy || item.fade || !item.Release()) {
        delete &item;
        ScheduleCheckEmpty();
    } else {
#ifndef NDEBUG
        item.is_idle = true;
#endif

        if (idle.size() == max_idle)
            ScheduleCleanup();

        idle.push_front(item);
    }

    ScheduleRetryWaiting();
}

void
Stock::ItemIdleDisconnect(StockItem &item) noexcept
{
    assert(item.is_idle);

    assert(!idle.empty());

    idle.erase(idle.iterator_to(item));

    if (idle.size() == max_idle)
        UnscheduleCleanup();

    delete &item;
    ScheduleCheckEmpty();
}
<commit_msg>stock/Stock: remove caller_pool reference<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "Class.hxx"
#include "GetHandler.hxx"
#include "pool/pool.hxx"
#include "util/Cancellable.hxx"

#include <assert.h>

inline
Stock::Waiting::Waiting(Stock &_stock, struct pool &_pool, void *_info,
                        StockGetHandler &_handler,
                        CancellablePointer &_cancel_ptr) noexcept
    :stock(_stock), pool(_pool), info(_info),
     handler(_handler),
     cancel_ptr(_cancel_ptr)
{
    cancel_ptr = *this;
}

inline void
Stock::Waiting::Destroy() noexcept
{
    delete this;
}

void
Stock::FadeAll() noexcept
{
    for (auto &i : busy)
        i.fade = true;

    ClearIdle();
    ScheduleCheckEmpty();

    // TODO: restart the "num_create" list?
}

/*
 * The "empty()" handler method.
 *
 */

void
Stock::CheckEmpty() noexcept
{
    if (IsEmpty() && handler != nullptr)
        handler->OnStockEmpty(*this);
}

void
Stock::ScheduleCheckEmpty() noexcept
{
    if (IsEmpty() && handler != nullptr)
        empty_event.Schedule();
}

/*
 * cleanup
 *
 */

void
Stock::CleanupEventCallback() noexcept
{
    assert(idle.size() > max_idle);

    /* destroy one third of the idle items */

    for (unsigned i = (idle.size() - max_idle + 2) / 3; i > 0; --i)
        idle.pop_front_and_dispose(DeleteDisposer());

    /* schedule next cleanup */

    if (idle.size() > max_idle)
        ScheduleCleanup();
    else
        CheckEmpty();
}

/*
 * wait operation
 *
 */

void
Stock::Waiting::Cancel() noexcept
{
    auto &list = stock.waiting;
    const auto i = list.iterator_to(*this);
    list.erase_and_dispose(i, [](Stock::Waiting *w){ w->Destroy(); });
}

void
Stock::RetryWaiting() noexcept
{
    if (limit == 0)
        /* no limit configured, no waiters possible */
        return;

    /* first try to serve existing idle items */

    while (!idle.empty()) {
        const auto i = waiting.begin();
        if (i == waiting.end())
            return;

        auto &w = *i;

        waiting.erase(i);

        if (GetIdle(w.handler))
            w.Destroy();
        else
            /* didn't work (probably because borrowing the item has
               failed) - re-add to "waiting" list */
            waiting.push_front(w);
    }

    /* if we're below the limit, create a bunch of new items */

    for (unsigned i = limit - busy.size() - num_create;
         busy.size() + num_create < limit && i > 0 && !waiting.empty();
         --i) {
        auto &w = waiting.front();
        waiting.pop_front();

        GetCreate(w.pool, w.info,
                  w.handler,
                  w.cancel_ptr);
        w.Destroy();
    }
}

void
Stock::ScheduleRetryWaiting() noexcept
{
    if (limit > 0 && !waiting.empty() &&
        busy.size() - num_create < limit)
        retry_event.Schedule();
}

/*
 * clear after 60 seconds idle
 *
 */

void
Stock::ClearIdle() noexcept
{
    logger.Format(5, "ClearIdle num_idle=%zu num_busy=%zu",
                  idle.size(), busy.size());

    if (idle.size() > max_idle)
        UnscheduleCleanup();

    idle.clear_and_dispose(DeleteDisposer());
}

void
Stock::ClearEventCallback() noexcept
{
    logger.Format(6, "ClearEvent may_clear=%d", may_clear);

    if (may_clear)
        ClearIdle();

    may_clear = true;
    ScheduleClear();
    CheckEmpty();
}

/*
 * constructor
 *
 */

Stock::Stock(EventLoop &event_loop, StockClass &_cls,
             const char *_name, unsigned _limit, unsigned _max_idle,
             StockHandler *_handler) noexcept
    :cls(_cls),
     name(_name),
     limit(_limit), max_idle(_max_idle),
     handler(_handler),
     logger(name),
     retry_event(event_loop, BIND_THIS_METHOD(RetryWaiting)),
     empty_event(event_loop, BIND_THIS_METHOD(CheckEmpty)),
     cleanup_event(event_loop, BIND_THIS_METHOD(CleanupEventCallback)),
     clear_event(event_loop, BIND_THIS_METHOD(ClearEventCallback))
{
    assert(max_idle > 0);

    ScheduleClear();
}

Stock::~Stock() noexcept
{
    assert(num_create == 0);

    /* must not delete the Stock when there are busy items left */
    assert(busy.empty());

    retry_event.Cancel();
    empty_event.Cancel();
    cleanup_event.Cancel();
    clear_event.Cancel();

    ClearIdle();
}

bool
Stock::GetIdle(StockGetHandler &get_handler) noexcept
{
    unsigned retry_unclean = idle.size();

    auto i = idle.begin();
    const auto end = idle.end();
    while (i != end) {
        StockItem &item = *i;
        assert(item.is_idle);

        i = idle.erase(i);

        if (item.unclean && retry_unclean > 0) {
            /* postpone reusal of this item until it's "clean" */
            // TODO: replace this kludge
            --retry_unclean;
            idle.push_back(item);
            continue;
        }

        if (idle.size() == max_idle)
            UnscheduleCleanup();

        if (item.Borrow()) {
#ifndef NDEBUG
            item.is_idle = false;
#endif

            busy.push_front(item);

            get_handler.OnStockItemReady(item);
            return true;
        }

        delete &item;
    }

    ScheduleCheckEmpty();
    return false;
}

void
Stock::GetCreate(struct pool &caller_pool, void *info,
                 StockGetHandler &get_handler,
                 CancellablePointer &cancel_ptr) noexcept
{
    ++num_create;

    try {
        cls.Create({*this, get_handler},
                   info, caller_pool, cancel_ptr);
    } catch (...) {
        ItemCreateError(get_handler, std::current_exception());
    }
}

void
Stock::Get(struct pool &caller_pool, void *info,
           StockGetHandler &get_handler,
           CancellablePointer &cancel_ptr) noexcept
{
    may_clear = false;

    if (GetIdle(get_handler))
        return;

    if (limit > 0 && busy.size() + num_create >= limit) {
        /* item limit reached: wait for an item to return */
        auto w = new Waiting(*this, caller_pool, info,
                             get_handler, cancel_ptr);
        waiting.push_front(*w);
        return;
    }

    GetCreate(caller_pool, info, get_handler, cancel_ptr);
}

StockItem *
Stock::GetNow(struct pool &caller_pool, void *info)
{
    struct NowRequest final : public StockGetHandler {
#ifndef NDEBUG
        bool created = false;
#endif
        StockItem *item;
        std::exception_ptr error;

        /* virtual methods from class StockGetHandler */
        void OnStockItemReady(StockItem &_item) noexcept override {
#ifndef NDEBUG
            created = true;
#endif

            item = &_item;
        }

        void OnStockItemError(std::exception_ptr ep) noexcept override {
#ifndef NDEBUG
            created = true;
#endif

            error = ep;
        }
    };

    NowRequest data;
    CancellablePointer cancel_ptr;

    /* cannot call this on a limited stock */
    assert(limit == 0);

    Get(caller_pool, info, data, cancel_ptr);
    assert(data.created);

    if (data.error)
        std::rethrow_exception(data.error);

    return data.item;
}

void
Stock::ItemCreateSuccess(StockItem &item) noexcept
{
    assert(num_create > 0);
    --num_create;

    busy.push_front(item);

    item.handler.OnStockItemReady(item);
}

void
Stock::ItemCreateError(StockItem &item, std::exception_ptr ep) noexcept
{
    ItemCreateError(item.handler, ep);
    delete &item;
}

void
Stock::ItemCreateAborted(StockItem &item) noexcept
{
    ItemCreateAborted();
    delete &item;
}

void
Stock::ItemCreateError(StockGetHandler &get_handler,
                       std::exception_ptr ep) noexcept
{
    assert(num_create > 0);
    --num_create;

    get_handler.OnStockItemError(ep);

    ScheduleCheckEmpty();
    ScheduleRetryWaiting();
}

void
Stock::ItemCreateAborted() noexcept
{
    assert(num_create > 0);
    --num_create;

    ScheduleCheckEmpty();
    ScheduleRetryWaiting();
}

void
Stock::Put(StockItem &item, bool destroy) noexcept
{
    assert(!item.is_idle);
    assert(&item.stock == this);

    may_clear = false;

    assert(!busy.empty());

    busy.erase(busy.iterator_to(item));

    if (destroy || item.fade || !item.Release()) {
        delete &item;
        ScheduleCheckEmpty();
    } else {
#ifndef NDEBUG
        item.is_idle = true;
#endif

        if (idle.size() == max_idle)
            ScheduleCleanup();

        idle.push_front(item);
    }

    ScheduleRetryWaiting();
}

void
Stock::ItemIdleDisconnect(StockItem &item) noexcept
{
    assert(item.is_idle);

    assert(!idle.empty());

    idle.erase(idle.iterator_to(item));

    if (idle.size() == max_idle)
        UnscheduleCleanup();

    delete &item;
    ScheduleCheckEmpty();
}
<|endoftext|>


<commit_before>/* -*- c-basic-offset: 2 -*- */
/*
  Copyright(C) 2017 Kouhei Sutou <kou@clear-code.com>

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

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "mrn_table_fields_offset_mover.hpp"

namespace mrn {
  TableFieldsOffsetMover::TableFieldsOffsetMover(TABLE *table,
                                                 my_ptrdiff_t diff)
    : table_(table),
      diff_(diff) {
    uint n_columns = table_->s->fields;
    for (uint i = 0; i < n_columns; ++i) {
      Field *field = table_->field[i];
      field->move_field_offset(diff_);
    }
  }

  TableFieldsOffsetMover::~TableFieldsOffsetMover() {
    uint n_columns = table_->s->fields;
    for (uint i = 0; i < n_columns; ++i) {
      Field *field = table_->field[i];
      field->move_field_offset(-diff_);
    }
  }
}
<commit_msg>mysql8.0: include table.h and field.h explicitly<commit_after>/* -*- c-basic-offset: 2 -*- */
/*
  Copyright(C) 2017-2018 Kouhei Sutou <kou@clear-code.com>

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

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "mrn_table_fields_offset_mover.hpp"

#include <table.h>
#include <field.h>

namespace mrn {
  TableFieldsOffsetMover::TableFieldsOffsetMover(TABLE *table,
                                                 my_ptrdiff_t diff)
    : table_(table),
      diff_(diff) {
    uint n_columns = table_->s->fields;
    for (uint i = 0; i < n_columns; ++i) {
      Field *field = table_->field[i];
      field->move_field_offset(diff_);
    }
  }

  TableFieldsOffsetMover::~TableFieldsOffsetMover() {
    uint n_columns = table_->s->fields;
    for (uint i = 0; i < n_columns; ++i) {
      Field *field = table_->field[i];
      field->move_field_offset(-diff_);
    }
  }
}
<|endoftext|>


<commit_before>#include <unistd.h>
#include <sys/wait.h>

#include <iostream>
#include <functional>
#include <thread>

#include <tls.h>

static int client(int fdr, int fdw);
static int server(int fdr, int fdw);

static pid_t spawn(std::function<int(int,int)> func, int &fdr, int &fdw);

int main()
{
    int fd[2][2];

    pid_t pid_client = spawn(&client, fd[0][0], fd[0][1]);
    pid_t pid_server = spawn(&server, fd[1][0], fd[1][1]);

    auto fwd = [](int fdr, int fdw)
    {
        char buf[1024] = {0};
        ssize_t nbytes = 0;
        do {
            nbytes = read(fdr, buf, 1024);
            write(fdw, buf, nbytes);
        } while (nbytes > 0);
        close(fdr);
        close(fdw);
    };

    std::thread fwd1(fwd, fd[0][0], fd[1][1]);
    std::thread fwd2(fwd, fd[1][0], fd[0][1]);

    fwd1.join();
    fwd2.join();

    waitpid(pid_client, nullptr, 0);
    waitpid(pid_server, nullptr, 0);

    std::cout << "bye!" << std::endl;

    return 0;
}

pid_t spawn(std::function<int(int,int)> func, int &fdr, int &fdw)
{
    int fd_w[2];
    int fd_r[2];

    if (pipe(fd_w) == -1) {
        std::cerr << "pipe() failed" << std::endl;
        return -1;
    }

    if (pipe(fd_r) == -1) {
        std::cerr << "pipe() failed" << std::endl;
        return -1;
    }

    const pid_t pid = fork();

    switch (pid) {
    case -1:
        return -1;
    case 0:
        // child process
        close(fd_w[0]);
        close(fd_r[1]);
        func(fd_r[0], fd_w[1]);
        close(fd_r[0]);
        close(fd_w[1]);
        exit(0);
    default:
        // parent process
        close(fd_w[1]);
        close(fd_r[0]);
        fdr = fd_w[0];
        fdw = fd_r[1];
        break;
    }

    return 0;
}

#if 0
int client(int fdr, int fdw)
{
    static char buf[1024] = {0};
    ssize_t nbytes = 0;
    do {
        nbytes = read(0, buf, 1024);
        write(fdw, buf, nbytes);
    } while (nbytes > 0);
}

int server(int fdr, int fdw)
{
    static char buf[1024] = {0};
    ssize_t nbytes = 0;
    do {
        nbytes = read(fdr, buf, 1024);
        write(1, buf, nbytes);
    } while (nbytes > 0);
}
#endif

int client(int fd_read, int fd_write)
{
    static char buf[1024] = {0};
    struct tls *tls;
    struct tls_config *tls_config;
    const char *key_file = "client.key";
    const char *cert_file = "client.crt";
    const char *ca_file = "ca.crt";

    if (tls_init() != 0) {
        std::cerr << "tls_init() failed" << std::endl;
        return -1;
    }

    if ((tls = tls_client()) == NULL) {
        std::cerr << "tls_client() failed" << std::endl;
        return -1;
    }

    if ((tls_config = tls_config_new()) == NULL) {
        std::cerr << "tls_config_new() failed" << std::endl;
        return -1;
    }

    if (tls_config_set_key_file(tls_config, key_file) == -1) {
        std::cerr << "unable to set TLS key file " << key_file << std::endl;
        return -1;
    }
    if (tls_config_set_cert_file(tls_config, cert_file) == -1) {
        std::cerr << "unable to set TLS certificate file " << cert_file << std::endl;
        return -1;
    }
    if (tls_config_set_ca_file(tls_config, ca_file) == -1) {
        std::cerr << "unable to set root CA file " << ca_file << std::endl;
        return -1;
    }

    if (tls_configure(tls, tls_config) != 0) {
        std::cerr << "tls_configure() failed" << std::endl;
        return -1;
    }

    if (tls_connect_fds(tls, fd_read, fd_write, "echo server") != 0) {
        std::cerr << "error: " << tls_error(tls) << std::endl;
        return -1;
    }

    ssize_t nbytes = 0;
    do {
        nbytes = read(0, buf, 1024);
        tls_write(tls, buf, nbytes);
    } while (nbytes > 0);

    tls_close(tls);
    tls_free(tls);
    tls_config_free(tls_config);

    return 0;
}

int server(int fd_read, int fd_write)
{
    static char buf[1024] = {0};
    struct tls *tls;
    struct tls_config *tls_config;
    const char *key_file = "server.key";
    const char *cert_file = "server.crt";
    const char *ca_file = "ca.crt";

    if (tls_init() != 0) {
        std::cerr << "tls_init() failed" << std::endl;
        return -1;
    }

    if ((tls = tls_server()) == NULL) {
        std::cerr << "tls_server() failed" << std::endl;
        return -1;
    }

    if ((tls_config = tls_config_new()) == NULL) {
        std::cerr << "tls_config_new() failed" << std::endl;
        return -1;
    }

    // require certificate from client
    tls_config_verify_client(tls_config);

    if (tls_config_set_key_file(tls_config, key_file) == -1) {
        std::cerr << "unable to set TLS key file " << key_file << std::endl;
        return -1;
    }
    if (tls_config_set_cert_file(tls_config, cert_file) == -1) {
        std::cerr << "unable to set TLS certificate file " << cert_file << std::endl;
        return -1;
    }
    if (tls_config_set_ca_file(tls_config, ca_file) == -1) {
        std::cerr << "unable to set root CA file " << ca_file << std::endl;
        return -1;
    }

    tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL);
    tls_config_set_ciphers(tls_config, "legacy");

    if (tls_configure(tls, tls_config) != 0) {
        std::cerr << "tls_configure() failed: " << tls_error(tls) << std::endl;
        return -1;
    }

    {
        struct tls *tls_cctx;
        ssize_t nbytes = 0;

        if (tls_accept_fds(tls, &tls_cctx, fd_read, fd_write) < 0) {
            std::cerr << "tls_accept_fds() failed: " << tls_error(tls) << std::endl;
            tls_cctx = NULL;
            return -1;
        }

        do {
            nbytes = tls_read(tls_cctx, buf, 1024);
            write(1, buf, nbytes);
        } while (nbytes > 0);

        if (read < 0) {
            std::cerr << "tls_read failed: " << tls_error(tls_cctx) << std::endl;
            return -1;
        }

        tls_close(tls_cctx);
        tls_free(tls_cctx);
    }

    tls_close(tls);
    tls_free(tls);
    tls_config_free(tls_config);

    return 0;
}
<commit_msg>echo: Echo data on server side<commit_after>#include <unistd.h>
#include <sys/wait.h>

#include <iostream>
#include <functional>
#include <thread>

#include <tls.h>

static int client(int fdr, int fdw);
static int server(int fdr, int fdw);

static pid_t spawn(std::function<int(int,int)> func, int &fdr, int &fdw);

int main()
{
    int fd[2][2];

    pid_t pid_client = spawn(&client, fd[0][0], fd[0][1]);
    pid_t pid_server = spawn(&server, fd[1][0], fd[1][1]);

    auto fwd = [](int fdr, int fdw)
    {
        char buf[1024] = {0};
        ssize_t nbytes = 0;
        do {
            nbytes = read(fdr, buf, 1024);
            write(fdw, buf, nbytes);
        } while (nbytes > 0);
        close(fdr);
        close(fdw);
    };

    std::thread fwd1(fwd, fd[0][0], fd[1][1]);
    std::thread fwd2(fwd, fd[1][0], fd[0][1]);

    fwd1.join();
    fwd2.join();

    waitpid(pid_client, nullptr, 0);
    waitpid(pid_server, nullptr, 0);

    std::cout << "bye!" << std::endl;

    return 0;
}

pid_t spawn(std::function<int(int,int)> func, int &fdr, int &fdw)
{
    int fd_w[2];
    int fd_r[2];

    if (pipe(fd_w) == -1) {
        std::cerr << "pipe() failed" << std::endl;
        return -1;
    }

    if (pipe(fd_r) == -1) {
        std::cerr << "pipe() failed" << std::endl;
        return -1;
    }

    const pid_t pid = fork();

    switch (pid) {
    case -1:
        return -1;
    case 0:
        // child process
        close(fd_w[0]);
        close(fd_r[1]);
        func(fd_r[0], fd_w[1]);
        close(fd_r[0]);
        close(fd_w[1]);
        exit(0);
    default:
        // parent process
        close(fd_w[1]);
        close(fd_r[0]);
        fdr = fd_w[0];
        fdw = fd_r[1];
        break;
    }

    return 0;
}

int client(int fd_read, int fd_write)
{
    static char buf[1024] = {0};
    struct tls *tls;
    struct tls_config *tls_config;
    const char *key_file = "client.key";
    const char *cert_file = "client.crt";
    const char *ca_file = "ca.crt";

    if (tls_init() != 0) {
        std::cerr << "tls_init() failed" << std::endl;
        return -1;
    }

    if ((tls = tls_client()) == NULL) {
        std::cerr << "tls_client() failed" << std::endl;
        return -1;
    }

    if ((tls_config = tls_config_new()) == NULL) {
        std::cerr << "tls_config_new() failed" << std::endl;
        return -1;
    }

    if (tls_config_set_key_file(tls_config, key_file) == -1) {
        std::cerr << "unable to set TLS key file " << key_file << std::endl;
        return -1;
    }
    if (tls_config_set_cert_file(tls_config, cert_file) == -1) {
        std::cerr << "unable to set TLS certificate file " << cert_file << std::endl;
        return -1;
    }
    if (tls_config_set_ca_file(tls_config, ca_file) == -1) {
        std::cerr << "unable to set root CA file " << ca_file << std::endl;
        return -1;
    }

    if (tls_configure(tls, tls_config) != 0) {
        std::cerr << "tls_configure() failed" << std::endl;
        return -1;
    }

    if (tls_connect_fds(tls, fd_read, fd_write, "echo server") != 0) {
        std::cerr << "error: " << tls_error(tls) << std::endl;
        return -1;
    }

    ssize_t nbytes = 0;
    do {
        nbytes = read(0, buf, 1024);
        if (nbytes > 0) {
            tls_write(tls, buf, nbytes);
            nbytes = tls_read(tls, buf, 1024);
            write(1, buf, nbytes);
        }
    } while (nbytes > 0);

    tls_close(tls);
    tls_free(tls);
    tls_config_free(tls_config);

    return 0;
}

int server(int fd_read, int fd_write)
{
    static char buf[1024] = {0};
    struct tls *tls;
    struct tls_config *tls_config;
    const char *key_file = "server.key";
    const char *cert_file = "server.crt";
    const char *ca_file = "ca.crt";

    if (tls_init() != 0) {
        std::cerr << "tls_init() failed" << std::endl;
        return -1;
    }

    if ((tls = tls_server()) == NULL) {
        std::cerr << "tls_server() failed" << std::endl;
        return -1;
    }

    if ((tls_config = tls_config_new()) == NULL) {
        std::cerr << "tls_config_new() failed" << std::endl;
        return -1;
    }

    // require certificate from client
    tls_config_verify_client(tls_config);

    if (tls_config_set_key_file(tls_config, key_file) == -1) {
        std::cerr << "unable to set TLS key file " << key_file << std::endl;
        return -1;
    }
    if (tls_config_set_cert_file(tls_config, cert_file) == -1) {
        std::cerr << "unable to set TLS certificate file " << cert_file << std::endl;
        return -1;
    }
    if (tls_config_set_ca_file(tls_config, ca_file) == -1) {
        std::cerr << "unable to set root CA file " << ca_file << std::endl;
        return -1;
    }

    tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL);
    tls_config_set_ciphers(tls_config, "legacy");

    if (tls_configure(tls, tls_config) != 0) {
        std::cerr << "tls_configure() failed: " << tls_error(tls) << std::endl;
        return -1;
    }

    {
        struct tls *tls_cctx;
        ssize_t nbytes = 0;

        if (tls_accept_fds(tls, &tls_cctx, fd_read, fd_write) < 0) {
            std::cerr << "tls_accept_fds() failed: " << tls_error(tls) << std::endl;
            tls_cctx = NULL;
            return -1;
        }

        do {
            nbytes = tls_read(tls_cctx, buf, 1024);
            tls_write(tls_cctx, buf, nbytes);
        } while (nbytes > 0);

        if (read < 0) {
            std::cerr << "tls_read failed: " << tls_error(tls_cctx) << std::endl;
            return -1;
        }

        tls_close(tls_cctx);
        tls_free(tls_cctx);
    }

    tls_close(tls);
    tls_free(tls);
    tls_config_free(tls_config);

    return 0;
}
<|endoftext|>


<commit_before>#include <thread>
#include <random>

#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>

#include <blackhole/utils/nullptr.hpp>
#include <blackhole/platform/random.hpp>

#include "global.hpp"

struct span_t {
    const std::uint64_t trace;
    const std::uint64_t span;
    const std::uint64_t parent;

    span_t(std::uint64_t trace, std::uint64_t span, std::uint64_t parent = 0) :
        trace(trace),
        span(span),
        parent(parent)
    {}

    bool valid() const {
        return trace != 0 && span != 0;
    }

    static const span_t& invalid() {
        static const span_t span;
        return span;
    }

    bool operator==(const span_t& other) const {
        return trace == other.trace && span == other.span && parent == other.parent;
    }

    friend
    std::ostream&
    operator<<(std::ostream& stream, const span_t& span) {
        stream << "span("
               << span.trace << ", " << span.span << ", " << span.parent
               << ")";
        return stream;
    }

private:
    span_t() :
        trace(0),
        span(0),
        parent(0)
    {}
};

template<typename Value>
struct distribution_t {
    typedef Value value_type;

#if defined(HAS_CXX11_RANDOM)
private:
    std::random_device device;
    std::mt19937 generator;
    std::uniform_int_distribution<value_type> distribution;

public:
    distribution_t() :
        generator(device()),
        distribution(1, std::numeric_limits<value_type>::max())
    {}
#else
private:
    std::mt19937 generator;
    std::uniform_int<value_type> distribution;

public:
    distribution_t() :
        generator(static_cast<value_type>(std::time(nullptr))),
        distribution(1, std::numeric_limits<value_type>::max())
    {}
#endif

    value_type next() {
        return distribution(generator);
    }
};

template<typename Distribution = distribution_t<std::uint64_t>>
class random_t {
public:
    typedef Distribution distribution_type;
    typedef typename distribution_type::value_type value_type;

private:
    distribution_type distribution_;

public:
    static random_t& instance() {
        static random_t self;
        return self;
    }

    distribution_type& distribution() {
        return distribution_;
    }

    value_type next() {
        return distribution_.next();
    }

private:
    random_t() {}
};

namespace this_thread {

const span_t& current_span();

} // namespace this_thread

namespace trace {

template<class Random = random_t<>>
class context_t;

class state_t {
    boost::thread_specific_ptr<span_t> span;

    template<class Random> friend class context_t;
    friend const span_t& this_thread::current_span();

public:
    static state_t& instance() {
        static state_t self;
        return self;
    }

private:
    state_t() :
        span(&deleter)
    {}

    span_t* get() const {
        return span.get();
    }

    void reset(span_t* span = nullptr) {
        this->span.reset(span);
    }

    static void deleter(span_t*) {}
};

template<class Random>
class context_t {
public:
    typedef Random random_type;
    typedef typename random_type::value_type value_type;

private:
    span_t span;

public:
    context_t() :
        span(generate())
    {
        state_t::instance().reset(&this->span);
    }

    context_t(value_type trace) :
        span(span_t(trace, trace))
    {
        state_t::instance().reset(&this->span);
    }

    context_t(span_t span) :
        span(std::move(span))
    {
        state_t::instance().reset(&this->span);
    }

    ~context_t() {
        state_t::instance().reset();
    }

private:
    static span_t generate() {
        const value_type id = random_type::instance().next();
        if (auto current = state_t::instance().get()) {
            return span_t(current->trace, id, current->span);
        }

        return span_t(id, id);
    }
};

} // namespace guard

namespace this_thread {

const span_t& current_span() {
    auto span = trace::state_t::instance().get();
    return span ? *span : span_t::invalid();
}

} // namespace this_thread

namespace trace {

template<typename F>
class callable_t {
public:
    typedef F function_type;

private:
    const span_t span;
    function_type f;

public:
    callable_t(function_type f) :
        span(this_thread::current_span()),
        f(std::move(f))
    {}

    template<typename... Args>
    void operator()(Args&&... args) {
        trace::context_t<> context(span);
        f(std::forward<Args>(args)...);
    }
};

template<typename F>
callable_t<F> wrap(F&& f) {
    return callable_t<F>(std::forward<F>(f));
}

} // namespace trace

namespace mock {

class distribution_t {
public:
    typedef std::uint64_t value_type;

    MOCK_METHOD0(next, value_type());
};

} // namespace mock

TEST(Context, Initiating) {
    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(1)
            .WillOnce(Return(42));

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);

    trace::context_t<random_t<mock::distribution_t>> context;
    EXPECT_EQ(42, this_thread::current_span().trace);
    EXPECT_EQ(42, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);
}

TEST(Context, ResetContextAfterLeavingScope) {
    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(1)
            .WillOnce(Return(42));

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);

    {
        trace::context_t<random_t<mock::distribution_t>> context;
        EXPECT_EQ(42, this_thread::current_span().trace);
        EXPECT_EQ(42, this_thread::current_span().span);
        EXPECT_EQ(0, this_thread::current_span().parent);
    }

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);
}

TEST(Context, ExactlyResetNotFallbackContextAfterLeavingScope) {
    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(2)
            .WillOnce(Return(42))
            .WillOnce(Return(100500));

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);

    {
        trace::context_t<random_t<mock::distribution_t>> context;
        EXPECT_EQ(42, this_thread::current_span().trace);
        EXPECT_EQ(42, this_thread::current_span().span);
        EXPECT_EQ(0, this_thread::current_span().parent);

        {
            trace::context_t<random_t<mock::distribution_t>> nested;
            EXPECT_EQ(42, this_thread::current_span().trace);
            EXPECT_EQ(100500, this_thread::current_span().span);
            EXPECT_EQ(42, this_thread::current_span().parent);
        }

        EXPECT_EQ(0, this_thread::current_span().trace);
        EXPECT_EQ(0, this_thread::current_span().span);
        EXPECT_EQ(0, this_thread::current_span().parent);
    }

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);
}

#include <boost/asio.hpp>

void checker(int& counter, span_t expected) {
    EXPECT_EQ(expected, this_thread::current_span());
    counter++;
}

TEST(Context, AsynchronousHandlingViaEventLoop) {
    boost::asio::io_service loop;
    int counter = 0;

    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(1)
            .WillOnce(Return(43));
    trace::context_t<random_t<mock::distribution_t>> context;

    loop.post(
        trace::wrap(
            std::bind(&checker, std::ref(counter), span_t(43, 43))
        )
    );

    loop.run();
    EXPECT_EQ(1, counter);
}

void create_trace(boost::asio::io_service& loop,
                  int& counter,
                  std::uint64_t id)
{
    trace::context_t<random_t<mock::distribution_t>> context(id);
    loop.post(
        trace::wrap(
            std::bind(&checker, std::ref(counter), span_t(id, id))
        )
    );
}

TEST(Context, SimultaneousSimpleTraceHandlingViaEventLoop) {
    boost::asio::io_service loop;
    int counter = 0;

    loop.post(std::bind(&create_trace, std::ref(loop), std::ref(counter), 1));
    loop.post(std::bind(&create_trace, std::ref(loop), std::ref(counter), 2));

    loop.run();
    EXPECT_EQ(2, counter);
}

//!@todo: Letter to myself in the future.
//! After two week vacation you will remember nothing about this subproject.
//! 1. More tests:
//! 1.2. Nested contexts: 2 traces, 2 spans in each.
//! 1.3. Two threads.
//! 1.4. Nested contexts in threads.
//! 1.5. Event loop in threads.
//! 1.6. Single event loop in multiple threads.
//! 1.7. Less code magic with tests. Less code templatization. More concrete
//!      hardcoded cases. In simplifies further reading.
//! 2. Think about returning span context after `trace::context_t` leaves its
//!    scope.

namespace random_delay {

void check_first(int& counter, const boost::system::error_code&) {
    EXPECT_EQ(span_t(44, 44, 0), this_thread::current_span());
    EXPECT_EQ(0, counter);
    counter++;
}

void check_second(int& counter) {
    EXPECT_EQ(span_t(45, 45, 0), this_thread::current_span());
    EXPECT_EQ(1, counter);
    counter++;
}

// Emulate socket accept handler.
void create_first_trace(boost::asio::io_service& loop, int& counter) {
    trace::context_t<random_t<mock::distribution_t>> context(44);
    boost::asio::deadline_timer timer(loop);
    timer.expires_from_now(boost::posix_time::milliseconds(1));
    timer.async_wait(
        trace::wrap(
            std::bind(&check_first, std::ref(counter), std::placeholders::_1)
        )
    );
}

void create_second_trace(boost::asio::io_service& loop, int& counter) {
    trace::context_t<random_t<mock::distribution_t>> context(45);
    loop.post(
        trace::wrap(
            std::bind(&check_second, std::ref(counter))
        )
    );
}

} // namespace random_delay

TEST(Context, AsynchronousContextWithRandomDelay) {
    //! Sequence diagram:
    //! --44----c1
    //! ----45c2--

    boost::asio::io_service loop;
    int counter = 0;

    loop.post(
        std::bind(
            &random_delay::create_first_trace,
            std::ref(loop),
            std::ref(counter)
        )
    );
    loop.post(
        std::bind(
            &random_delay::create_second_trace,
            std::ref(loop),
            std::ref(counter)
        )
    );

    loop.run();

    EXPECT_EQ(span_t::invalid(), this_thread::current_span());

    EXPECT_EQ(2, counter);
}
void print(std::initializer_list<std::string> list) {
    static std::mutex mutex;
    std::lock_guard<std::mutex> lock(mutex);
    auto span = this_thread::current_span();
    std::cout << "[" << std::setw(20) << span.trace << "]" << "[" << std::setw(20) << span.span << "]: ";
    for (auto it = list.begin(); it != list.end(); ++it) {
        std::cout << *it;
    }
    std::cout << std::endl;
}

void func(int i) {
    print({ "fn", boost::lexical_cast<std::string>(i), " start " });
    {
        trace::context_t<> g;
        print({ "fn", boost::lexical_cast<std::string>(i), " mid " });
        if (i <= 1) {
            func(i + 1);
        }
    }

    print({ "fn", boost::lexical_cast<std::string>(i), " end " });
}

TEST(Manual, 1) {
    print({ "main begin" });
    {
        trace::context_t<> g;
        print({ "before invoke" });
        func(1);
        print({ "after invoke" });
    }
    print({ "main end" });
}

TEST(Manual, 2) {
    print({ "main begin" });
    {
        trace::context_t<> g;
        print({ "before invoke" });
        std::thread t1(trace::wrap(std::bind(&func, 1)));
        std::thread t2(trace::wrap(std::bind(&func, 1)));
        print({ "before join" });
        t1.join();
        print({ "middle join" });
        t2.join();
        print({ "after join" });
    }
    print({ "main end" });
}

#include <boost/asio.hpp>

void on_timer(int i, const boost::system::error_code&) {
    print({"timer", boost::lexical_cast<std::string>(i)});
    func(i);
}

TEST(Manual, 3) {
    trace::context_t<> g;
    print({ "main begin" });
    boost::asio::io_service loop;
    boost::asio::deadline_timer timer1(loop);
    boost::asio::deadline_timer timer2(loop);

    std::function<void(const boost::system::error_code&)> fn = std::bind(&on_timer, 1, std::placeholders::_1);

    timer1.expires_from_now(boost::posix_time::milliseconds(100));
    timer2.expires_from_now(boost::posix_time::milliseconds(100));

    timer1.async_wait(trace::wrap(std::bind(&on_timer, 1, std::placeholders::_1)));
    timer2.async_wait(trace::wrap(std::bind(&on_timer, 1, std::placeholders::_1)));
    loop.run();
    print({ "main end" });
}

TEST(Manual, 4) {
//    run loop in the separate thread
//    run service with 2 timers
//    run service with 2 timers
//    join
}
<commit_msg>[Code Style] Style.<commit_after>#include <thread>
#include <random>

#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>

#include <blackhole/utils/nullptr.hpp>
#include <blackhole/platform/random.hpp>

#include "global.hpp"

struct span_t {
    const std::uint64_t trace;
    const std::uint64_t span;
    const std::uint64_t parent;

    span_t(std::uint64_t trace, std::uint64_t span, std::uint64_t parent = 0) :
        trace(trace),
        span(span),
        parent(parent)
    {}

    bool valid() const {
        return trace != 0 && span != 0;
    }

    static const span_t& invalid() {
        static const span_t span;
        return span;
    }

    bool operator==(const span_t& other) const {
        return trace == other.trace && span == other.span && parent == other.parent;
    }

    friend
    std::ostream&
    operator<<(std::ostream& stream, const span_t& span) {
        stream << "span("
               << span.trace << ", " << span.span << ", " << span.parent
               << ")";
        return stream;
    }

private:
    span_t() :
        trace(0),
        span(0),
        parent(0)
    {}
};

template<typename Value>
struct distribution_t {
    typedef Value value_type;

#if defined(HAS_CXX11_RANDOM)
private:
    std::random_device device;
    std::mt19937 generator;
    std::uniform_int_distribution<value_type> distribution;

public:
    distribution_t() :
        generator(device()),
        distribution(1, std::numeric_limits<value_type>::max())
    {}
#else
private:
    std::mt19937 generator;
    std::uniform_int<value_type> distribution;

public:
    distribution_t() :
        generator(static_cast<value_type>(std::time(nullptr))),
        distribution(1, std::numeric_limits<value_type>::max())
    {}
#endif

    value_type next() {
        return distribution(generator);
    }
};

template<typename Distribution = distribution_t<std::uint64_t>>
class random_t {
public:
    typedef Distribution distribution_type;
    typedef typename distribution_type::value_type value_type;

private:
    distribution_type distribution_;

public:
    static random_t& instance() {
        static random_t self;
        return self;
    }

    distribution_type& distribution() {
        return distribution_;
    }

    value_type next() {
        return distribution_.next();
    }

private:
    random_t() {}
};

namespace this_thread {

const span_t& current_span();

} // namespace this_thread

namespace trace {

template<class Random = random_t<>>
class context_t;

class state_t {
    boost::thread_specific_ptr<span_t> span;

    template<class Random> friend class context_t;
    friend const span_t& this_thread::current_span();

public:
    static state_t& instance() {
        static state_t self;
        return self;
    }

private:
    state_t() :
        span(&deleter)
    {}

    span_t* get() const {
        return span.get();
    }

    void reset(span_t* span = nullptr) {
        this->span.reset(span);
    }

    static void deleter(span_t*) {}
};

template<class Random>
class context_t {
public:
    typedef Random random_type;
    typedef typename random_type::value_type value_type;

private:
    span_t span;

public:
    context_t() :
        span(generate())
    {
        state_t::instance().reset(&this->span);
    }

    context_t(value_type trace) :
        span(span_t(trace, trace))
    {
        state_t::instance().reset(&this->span);
    }

    context_t(span_t span) :
        span(std::move(span))
    {
        state_t::instance().reset(&this->span);
    }

    ~context_t() {
        state_t::instance().reset();
    }

private:
    static span_t generate() {
        const value_type id = random_type::instance().next();
        if (auto current = state_t::instance().get()) {
            return span_t(current->trace, id, current->span);
        }

        return span_t(id, id);
    }
};

} // namespace guard

namespace this_thread {

const span_t& current_span() {
    auto span = trace::state_t::instance().get();
    return span ? *span : span_t::invalid();
}

} // namespace this_thread

namespace trace {

template<typename F>
class callable_t {
public:
    typedef F function_type;

private:
    const span_t span;
    function_type f;

public:
    callable_t(function_type f) :
        span(this_thread::current_span()),
        f(std::move(f))
    {}

    template<typename... Args>
    void operator()(Args&&... args) {
        trace::context_t<> context(span);
        f(std::forward<Args>(args)...);
    }
};

template<typename F>
callable_t<F> wrap(F&& f) {
    return callable_t<F>(std::forward<F>(f));
}

} // namespace trace

namespace mock {

class distribution_t {
public:
    typedef std::uint64_t value_type;

    MOCK_METHOD0(next, value_type());
};

} // namespace mock

TEST(Context, Initiating) {
    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(1)
            .WillOnce(Return(42));

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);

    trace::context_t<random_t<mock::distribution_t>> context;
    EXPECT_EQ(42, this_thread::current_span().trace);
    EXPECT_EQ(42, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);
}

TEST(Context, ResetContextAfterLeavingScope) {
    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(1)
            .WillOnce(Return(42));

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);

    {
        trace::context_t<random_t<mock::distribution_t>> context;
        EXPECT_EQ(42, this_thread::current_span().trace);
        EXPECT_EQ(42, this_thread::current_span().span);
        EXPECT_EQ(0, this_thread::current_span().parent);
    }

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);
}

TEST(Context, ExactlyResetNotFallbackContextAfterLeavingScope) {
    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(2)
            .WillOnce(Return(42))
            .WillOnce(Return(100500));

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);

    {
        trace::context_t<random_t<mock::distribution_t>> context;
        EXPECT_EQ(42, this_thread::current_span().trace);
        EXPECT_EQ(42, this_thread::current_span().span);
        EXPECT_EQ(0, this_thread::current_span().parent);

        {
            trace::context_t<random_t<mock::distribution_t>> nested;
            EXPECT_EQ(42, this_thread::current_span().trace);
            EXPECT_EQ(100500, this_thread::current_span().span);
            EXPECT_EQ(42, this_thread::current_span().parent);
        }

        EXPECT_EQ(0, this_thread::current_span().trace);
        EXPECT_EQ(0, this_thread::current_span().span);
        EXPECT_EQ(0, this_thread::current_span().parent);
    }

    EXPECT_EQ(0, this_thread::current_span().trace);
    EXPECT_EQ(0, this_thread::current_span().span);
    EXPECT_EQ(0, this_thread::current_span().parent);
}

#include <boost/asio.hpp>

void checker(int& counter, span_t expected) {
    EXPECT_EQ(expected, this_thread::current_span());
    counter++;
}

TEST(Context, AsynchronousHandlingViaEventLoop) {
    boost::asio::io_service loop;
    int counter = 0;

    auto& distribution = random_t<mock::distribution_t>::instance().distribution();
    EXPECT_CALL(distribution, next())
            .Times(1)
            .WillOnce(Return(43));
    trace::context_t<random_t<mock::distribution_t>> context;

    loop.post(
        trace::wrap(
            std::bind(&checker, std::ref(counter), span_t(43, 43))
        )
    );

    loop.run();
    EXPECT_EQ(1, counter);
}

void create_trace(boost::asio::io_service& loop,
                  int& counter,
                  std::uint64_t id)
{
    trace::context_t<random_t<mock::distribution_t>> context(id);
    loop.post(
        trace::wrap(
            std::bind(&checker, std::ref(counter), span_t(id, id))
        )
    );
}

TEST(Context, SimultaneousSimpleTraceHandlingViaEventLoop) {
    boost::asio::io_service loop;
    int counter = 0;

    loop.post(std::bind(&create_trace, std::ref(loop), std::ref(counter), 1));
    loop.post(std::bind(&create_trace, std::ref(loop), std::ref(counter), 2));

    loop.run();
    EXPECT_EQ(2, counter);
}

//!@todo: Letter to myself in the future.
//! After two week vacation you will remember nothing about this subproject.
//! 1. More tests:
//! 1.2. Nested contexts: 2 traces, 2 spans in each.
//! 1.3. Two threads.
//! 1.4. Nested contexts in threads.
//! 1.5. Event loop in threads.
//! 1.6. Single event loop in multiple threads.
//! 1.7. Less code magic with tests. Less code templatization. More concrete
//!      hardcoded cases. In simplifies further reading.
//! 2. Think about returning span context after `trace::context_t` leaves its
//!    scope.

namespace random_delay {

void check_first(int& counter, const boost::system::error_code&) {
    EXPECT_EQ(span_t(44, 44, 0), this_thread::current_span());
    EXPECT_EQ(0, counter);
    counter++;
}

void check_second(int& counter) {
    EXPECT_EQ(span_t(45, 45, 0), this_thread::current_span());
    EXPECT_EQ(1, counter);
    counter++;
}

// Emulate socket accept handler.
void create_first_trace(boost::asio::io_service& loop, int& counter) {
    trace::context_t<random_t<mock::distribution_t>> context(44);
    boost::asio::deadline_timer timer(loop);
    timer.expires_from_now(boost::posix_time::milliseconds(1));
    timer.async_wait(
        trace::wrap(
            std::bind(&check_first, std::ref(counter), std::placeholders::_1)
        )
    );
}

void create_second_trace(boost::asio::io_service& loop, int& counter) {
    trace::context_t<random_t<mock::distribution_t>> context(45);
    loop.post(
        trace::wrap(
            std::bind(&check_second, std::ref(counter))
        )
    );
}

} // namespace random_delay

TEST(Context, AsynchronousContextWithRandomDelay) {
    //! Sequence diagram:
    //! --44----c1
    //! ----45c2--

    boost::asio::io_service loop;
    int counter = 0;

    loop.post(
        std::bind(
            &random_delay::create_first_trace,
            std::ref(loop),
            std::ref(counter)
        )
    );
    loop.post(
        std::bind(
            &random_delay::create_second_trace,
            std::ref(loop),
            std::ref(counter)
        )
    );

    loop.run();

    EXPECT_EQ(span_t::invalid(), this_thread::current_span());

    EXPECT_EQ(2, counter);
}
void print(std::initializer_list<std::string> list) {
    static std::mutex mutex;
    std::lock_guard<std::mutex> lock(mutex);
    auto span = this_thread::current_span();
    std::cout << "[" << std::setw(20) << span.trace << "] ["
              << std::setw(20) << span.span << "]: ";
    for (auto it = list.begin(); it != list.end(); ++it) {
        std::cout << *it;
    }
    std::cout << std::endl;
}

void func(int i) {
    print({ "fn", boost::lexical_cast<std::string>(i), " start " });
    {
        trace::context_t<> g;
        print({ "fn", boost::lexical_cast<std::string>(i), " mid " });
        if (i <= 1) {
            func(i + 1);
        }
    }

    print({ "fn", boost::lexical_cast<std::string>(i), " end " });
}

TEST(Manual, 1) {
    print({ "main begin" });
    {
        trace::context_t<> g;
        print({ "before invoke" });
        func(1);
        print({ "after invoke" });
    }
    print({ "main end" });
}

TEST(Manual, 2) {
    print({ "main begin" });
    {
        trace::context_t<> g;
        print({ "before invoke" });
        std::thread t1(trace::wrap(std::bind(&func, 1)));
        std::thread t2(trace::wrap(std::bind(&func, 1)));
        print({ "before join" });
        t1.join();
        print({ "middle join" });
        t2.join();
        print({ "after join" });
    }
    print({ "main end" });
}

#include <boost/asio.hpp>

void on_timer(int i, const boost::system::error_code&) {
    print({"timer", boost::lexical_cast<std::string>(i)});
    func(i);
}

TEST(Manual, 3) {
    trace::context_t<> g;
    print({ "main begin" });
    boost::asio::io_service loop;
    boost::asio::deadline_timer timer1(loop);
    boost::asio::deadline_timer timer2(loop);

    std::function<void(const boost::system::error_code&)> fn = std::bind(&on_timer, 1, std::placeholders::_1);

    timer1.expires_from_now(boost::posix_time::milliseconds(100));
    timer2.expires_from_now(boost::posix_time::milliseconds(100));

    timer1.async_wait(trace::wrap(std::bind(&on_timer, 1, std::placeholders::_1)));
    timer2.async_wait(trace::wrap(std::bind(&on_timer, 1, std::placeholders::_1)));
    loop.run();
    print({ "main end" });
}

TEST(Manual, 4) {
//    run loop in the separate thread
//    run service with 2 timers
//    run service with 2 timers
//    join
}
<|endoftext|>