Skip to content
Snippets Groups Projects
Commit f931f6ef authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

MAINT-5232: Add LLCoros::get_id() to identify the running coroutine.

Change the module-static thread_specific_ptr to a function-static
thread_specific_ptr so it will be initialized on demand -- since LLSingleton
will need to rely on get_id(). Note that since LLCoros isa LLSingleton, we
must take great care to avoid circularity.

Introduce a private helper class LLCoros::Current to obtain and bind that
thread_specific_ptr. Change all existing internal references from the static
thread_specific_ptr to the new Current helper class.
parent c71e6222
No related branches found
No related tags found
No related merge requests found
...@@ -44,16 +44,25 @@ void LLCoros::no_cleanup(CoroData*) {} ...@@ -44,16 +44,25 @@ void LLCoros::no_cleanup(CoroData*) {}
// CoroData for the currently-running coroutine. Use a thread_specific_ptr // CoroData for the currently-running coroutine. Use a thread_specific_ptr
// because each thread potentially has its own distinct pool of coroutines. // because each thread potentially has its own distinct pool of coroutines.
// This thread_specific_ptr does NOT own the CoroData object! That's owned by LLCoros::Current::Current()
// LLCoros::mCoros. It merely identifies it. For this reason we instantiate {
// it with a no-op cleanup function. // Use a function-static instance so this thread_specific_ptr is
boost::thread_specific_ptr<LLCoros::CoroData> // instantiated on demand. Since we happen to know it's consumed by
LLCoros::sCurrentCoro(LLCoros::no_cleanup); // LLSingleton, this is likely to happen before the runtime has finished
// initializing module-static data. For the same reason, we can't package
// this pointer in an LLSingleton.
// This thread_specific_ptr does NOT own the CoroData object! That's owned
// by LLCoros::mCoros. It merely identifies it. For this reason we
// instantiate it with a no-op cleanup function.
static boost::thread_specific_ptr<LLCoros::CoroData> sCurrent(LLCoros::no_cleanup);
mCurrent = &sCurrent;
}
//static //static
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
{ {
CoroData* current = sCurrentCoro.get(); CoroData* current = Current();
if (! current) if (! current)
{ {
LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL; LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL;
...@@ -79,20 +88,23 @@ bool LLCoros::get_consuming() ...@@ -79,20 +88,23 @@ bool LLCoros::get_consuming()
return get_CoroData("get_consuming()").mConsuming; return get_CoroData("get_consuming()").mConsuming;
} }
llcoro::Suspending::Suspending(): llcoro::Suspending::Suspending()
mSuspended(LLCoros::sCurrentCoro.get())
{ {
// Revert mCurrentCoro to the value it had at the moment we last switched LLCoros::Current current;
// Remember currently-running coroutine: we're about to suspend it.
mSuspended = current;
// Revert Current to the value it had at the moment we last switched
// into this coroutine. // into this coroutine.
LLCoros::sCurrentCoro.reset(mSuspended->mPrev); current.reset(mSuspended->mPrev);
} }
llcoro::Suspending::~Suspending() llcoro::Suspending::~Suspending()
{ {
LLCoros::Current current;
// Okay, we're back, update our mPrev // Okay, we're back, update our mPrev
mSuspended->mPrev = LLCoros::sCurrentCoro.get(); mSuspended->mPrev = current;
// and reinstate our sCurrentCoro. // and reinstate our Current.
LLCoros::sCurrentCoro.reset(mSuspended); current.reset(mSuspended);
} }
LLCoros::LLCoros(): LLCoros::LLCoros():
...@@ -212,7 +224,7 @@ bool LLCoros::kill(const std::string& name) ...@@ -212,7 +224,7 @@ bool LLCoros::kill(const std::string& name)
std::string LLCoros::getName() const std::string LLCoros::getName() const
{ {
CoroData* current = sCurrentCoro.get(); CoroData* current = Current();
if (! current) if (! current)
{ {
// not in a coroutine // not in a coroutine
...@@ -228,8 +240,8 @@ void LLCoros::setStackSize(S32 stacksize) ...@@ -228,8 +240,8 @@ void LLCoros::setStackSize(S32 stacksize)
} }
// Top-level wrapper around caller's coroutine callable. This function accepts // Top-level wrapper around caller's coroutine callable. This function accepts
// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf // the coroutine library's implicit coro::self& parameter and saves it, but
// but does not pass it down to the caller's callable. // does not pass it down to the caller's callable.
void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable) void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable)
{ {
// capture the 'self' param in CoroData // capture the 'self' param in CoroData
...@@ -237,8 +249,8 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla ...@@ -237,8 +249,8 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla
// run the code the caller actually wants in the coroutine // run the code the caller actually wants in the coroutine
callable(); callable();
// This cleanup isn't perfectly symmetrical with the way we initially set // This cleanup isn't perfectly symmetrical with the way we initially set
// data->mPrev, but this is our last chance to reset mCurrentCoro. // data->mPrev, but this is our last chance to reset Current.
sCurrentCoro.reset(data->mPrev); Current().reset(data->mPrev);
} }
/***************************************************************************** /*****************************************************************************
...@@ -261,7 +273,7 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name, ...@@ -261,7 +273,7 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
mPrev(prev), mPrev(prev),
mName(name), mName(name),
// Wrap the caller's callable in our toplevel() function so we can manage // Wrap the caller's callable in our toplevel() function so we can manage
// sCurrentCoro appropriately at startup and shutdown of each coroutine. // Current appropriately at startup and shutdown of each coroutine.
mCoro(boost::bind(toplevel, _1, this, callable), stacksize), mCoro(boost::bind(toplevel, _1, this, callable), stacksize),
// don't consume events unless specifically directed // don't consume events unless specifically directed
mConsuming(false), mConsuming(false),
...@@ -272,13 +284,13 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name, ...@@ -272,13 +284,13 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
{ {
std::string name(generateDistinctName(prefix)); std::string name(generateDistinctName(prefix));
// pass the current value of sCurrentCoro as previous context Current current;
CoroData* newCoro = new CoroData(sCurrentCoro.get(), name, // pass the current value of Current as previous context
callable, mStackSize); CoroData* newCoro = new CoroData(current, name, callable, mStackSize);
// Store it in our pointer map // Store it in our pointer map
mCoros.insert(name, newCoro); mCoros.insert(name, newCoro);
// also set it as current // also set it as current
sCurrentCoro.reset(newCoro); current.reset(newCoro);
/* Run the coroutine until its first wait, then return here */ /* Run the coroutine until its first wait, then return here */
(newCoro->mCoro)(std::nothrow); (newCoro->mCoro)(std::nothrow);
return name; return name;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <boost/ptr_container/ptr_map.hpp> #include <boost/ptr_container/ptr_map.hpp>
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/thread/tss.hpp> #include <boost/thread/tss.hpp>
#include <boost/noncopyable.hpp>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
...@@ -145,6 +146,10 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> ...@@ -145,6 +146,10 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
*/ */
std::string getName() const; std::string getName() const;
/// Get an opaque, distinct token for the running coroutine (or main).
typedef void* id;
static id get_id() { return Current(); }
/// for delayed initialization /// for delayed initialization
void setStackSize(S32 stacksize); void setStackSize(S32 stacksize);
...@@ -222,8 +227,21 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> ...@@ -222,8 +227,21 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
typedef boost::ptr_map<std::string, CoroData> CoroMap; typedef boost::ptr_map<std::string, CoroData> CoroMap;
CoroMap mCoros; CoroMap mCoros;
// identify the current coroutine's CoroData // Identify the current coroutine's CoroData. Use a little helper class so
static boost::thread_specific_ptr<LLCoros::CoroData> sCurrentCoro; // a caller can either use a temporary instance, or instantiate a named
// variable and access it multiple times.
class Current
{
public:
Current();
operator LLCoros::CoroData*() { return get(); }
LLCoros::CoroData* get() { return mCurrent->get(); }
void reset(LLCoros::CoroData* ptr) { mCurrent->reset(ptr); }
private:
boost::thread_specific_ptr<LLCoros::CoroData>* mCurrent;
};
}; };
namespace llcoro namespace llcoro
...@@ -231,7 +249,7 @@ namespace llcoro ...@@ -231,7 +249,7 @@ namespace llcoro
/// Instantiate one of these in a block surrounding any leaf point when /// Instantiate one of these in a block surrounding any leaf point when
/// control literally switches away from this coroutine. /// control literally switches away from this coroutine.
class Suspending class Suspending: boost::noncopyable
{ {
public: public:
Suspending(); Suspending();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment