diff options
Diffstat (limited to 'indra/llcommon/llcallbacklist.h')
-rw-r--r-- | indra/llcommon/llcallbacklist.h | 228 |
1 files changed, 206 insertions, 22 deletions
diff --git a/indra/llcommon/llcallbacklist.h b/indra/llcommon/llcallbacklist.h index 89716cd74c..522a9b838b 100644 --- a/indra/llcommon/llcallbacklist.h +++ b/indra/llcommon/llcallbacklist.h @@ -27,53 +27,237 @@ #ifndef LL_LLCALLBACKLIST_H #define LL_LLCALLBACKLIST_H +#include "lldate.h" +#include "llsingleton.h" #include "llstl.h" -#include <boost/function.hpp> -#include <list> +#include <boost/container_hash/hash.hpp> +#include <boost/heap/fibonacci_heap.hpp> +#include <boost/signals2.hpp> +#include <functional> +#include <unordered_map> -class LLCallbackList +/***************************************************************************** +* LLCallbackList: callbacks every idle tick (every callFunctions() call) +*****************************************************************************/ +class LLCallbackList: public LLSingleton<LLCallbackList> { + LLSINGLETON(LLCallbackList); public: typedef void (*callback_t)(void*); - typedef std::pair< callback_t,void* > callback_pair_t; - // NOTE: It is confirmed that we DEPEND on the order provided by using a list :( - // - typedef std::list< callback_pair_t > callback_list_t; - - LLCallbackList(); + typedef boost::signals2::signal<void()> callback_list_t; + typedef callback_list_t::slot_type callable_t; + typedef boost::signals2::connection handle_t; + typedef boost::signals2::scoped_connection temp_handle_t; + typedef std::function<bool ()> bool_func_t; + ~LLCallbackList(); - void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data) + handle_t addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data) + handle_t addFunction( const callable_t& func ); bool containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair bool deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found - void callFunctions(); // calls all functions + void deleteFunction( const handle_t& handle ); + void callFunctions(); // calls all functions void deleteAllFunctions(); + handle_t doOnIdleOneTime( const callable_t& func ); + handle_t doOnIdleRepeating( const bool_func_t& func ); + bool isRunning(const handle_t& handle) const { return handle.connected(); }; + static void test(); protected: - - inline callback_list_t::iterator find(callback_t func, void *data); - callback_list_t mCallbackList; + + // "Additional specializations for std::pair and the standard container + // types, as well as utility functions to compose hashes are available in + // boost::hash." + // https://en.cppreference.com/w/cpp/utility/hash + typedef std::pair< callback_t,void* > callback_pair_t; + typedef std::unordered_map<callback_pair_t, handle_t, + boost::hash<callback_pair_t>> lookup_table; + lookup_table mLookup; }; -typedef boost::function<void ()> nullary_func_t; -typedef boost::function<bool ()> bool_func_t; +/*-------------------- legacy names in global namespace --------------------*/ +#define gIdleCallbacks (LLCallbackList::instance()) + +using nullary_func_t = LLCallbackList::callable_t; +using bool_func_t = LLCallbackList::bool_func_t; // Call a given callable once in idle loop. -void doOnIdleOneTime(nullary_func_t callable); +inline +LLCallbackList::handle_t doOnIdleOneTime(nullary_func_t callable) +{ + return gIdleCallbacks.doOnIdleOneTime(callable); +} // Repeatedly call a callable in idle loop until it returns true. -void doOnIdleRepeating(bool_func_t callable); +inline +LLCallbackList::handle_t doOnIdleRepeating(bool_func_t callable) +{ + return gIdleCallbacks.doOnIdleRepeating(callable); +} + +/***************************************************************************** +* LLLater: callbacks at some future time +*****************************************************************************/ +class LLLater: public LLSingleton<LLLater> +{ + LLSINGLETON(LLLater); + + using token_t = U32; + + // Define a struct for our priority queue entries, instead of using + // a tuple, because we need to define the comparison operator. + struct func_at + { + nullary_func_t mFunc; + LLDate::timestamp mTime; + token_t mToken; + + func_at(const nullary_func_t& func, LLDate::timestamp tm, token_t token): + mFunc(func), + mTime(tm), + mToken(token) + {} + + friend bool operator<(const func_at& lhs, const func_at& rhs) + { + // use greater-than because we want fibonacci_heap to select the + // EARLIEST time as the top() + return lhs.mTime > rhs.mTime; + } + }; + + // Accept default stable<false>: when two funcs have the same timestamp, + // we don't care in what order they're called. + // Specify constant_time_size<false>: we don't need to optimize the size() + // method, iow we don't need to store and maintain a count of entries. + typedef boost::heap::fibonacci_heap<func_at, boost::heap::constant_time_size<false>> + queue_t; + +public: + // If tasks that come ready during a given tick() take longer than this, + // defer any subsequent ready tasks to a future tick() call. + static constexpr F32 TIMESLICE{ 0.005f }; + class handle_t + { + private: + friend class LLLater; + token_t token; + public: + handle_t(token_t token=0): token(token) {} + bool operator==(const handle_t& rhs) const { return this->token == rhs.token; } + explicit operator bool() const { return bool(token); } + bool operator!() const { return ! bool(*this); } + }; + + // Call a given callable once at specified timestamp. + handle_t doAtTime(nullary_func_t callable, LLDate::timestamp time); + + // Call a given callable once after specified interval. + handle_t doAfterInterval(nullary_func_t callable, F32 seconds); + + // Call a given callable every specified number of seconds, until it returns true. + handle_t doPeriodically(bool_func_t callable, F32 seconds); + + // test whether specified handle is still live + bool isRunning(handle_t timer); + + // Cancel a future timer set by doAtTime(), doAfterInterval(), doPeriodically(). + // Return true iff the handle corresponds to a live timer. + bool cancel(const handle_t& timer); + // If we're canceling a non-const handle_t, also clear it so we need not + // cancel again. + bool cancel(handle_t& timer); + + // Store a handle_t returned by doAtTime(), doAfterInterval() or + // doPeriodically() in a temp_handle_t to cancel() automatically on + // destruction of the temp_handle_t. + class temp_handle_t + { + public: + temp_handle_t() {} + temp_handle_t(const handle_t& hdl): mHandle(hdl) {} + temp_handle_t(const temp_handle_t&) = delete; + temp_handle_t(temp_handle_t&&) = default; + temp_handle_t& operator=(const handle_t& hdl) + { + // initializing a new temp_handle_t, then swapping it into *this, + // takes care of destroying any previous mHandle + temp_handle_t replacement(hdl); + swap(replacement); + return *this; + } + temp_handle_t& operator=(const temp_handle_t&) = delete; + temp_handle_t& operator=(temp_handle_t&&) = default; + ~temp_handle_t() + { + cancel(); + } + + // temp_handle_t should be usable wherever handle_t is + operator handle_t() const { return mHandle; } + // If we're dealing with a non-const temp_handle_t, pass a reference + // to our handle_t member (e.g. to LLLater::cancel()). + operator handle_t&() { return mHandle; } + + // For those in the know, provide a cancel() method of our own that + // avoids LLLater::instance() lookup when mHandle isn't live. + bool cancel() + { + if (! mHandle) + { + return false; + } + else + { + return LLLater::instance().cancel(mHandle); + } + } + + void swap(temp_handle_t& other) noexcept + { + std::swap(this->mHandle, other.mHandle); + } + + private: + handle_t mHandle; + }; + +private: + bool tick(); + + // NOTE: We don't lock our data members because it doesn't make sense to + // register cross-thread callbacks. If we start wanting to use them on + // threads other than the main thread, it would make more sense to make + // our data members thread_local than to lock them. + + // the heap aka priority queue + queue_t mQueue; + // handles we've returned that haven't yet canceled + std::unordered_map<token_t, queue_t::handle_type> mHandles; + token_t mToken{ 0 }; + // While mQueue is non-empty, register for regular callbacks. + LLCallbackList::temp_handle_t mLive; +}; + +/*-------------------- legacy names in global namespace --------------------*/ // Call a given callable once after specified interval. -void doAfterInterval(nullary_func_t callable, F32 seconds); +inline +LLLater::handle_t doAfterInterval(nullary_func_t callable, F32 seconds) +{ + return LLLater::instance().doAfterInterval(callable, seconds); +} // Call a given callable every specified number of seconds, until it returns true. -void doPeriodically(bool_func_t callable, F32 seconds); - -extern LLCallbackList gIdleCallbacks; +inline +LLLater::handle_t doPeriodically(bool_func_t callable, F32 seconds) +{ + return LLLater::instance().doPeriodically(callable, seconds); +} #endif |