summaryrefslogtreecommitdiff
path: root/indra/llcommon/llcoros.cpp
diff options
context:
space:
mode:
authorBrad Payne (Vir Linden) <vir@lindenlab.com>2016-04-01 08:35:38 -0400
committerBrad Payne (Vir Linden) <vir@lindenlab.com>2016-04-01 08:35:38 -0400
commitcaf4f494378558df14738f2e11b20f0c03674764 (patch)
treec8994793b992960a76a69516bc4891228a684afd /indra/llcommon/llcoros.cpp
parent9c5d0d6152caff914a6f0159ba4f2382915d4055 (diff)
parent18928ea6c6f2830a0d45ec412c915eceff1b76b0 (diff)
merge
Diffstat (limited to 'indra/llcommon/llcoros.cpp')
-rwxr-xr-xindra/llcommon/llcoros.cpp159
1 files changed, 142 insertions, 17 deletions
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index baaddcaed1..d16bf0160b 100755
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -39,6 +39,62 @@
#include "llerror.h"
#include "stringize.h"
+// do nothing, when we need nothing done
+void LLCoros::no_cleanup(CoroData*) {}
+
+// CoroData for the currently-running coroutine. Use a thread_specific_ptr
+// 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::mCoros. It merely identifies it. For this reason we instantiate
+// it with a no-op cleanup function.
+boost::thread_specific_ptr<LLCoros::CoroData>
+LLCoros::sCurrentCoro(LLCoros::no_cleanup);
+
+//static
+LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
+{
+ CoroData* current = sCurrentCoro.get();
+ if (! current)
+ {
+ LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL;
+ }
+ return *current;
+}
+
+//static
+LLCoros::coro::self& LLCoros::get_self()
+{
+ return *get_CoroData("get_self()").mSelf;
+}
+
+//static
+void LLCoros::set_consuming(bool consuming)
+{
+ get_CoroData("set_consuming()").mConsuming = consuming;
+}
+
+//static
+bool LLCoros::get_consuming()
+{
+ return get_CoroData("get_consuming()").mConsuming;
+}
+
+llcoro::Suspending::Suspending():
+ mSuspended(LLCoros::sCurrentCoro.get())
+{
+ // Revert mCurrentCoro to the value it had at the moment we last switched
+ // into this coroutine.
+ LLCoros::sCurrentCoro.reset(mSuspended->mPrev);
+}
+
+llcoro::Suspending::~Suspending()
+{
+ // Okay, we're back, update our mPrev
+ mSuspended->mPrev = LLCoros::sCurrentCoro.get();
+ // and reinstate our sCurrentCoro.
+ LLCoros::sCurrentCoro.reset(mSuspended);
+}
+
LLCoros::LLCoros():
// MAINT-2724: default coroutine stack size too small on Windows.
// Previously we used
@@ -53,14 +109,33 @@ LLCoros::LLCoros():
bool LLCoros::cleanup(const LLSD&)
{
+ static std::string previousName;
+ static int previousCount = 0;
// Walk the mCoros map, checking and removing completed coroutines.
for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
{
// Has this coroutine exited (normal return, exception, exit() call)
// since last tick?
- if (mi->second->exited())
+ if (mi->second->mCoro.exited())
{
- LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
+ if (previousName != mi->first)
+ {
+ previousName = mi->first;
+ previousCount = 1;
+ }
+ else
+ {
+ ++previousCount;
+ }
+
+ if ((previousCount < 5) || !(previousCount % 50))
+ {
+ if (previousCount < 5)
+ LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
+ else
+ LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL;
+
+ }
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
@@ -78,6 +153,9 @@ bool LLCoros::cleanup(const LLSD&)
std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
+ static std::string previousName;
+ static int previousCount = 0;
+
// Allowing empty name would make getName()'s not-found return ambiguous.
if (prefix.empty())
{
@@ -94,7 +172,25 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
if (mCoros.find(name) == mCoros.end())
{
- LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
+ if (previousName != name)
+ {
+ previousName = name;
+ previousCount = 1;
+ }
+ else
+ {
+ ++previousCount;
+ }
+
+ if ((previousCount < 5) || !(previousCount % 50))
+ {
+ if (previousCount < 5)
+ LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
+ else
+ LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL;
+
+ }
+
return name;
}
}
@@ -114,20 +210,15 @@ bool LLCoros::kill(const std::string& name)
return true;
}
-std::string LLCoros::getNameByID(const void* self_id) const
+std::string LLCoros::getName() const
{
- // Walk the existing coroutines, looking for one from which the 'self_id'
- // passed to us comes.
- for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
+ CoroData* current = sCurrentCoro.get();
+ if (! current)
{
- namespace coro_private = boost::dcoroutines::detail;
- if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
- == self_id)
- {
- return mi->first;
- }
+ // not in a coroutine
+ return "";
}
- return "";
+ return current->mName;
}
void LLCoros::setStackSize(S32 stacksize)
@@ -136,10 +227,24 @@ void LLCoros::setStackSize(S32 stacksize)
mStackSize = stacksize;
}
+// Top-level wrapper around caller's coroutine callable. This function accepts
+// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf
+// but does not pass it down to the caller's callable.
+void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable)
+{
+ // capture the 'self' param in CoroData
+ data->mSelf = &self;
+ // run the code the caller actually wants in the coroutine
+ callable();
+ // This cleanup isn't perfectly symmetrical with the way we initially set
+ // data->mPrev, but this is our last chance to reset mCurrentCoro.
+ sCurrentCoro.reset(data->mPrev);
+}
+
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
-// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
+// Turn off MSVC optimizations for just LLCoros::launch() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
@@ -147,15 +252,35 @@ void LLCoros::setStackSize(S32 stacksize)
#if LL_MSVC
// work around broken optimizations
#pragma warning(disable: 4748)
+#pragma warning(disable: 4355) // 'this' used in initializer list: yes, intentionally
#pragma optimize("", off)
#endif // LL_MSVC
-std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
+LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
+ const callable_t& callable, S32 stacksize):
+ mPrev(prev),
+ mName(name),
+ // Wrap the caller's callable in our toplevel() function so we can manage
+ // sCurrentCoro appropriately at startup and shutdown of each coroutine.
+ mCoro(boost::bind(toplevel, _1, this, callable), stacksize),
+ // don't consume events unless specifically directed
+ mConsuming(false),
+ mSelf(0)
+{
+}
+
+std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
{
std::string name(generateDistinctName(prefix));
+ // pass the current value of sCurrentCoro as previous context
+ CoroData* newCoro = new CoroData(sCurrentCoro.get(), name,
+ callable, mStackSize);
+ // Store it in our pointer map
mCoros.insert(name, newCoro);
+ // also set it as current
+ sCurrentCoro.reset(newCoro);
/* Run the coroutine until its first wait, then return here */
- (*newCoro)(std::nothrow);
+ (newCoro->mCoro)(std::nothrow);
return name;
}