jcsyshc 3 лет назад
Родитель
Сommit
8a35b96f95

+ 0 - 4
src/core/sophiar_manager.cpp

@@ -626,10 +626,6 @@ namespace sophiar {
         pimpl->on_object_stopped(obj);
     }
 
-    timestamp_type sophiar_manager::get_global_obj_update_timestamp(global_obj_index_type obj_index) {
-        return pimpl->global_obj_pool[obj_index].last_update_ts;
-    }
-
     global_obj_index_type sophiar_manager::register_global_obj_impl(const std::string &obj_name,
                                                                     std::type_index obj_type,
                                                                     void *placeholder) {

+ 3 - 4
src/core/sophiar_manager.h

@@ -49,10 +49,11 @@ namespace sophiar {
 
         template<typename SmallObjType>
         global_obj_index_type register_global_obj(const std::string &obj_name) {
+            static_assert(std::is_default_constructible_v<SmallObjType>);
             auto ret = register_global_obj_impl(obj_name, typeid(SmallObjType), nullptr);
-            if ((~ret) == 0) { // first time of register
+            if (static_cast<decltype(ret)>(~ret) == 0) { // first time of register
                 using ptr_type = typename SmallObjType::pointer;
-                auto placeholder = new ptr_type;
+                auto placeholder = new ptr_type(SmallObjType::new_instance()); // requires default constructable
                 ret = register_global_obj_impl(obj_name, typeid(SmallObjType), placeholder);
             }
             return ret;
@@ -76,8 +77,6 @@ namespace sophiar {
             return *static_cast<ptr_type *>(placeholder);
         }
 
-        timestamp_type get_global_obj_update_timestamp(global_obj_index_type obj_index);
-
         signal_watcher request_global_obj_update_watcher(global_obj_index_type obj_index);
 
         template<typename ...Args>

+ 2 - 1
src/core/transform_tree.cpp

@@ -1,9 +1,10 @@
 #include "transform_tree.h"
 
 #include "core/sophiar_manager.h"
+#include "utility/coro_worker.hpp"
+#include "utility/global_obj_helper.hpp"
 #include "utility/named_vector.hpp"
 #include "utility/signal_muxer.hpp"
-#include "utility/coro_worker.hpp"
 
 #include <fmt/format.h>
 

+ 107 - 109
src/utility/coro_signal2.hpp

@@ -1,40 +1,80 @@
 #ifndef SOPHIAR2_CORO_SIGNAL2_HPP
 #define SOPHIAR2_CORO_SIGNAL2_HPP
 
-#ifndef CORO_SIGNAL2_USE_TIMER // channel based implementation, ~ 6% faster than timer
-
 #include "core/timestamp_helper.hpp"
-#include "third_party/scope_guard.hpp"
 
 #include <boost/asio/awaitable.hpp>
-#include <boost/asio/experimental/channel.hpp>
 #include <boost/asio/use_awaitable.hpp>
 #include <boost/core/noncopyable.hpp>
-#include <boost/intrusive/list.hpp>
 
 #include <cassert>
 
+#ifdef CORO_SIGNAL2_USE_TIMER // timer based implementation
+
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/redirect_error.hpp>
+
+#else // channel based implementation, ~ 6% faster than timer
+
+#include "third_party/scope_guard.hpp"
+
+#include <boost/asio/experimental/channel.hpp>
+#include <boost/intrusive/list.hpp>
+
+#endif
+
 namespace sophiar {
 
     class coro_signal2;
 
+#ifdef CORO_SIGNAL2_USE_TIMER
+
+    class signal_watcher {
+
+#else
+
     using singal_watcher_base =
             boost::intrusive::list_base_hook<
                     boost::intrusive::link_mode<
                             boost::intrusive::auto_unlink>>;
 
     class signal_watcher : public singal_watcher_base {
+
+#endif
+
     public:
 
         template<typename Executor>
         explicit signal_watcher(Executor &executor, coro_signal2 &_sig)
+
+#ifdef CORO_SIGNAL2_USE_TIMER
+
+        : sig(_sig) {}
+
+#else
+
                 : sig(_sig),
                   chan(executor, 1) {}
 
+#endif
+
+
         signal_watcher(signal_watcher &&other) noexcept
+
+#ifdef CORO_SIGNAL2_USE_TIMER
+
+        : sig(other.sig),
+          last_watch_ts(other.last_watch_ts) {}
+
+#else
+
                 : sig(other.sig),
                   chan(std::move(other.chan)),
-                  last_watch_ts(other.last_watch_ts) {}
+                  last_watch_ts(other.last_watch_ts) {
+            assert(!other.is_linked());
+        }
+
+#endif
 
         bool try_wait();
 
@@ -42,16 +82,24 @@ namespace sophiar {
 
         void sync();
 
+        timestamp_type get_last_update_ts() const {
+            return last_watch_ts;
+        }
+
     private:
 
         friend class coro_signal2;
 
+#ifndef CORO_SIGNAL2_USE_TIMER
+
         using channel_type = boost::asio::experimental::channel<
                 void(boost::system::error_code, bool)>;
+        channel_type chan;
+
+#endif
 
         timestamp_type last_watch_ts = 0;
         coro_signal2 &sig;
-        channel_type chan;
 
     };
 
@@ -59,7 +107,19 @@ namespace sophiar {
     public:
 
         template<typename Executor>
-        explicit coro_signal2(Executor &) {}
+        explicit coro_signal2(Executor &executor)
+
+#ifdef CORO_SIGNAL2_USE_TIMER
+
+        :timer(executor) {
+    timer.expires_at(boost::posix_time::pos_infin);
+}
+
+#else
+
+        {}
+
+#endif
 
         template<typename Executor>
         auto new_watcher(Executor &executor) {
@@ -68,6 +128,13 @@ namespace sophiar {
 
         void try_notify_all(timestamp_type ts = current_timestamp()) {
             last_notify_ts = ts;
+
+#ifdef CORO_SIGNAL2_USE_TIMER
+
+            timer.cancel();
+
+#else
+
             list_type requeue_list;
             while (!watcher_list.empty()) {
                 auto &node = watcher_list.front();
@@ -81,20 +148,31 @@ namespace sophiar {
                 }
             }
             watcher_list.swap(requeue_list);
+
+#endif
+
         }
 
     private:
 
         friend class signal_watcher;
 
+        timestamp_type last_notify_ts = 0;
+
+#ifdef CORO_SIGNAL2_USE_TIMER
+
+        boost::asio::deadline_timer timer;
+
+#else
+
         using list_type =
                 boost::intrusive::list<
                         signal_watcher,
                         boost::intrusive::constant_time_size<false>>;
-
-        timestamp_type last_notify_ts = 0;
         list_type watcher_list;
 
+#endif
+
     };
 
     inline bool signal_watcher::try_wait() {
@@ -106,7 +184,13 @@ namespace sophiar {
     }
 
     inline boost::asio::awaitable<void> signal_watcher::coro_wait(bool auto_sync) {
+
+#ifndef CORO_SIGNAL2_USE_TIMER
+
         assert(!chan.ready());
+
+#endif
+
         if (auto_sync) {
             sync();
         } else {
@@ -115,117 +199,33 @@ namespace sophiar {
                 co_return;
             }
         }
+
+#ifndef CORO_SIGNAL2_USE_TIMER
+
         auto closer = sg::make_scope_guard([&]() {
             if (is_linked()) unlink();
             if (chan.ready()) chan.reset();
         });
         assert(!is_linked());
         sig.watcher_list.push_back(*this);
-        for (;;) {
-            co_await chan.async_receive(boost::asio::use_awaitable);
-            if (last_watch_ts < sig.last_notify_ts) break;
-        }
-        sync();
-        co_return;
-    }
-
-    inline void signal_watcher::sync() {
-        last_watch_ts = sig.last_notify_ts;
-    }
 
-}
-
-#else // timer based implementation
-
-#include "core/timestamp_helper.hpp"
-
-#include <boost/asio/awaitable.hpp>
-#include <boost/asio/deadline_timer.hpp>
-#include <boost/asio/redirect_error.hpp>
-#include <boost/asio/use_awaitable.hpp>
-#include <boost/core/noncopyable.hpp>
-
-#include <cassert>
-
-namespace sophiar {
-
-    class coro_signal2;
-
-    class signal_watcher {
-    public:
-
-        explicit signal_watcher(coro_signal2 &_sig)
-                : sig(_sig) {}
-
-        signal_watcher(signal_watcher &&other) noexcept
-                : sig(other.sig),
-                  last_watch_ts(other.last_watch_ts) {}
-
-        bool try_wait();
-
-        boost::asio::awaitable<void> coro_wait(bool auto_sync = true);
-
-        void sync();
-
-    private:
-
-        friend class coro_signal2;
-
-        timestamp_type last_watch_ts = 0;
-        coro_signal2 &sig;
-
-    };
-
-    class coro_signal2 : private boost::noncopyable {
-    public:
-
-        template<typename Executor>
-        explicit coro_signal2(Executor &executor)
-                :timer(executor) {
-            timer.expires_at(boost::posix_time::pos_infin);
-        }
-
-        template<typename Executor>
-        auto new_watcher(Executor &) {
-            return signal_watcher{*this};
-        }
-
-        void try_notify_all(timestamp_type ts = current_timestamp()) {
-            last_notify_ts = ts;
-            timer.cancel();
-        }
-
-    private:
-
-        friend class signal_watcher;
-
-        timestamp_type last_notify_ts = 0;
-        boost::asio::deadline_timer timer;
+#endif
 
-    };
+        for (;;) {
 
-    inline bool signal_watcher::try_wait() {
-        if (last_watch_ts < sig.last_notify_ts) {
-            sync();
-            return true;
-        }
-        return false;
-    }
+#ifdef CORO_SIGNAL2_USE_TIMER
 
-    inline boost::asio::awaitable<void> signal_watcher::coro_wait(bool auto_sync) {
-        if (auto_sync) {
-            sync();
-        } else {
-            if (last_watch_ts < sig.last_notify_ts) {
-                sync();
-                co_return;
-            }
-        }
-        for (;;) {
             boost::system::error_code ec;
             co_await sig.timer.async_wait(
                     boost::asio::redirect_error(boost::asio::use_awaitable, ec));
             assert(ec == boost::asio::error::operation_aborted);
+
+#else
+
+            co_await chan.async_receive(boost::asio::use_awaitable);
+
+#endif
+
             if (last_watch_ts < sig.last_notify_ts) break;
         }
         sync();
@@ -238,6 +238,4 @@ namespace sophiar {
 
 }
 
-#endif
-
 #endif //SOPHIAR2_CORO_SIGNAL2_HPP

+ 94 - 0
src/utility/global_obj_helper.hpp

@@ -0,0 +1,94 @@
+#ifndef SOPHIAR2_GLOBAL_OBJ_HELPER_HPP
+#define SOPHIAR2_GLOBAL_OBJ_HELPER_HPP
+
+#include "core/small_obj.hpp"
+#include "core/sophiar_manager.h"
+#include "utility/coro_signal2.hpp"
+
+namespace sophiar {
+
+    struct global_obj_helper_auto_sync {
+    };
+    struct global_obj_helper_manual_sync {
+    };
+
+    template<typename SmallObjType, typename SyncMode>
+    class global_obj_delegate {
+    public:
+
+        using pointer_type = typename SmallObjType::pointer;
+
+        global_obj_delegate(sophiar_manager &_manager,
+                            global_obj_index_type _obj_index)
+                : manager(_manager),
+                  watcher(_manager.request_global_obj_update_watcher(_obj_index)),
+                  obj_index(_obj_index) {
+            watcher.sync();
+            obj_ptr = manager.get_global_obj<SmallObjType>(obj_index);
+            assert(obj_ptr.get() != nullptr);
+        }
+
+        global_obj_delegate(global_obj_delegate &other) noexcept
+                : manager(other.manager),
+                  watcher(std::move(other.watcher)),
+                  obj_index(other.obj_index),
+                  obj_ptr(std::move(other.obj_ptr)) {}
+
+        void set_value(const pointer_type &ptr,
+                       timestamp_type ts = current_timestamp()) {
+            assert(ptr.get() != nullptr);
+            manager.update_global_obj<SmallObjType>(obj_index, ptr, ts);
+        }
+
+        pointer_type get_value() {
+            if constexpr (std::is_same_v<SyncMode, global_obj_helper_auto_sync>) {
+                if (watcher.try_wait()) { // new value available
+                    obj_ptr = manager.get_global_obj<SmallObjType>(obj_index);
+                }
+            }
+            assert(obj_ptr.get() != nullptr);
+            return obj_ptr;
+        }
+
+        timestamp_type get_last_update_ts() const {
+            return watcher.get_last_update_ts();
+        }
+
+        global_obj_delegate &operator=(const pointer_type &ptr) {
+            set_value(ptr);
+            return *this;
+        }
+
+        const SmallObjType &operator*() {
+            return *get_value();
+        }
+
+        const SmallObjType *operator->() {
+            return get_value().get();
+        }
+
+        template<typename OtherSyncMode=SyncMode>
+        boost::asio::awaitable<void> coro_wait_update(bool auto_sync = true) {
+            static_assert(std::is_same_v<OtherSyncMode, global_obj_helper_manual_sync>);
+            co_await watcher.coro_wait(auto_sync);
+            obj_ptr = manager.get_global_obj<SmallObjType>(obj_index);
+            co_return;
+        }
+
+    private:
+        global_obj_index_type obj_index;
+        pointer_type obj_ptr;
+        signal_watcher watcher;
+        sophiar_manager &manager;
+
+    };
+
+    template<typename SmallObjType>
+    using global_obj_auto_sync_delegate = global_obj_delegate<SmallObjType, global_obj_helper_auto_sync>;
+
+    template<typename SmallObjType>
+    using global_obj_manual_sync_delegate = global_obj_delegate<SmallObjType, global_obj_helper_manual_sync>;
+
+}
+
+#endif //SOPHIAR2_GLOBAL_OBJ_HELPER_HPP

+ 1 - 0
tests/CMakeLists.txt

@@ -18,6 +18,7 @@ target_link_libraries(test_core ${Boost_LIBRARIES} ${EXTRA_LIBS})
 add_executable(test_utility
         utility/coro_signal2.cpp
         utility/coro_worker.cpp
+        utility/global_obj_helper.cpp
         ${EXTERN_DEF_FILES}
         ${CORE_IMPL_FILES})
 target_compile_definitions(test_utility PUBLIC SOPHIAR_TEST)

+ 70 - 0
tests/utility/global_obj_helper.cpp

@@ -0,0 +1,70 @@
+#define BOOST_TEST_DYN_LINK
+
+#include "core/sophiar_obj.hpp"
+#include "utility/global_obj_helper.hpp"
+#include "utility/debug_utility.hpp"
+
+#include <boost/asio/co_spawn.hpp>
+#include <boost/asio/detached.hpp>
+#include <boost/asio/this_coro.hpp>
+#include <boost/asio/use_awaitable.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <iostream>
+#include <vector>
+
+using boost::asio::awaitable;
+using boost::asio::co_spawn;
+using boost::asio::detached;
+using boost::asio::use_awaitable;
+
+using namespace sophiar;
+using namespace std::chrono_literals;
+
+awaitable<void> test_global_obj_helper_1() {
+    struct small_a : public small_obj<small_a> {
+        int value;
+
+        explicit small_a(int _value = 0) { value = _value; }
+    };
+
+    auto obj_a_index = global_sophiar_manager.register_global_obj<small_a>("obj_a");
+    global_sophiar_manager.update_global_obj<small_a>(obj_a_index, small_a::new_instance(123));
+
+    co_spawn(global_context, [=]() -> awaitable<void> {
+        auto obj_a = global_obj_auto_sync_delegate<small_a>(
+                global_sophiar_manager, obj_a_index);
+        BOOST_TEST(obj_a->value == 123);
+        co_await coro_sleep(10ms);
+        BOOST_TEST(obj_a->value == 234);
+        co_await coro_sleep(10ms);
+        BOOST_TEST(obj_a->value == 456);
+        co_return;
+    }, detached);
+
+    co_spawn(global_context, [=]() -> awaitable<void> {
+        auto obj_a = global_obj_manual_sync_delegate<small_a>(
+                global_sophiar_manager, obj_a_index);
+        BOOST_TEST(obj_a->value == 123);
+        co_await coro_sleep(10ms);
+        BOOST_TEST(obj_a->value == 123);
+        co_await obj_a.coro_wait_update(false);
+        BOOST_TEST(obj_a->value == 234);
+        co_return;
+    }, detached);
+
+    global_sophiar_manager.update_global_obj<small_a>(obj_a_index, small_a::new_instance(234));
+
+    co_await coro_sleep(15ms);
+    auto obj_a = global_obj_auto_sync_delegate<small_a>(
+            global_sophiar_manager, obj_a_index);
+    obj_a = small_a::new_instance(456);
+
+    co_return;
+}
+
+BOOST_AUTO_TEST_CASE(test_global_obj_helper) {
+    co_spawn(global_context, test_global_obj_helper_1(), detached);
+    global_context.run();
+}