diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2016-08-12 17:00:37 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2016-08-12 17:00:37 +0200 |
commit | 0ed45348c428a950db589caa227efa86af499170 (patch) | |
tree | c24267d08b40726ccfb706da6fc88144565215ee | |
parent | 41c0a5169bfb6e87a48f90a2af57c03bba707470 (diff) |
compat: add util header
The run_in_thread_{sync,async} functions inject a functional into a Qt event
queue. The former also takes care to process void return values.
-rw-r--r-- | opentrack-compat/options.hpp | 6 | ||||
-rw-r--r-- | opentrack-compat/util.hpp | 93 |
2 files changed, 94 insertions, 5 deletions
diff --git a/opentrack-compat/options.hpp b/opentrack-compat/options.hpp index 78699f63..40aca2de 100644 --- a/opentrack-compat/options.hpp +++ b/opentrack-compat/options.hpp @@ -43,11 +43,7 @@ #include "export.hpp" #include "slider.hpp" -// just put them here for lack of util header -#define progn(...) ([&]() { __VA_ARGS__ }()) -template<typename t> using mem = std::shared_ptr<t>; -template<typename t> using ptr = std::unique_ptr<t>; -#include "make-unique.hpp" +#include "util.hpp" #define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" #define OPENTRACK_DEFAULT_CONFIG "default.ini" diff --git a/opentrack-compat/util.hpp b/opentrack-compat/util.hpp new file mode 100644 index 00000000..1217e654 --- /dev/null +++ b/opentrack-compat/util.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "make-unique.hpp" + +#include <memory> +#include <utility> +#include <type_traits> +#include <thread> +#include <condition_variable> + +#include <QDebug> + +#define progn(...) ([&]() { __VA_ARGS__ }()) +template<typename t> using mem = std::shared_ptr<t>; +template<typename t> using ptr = std::unique_ptr<t>; + +template<typename F> +void run_in_thread_async(QObject* obj, F&& fun) +{ + QObject src; + src.moveToThread(obj->thread()); + QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection); +} + +namespace detail { + +template<typename t> +struct run_in_thread_traits +{ + using type = t; + using ret_type = t&&; + static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; } + static inline ret_type&& pass(ret_type&& val) { return std::move(val); } + template<typename F> static ret_type call(F& fun) { return std::move(fun()); } +}; + +template<> +struct run_in_thread_traits<void> +{ + using type = unsigned char; + using ret_type = void; + static inline void assign(unsigned char&, unsigned char&&) {} + static inline void pass(type&&) {} + template<typename F> static type&& call(F& fun) { fun(); return std::move(type(0)); } +}; + +} + +template<typename F> +auto run_in_thread_sync(QObject* obj, F&& fun) + -> typename detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>::ret_type +{ + using lock_guard = std::unique_lock<std::mutex>; + + std::mutex mtx; + lock_guard guard(mtx); + std::condition_variable cvar; + + std::thread::id waiting_thread = std::this_thread::get_id(); + + using traits = detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>; + + typename traits::type ret; + + bool skip_wait = false; + + { + QObject src; + src.moveToThread(obj->thread()); + QObject::connect(&src, + &QObject::destroyed, + obj, + [&]() { + std::thread::id calling_thread = std::this_thread::get_id(); + if (waiting_thread == calling_thread) + { + skip_wait = true; + traits::assign(ret, traits::call(fun)); + } + else + { + lock_guard guard(mtx); + traits::assign(ret, traits::call(fun)); + cvar.notify_one(); + } + }, + Qt::AutoConnection); + } + + if (!skip_wait) + cvar.wait(guard); + return traits::pass(std::move(ret)); +} |