diff options
Diffstat (limited to 'indra/llcommon/llmainthreadtask.h')
-rw-r--r-- | indra/llcommon/llmainthreadtask.h | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/indra/llcommon/llmainthreadtask.h b/indra/llcommon/llmainthreadtask.h new file mode 100644 index 0000000000..526374981a --- /dev/null +++ b/indra/llcommon/llmainthreadtask.h @@ -0,0 +1,102 @@ +/** + * @file llmainthreadtask.h + * @author Nat Goodspeed + * @date 2019-12-04 + * @brief LLMainThreadTask dispatches work to the main thread. When invoked on + * the main thread, it performs the work inline. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLMAINTHREADTASK_H) +#define LL_LLMAINTHREADTASK_H + +#include "lleventtimer.h" +#include "llthread.h" +#include "lockstatic.h" +#include "llmake.h" +#include <future> +#include <type_traits> // std::result_of +#include <boost/signals2/dummy_mutex.hpp> + +class LLMainThreadTask +{ +private: + // Don't instantiate this class -- use dispatch() instead. + LLMainThreadTask() {} + // If our caller doesn't explicitly pass a LockStatic<something>, make a + // fake one. + struct Static + { + boost::signals2::dummy_mutex mMutex; + }; + typedef llthread::LockStatic<Static> LockStatic; + +public: + /// dispatch() is the only way to invoke this functionality. + /// If you call it with a LockStatic<something>, dispatch() unlocks it + /// before blocking for the result. + template <typename Static, typename CALLABLE> + static auto dispatch(llthread::LockStatic<Static>& lk, CALLABLE&& callable) + -> decltype(callable()) + { + if (on_main_thread()) + { + // we're already running on the main thread, perfect + return callable(); + } + else + { + // It's essential to construct LLEventTimer subclass instances on + // the heap because, on completion, LLEventTimer deletes them. + // Once we enable C++17, we can use Class Template Argument + // Deduction. Until then, use llmake_heap(). + auto* task = llmake_heap<Task>(std::forward<CALLABLE>(callable)); + // The moment we construct a new LLEventTimer subclass object, its + // tick() method might get called. However, its tick() method + // might depend on something locked by the passed LockStatic. + // Unlock it so tick() can proceed. + lk.unlock(); + auto future = task->mTask.get_future(); + // Now simply block on the future. + return future.get(); + } + } + + /// You can call dispatch() without a LockStatic<something>. + template <typename CALLABLE> + static auto dispatch(CALLABLE&& callable) -> decltype(callable()) + { + LockStatic lk; + return dispatch(lk, std::forward<CALLABLE>(callable)); + } + +private: + template <typename CALLABLE> + struct Task: public LLEventTimer + { + Task(CALLABLE&& callable): + // no wait time: call tick() next chance we get + LLEventTimer(0), + mTask(std::forward<CALLABLE>(callable)) + {} + BOOL tick() override + { + // run the task on the main thread, will populate the future + // obtained by get_future() + mTask(); + // tell LLEventTimer we're done (one shot) + return TRUE; + } + // Given arbitrary CALLABLE, which might be a lambda, how are we + // supposed to obtain its signature for std::packaged_task? It seems + // redundant to have to add an argument list to engage result_of, then + // add the argument list again to complete the signature. At least we + // only support a nullary CALLABLE. + std::packaged_task<typename std::result_of<CALLABLE()>::type()> mTask; + }; +}; + +#endif /* ! defined(LL_LLMAINTHREADTASK_H) */ |