diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 957fe034e156e474f79bb892a244d946acee7ef1..d76401d01b394998f8e77eee0b947b16d6fadaea 100755
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -34,63 +34,47 @@
 // std headers
 // external library headers
 #include <boost/bind.hpp>
-#include <boost/thread/tss.hpp>
 // other Linden headers
 #include "llevents.h"
 #include "llerror.h"
 #include "stringize.h"
 
-namespace {
-
 // do nothing, when we need nothing done
-void no_cleanup(LLCoros::coro::self*) {}
-
-// 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 static 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.
-// We use a boost::thread_specific_ptr because each thread potentially has its
-// own distinct pool of coroutines.
-// This thread_specific_ptr does NOT own the 'self' object! It merely
-// identifies it. For this reason we instantiate it with a no-op cleanup
-// function.
-static boost::thread_specific_ptr<LLCoros::coro::self>
-sCurrentSelf(no_cleanup);
-
-} // anonymous
+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::coro::self& llcoro::get_self()
+LLCoros::coro::self& LLCoros::get_self()
 {
-    LLCoros::coro::self* current_self = sCurrentSelf.get();
-    if (! current_self)
+    CoroData* current = sCurrentCoro.get();
+    if (! current)
     {
         LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL;
     }
-    return *current_self;
+    return *current->mSelf;
 }
 
 llcoro::Suspending::Suspending():
-    mSuspended(sCurrentSelf.get())
+    mSuspended(LLCoros::sCurrentCoro.get())
 {
-    // For the duration of our time away from this coroutine, sCurrentSelf
-    // must NOT refer to this coroutine.
-    sCurrentSelf.reset();
+    // 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, reinstate previous value of sCurrentSelf.
-    sCurrentSelf.reset(mSuspended);
+    // Okay, we're back, update our mPrev
+    mSuspended->mPrev = LLCoros::sCurrentCoro.get();
+    // and reinstate our sCurrentCoro.
+    LLCoros::sCurrentCoro.reset(mSuspended);
 }
 
 LLCoros::LLCoros():
@@ -112,7 +96,7 @@ bool LLCoros::cleanup(const LLSD&)
     {
         // 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;
             // The erase() call will invalidate its passed iterator value --
@@ -170,18 +154,13 @@ bool LLCoros::kill(const std::string& name)
 
 std::string LLCoros::getName() const
 {
-    // Walk the existing coroutines, looking for the current one.
-    void* self_id = llcoro::get_self().get_id();
-    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)
@@ -190,20 +169,20 @@ void LLCoros::setStackSize(S32 stacksize)
     mStackSize = stacksize;
 }
 
-namespace {
-
 // 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 toplevel(LLCoros::coro::self& self, const LLCoros::callable_t& callable)
+void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable)
 {
-    sCurrentSelf.reset(&self);
+    // capture the 'self' param in CoroData
+    data->mSelf = &self;
+    // run the code the caller actually wants in the coroutine
     callable();
-    sCurrentSelf.reset();
+    // 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);
 }
 
-} // anonymous
-
 /*****************************************************************************
 *   MUST BE LAST
 *****************************************************************************/
@@ -215,19 +194,33 @@ void toplevel(LLCoros::coro::self& self, const LLCoros::callable_t& callable)
 #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
 
+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),
+    mSelf(0)
+{
+}
+
 std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
 {
     std::string name(generateDistinctName(prefix));
-    // Wrap the caller's callable in our toplevel() function so we can manage
-    // sCurrentSelf appropriately at startup and shutdown of each coroutine.
-    coro* newCoro = new coro(boost::bind(toplevel, _1, callable), mStackSize);
+    // 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;
 }
 
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index e478600f00ba44102abff771728b4753cdd6612c..56eed8cafed0a3717c7e16735cf1388f81349833 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 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
     /// 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 @@ class Suspending
     ~Suspending();
 
 private:
-    LLCoros::coro::self* mSuspended;
+    LLCoros::CoroData* mSuspended;
 };
 
 } // namespace llcoro
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index ad02e139b896aef993c8992eb427eed5a3690cbc..85af0d9b9fe37ff39524b258d82d8d65cf931038 100755
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -157,7 +157,7 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request
                          const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath)
 {
     // declare the future
-    boost::dcoroutines::future<LLSD> future(llcoro::get_self());
+    boost::dcoroutines::future<LLSD> future(LLCoros::get_self());
     // make a callback that will assign a value to the future, and listen on
     // the specified LLEventPump with that callback
     std::string listenerName(listenerNameForCoro());
@@ -250,7 +250,7 @@ LLEventWithID postAndWait2(const LLSD& event,
                            const LLSD& replyPump1NamePath)
 {
     // declare the future
-    boost::dcoroutines::future<LLEventWithID> future(llcoro::get_self());
+    boost::dcoroutines::future<LLEventWithID> future(LLCoros::get_self());
     // either callback will assign a value to this future; listen on
     // each specified LLEventPump with a callback
     std::string name(listenerNameForCoro());