diff options
Diffstat (limited to 'indra/llcommon/llcoros.h')
-rwxr-xr-x | indra/llcommon/llcoros.h | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index e478600f00..56eed8cafe 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -33,9 +33,16 @@ #include "llsingleton.h" #include <boost/ptr_container/ptr_map.hpp> #include <boost/function.hpp> +#include <boost/thread/tss.hpp> #include <string> #include <stdexcept> +// forward-declare helper class +namespace llcoro +{ +class Suspending; +} + /** * Registry of named Boost.Coroutine instances * @@ -140,23 +147,63 @@ public: /// for delayed initialization void setStackSize(S32 stacksize); + /// get the current coro::self& for those who really really care + static coro::self& get_self(); + private: LLCoros(); friend class LLSingleton<LLCoros>; + friend class llcoro::Suspending; std::string generateDistinctName(const std::string& prefix) const; bool cleanup(const LLSD&); + struct CoroData; + static void no_cleanup(CoroData*); + static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); S32 mStackSize; - typedef boost::ptr_map<std::string, coro> CoroMap; + + // coroutine-local storage, as it were: one per coro we track + struct CoroData + { + CoroData(CoroData* prev, const std::string& name, + const callable_t& callable, S32 stacksize); + + // The boost::dcoroutines library supports asymmetric coroutines. Every + // time we context switch out of a coroutine, we pass control to the + // previously-active one (or to the non-coroutine stack owned by the + // thread). So our management of the "current" coroutine must be able to + // restore the previous value when we're about to switch away. + CoroData* mPrev; + // tweaked name of the current coroutine + const std::string mName; + // the actual coroutine instance + LLCoros::coro mCoro; + // When the dcoroutine library calls a top-level callable, it implicitly + // passes coro::self& as the first parameter. All our consumer code used + // to explicitly pass coro::self& down through all levels of call stack, + // because at the leaf level we need it for context-switching. But since + // coroutines are based on cooperative switching, we can cause the + // top-level entry point to stash a pointer to the currently-running + // coroutine, and manage it appropriately as we switch out and back in. + // That eliminates the need to pass it as an explicit parameter down + // through every level, which is unfortunately viral in nature. Finding it + // implicitly rather than explicitly allows minor maintenance in which a + // leaf-level function adds a new async I/O call that suspends the calling + // coroutine, WITHOUT having to propagate coro::self& through every + // function signature down to that point -- and of course through every + // other caller of every such function. + LLCoros::coro::self* mSelf; + }; + typedef boost::ptr_map<std::string, CoroData> CoroMap; CoroMap mCoros; + + // identify the current coroutine's CoroData + static boost::thread_specific_ptr<LLCoros::CoroData> sCurrentCoro; }; namespace llcoro { -/// get the current coro::self& for those who really really care -LLCoros::coro::self& get_self(); - /// Instantiate one of these in a block surrounding any leaf point when /// control literally switches away from this coroutine. class Suspending @@ -166,7 +213,7 @@ public: ~Suspending(); private: - LLCoros::coro::self* mSuspended; + LLCoros::CoroData* mSuspended; }; } // namespace llcoro |