diff options
| author | Nat Goodspeed <nat@lindenlab.com> | 2015-07-10 19:37:19 -0400 | 
|---|---|---|
| committer | Nat Goodspeed <nat@lindenlab.com> | 2015-07-10 19:37:19 -0400 | 
| commit | 9bde72f784a4836c3d19ef4dbcd49d4414640d8e (patch) | |
| tree | 7f5baa9b4bc0c6f69454b603d45d8b0518528bed /indra | |
| parent | 391d9a367b61fe8098ffff0d6a6a54f65f0a3a9e (diff) | |
| parent | 0c915913fd05007bb3fb6cc84be89799b7d85ae5 (diff) | |
Automated merge with file:///Users/nat/linden/maint-4952-v-t-u
Diffstat (limited to 'indra')
| -rwxr-xr-x | indra/llcommon/llcoros.cpp | 109 | ||||
| -rwxr-xr-x | indra/llcommon/llcoros.h | 57 | ||||
| -rwxr-xr-x | indra/llcommon/lleventcoro.cpp | 2 | ||||
| -rwxr-xr-x | indra/llcommon/tests/lleventcoro_test.cpp | 1 | ||||
| -rwxr-xr-x | indra/llcorehttp/CMakeLists.txt | 4 | ||||
| -rwxr-xr-x | indra/newview/llviewerregion.cpp | 2 | 
6 files changed, 110 insertions, 65 deletions
| diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 2d0c419ae0..6cca5e7c60 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& LLCoros::get_self()  { -    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;  }  LLCoros::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);  }  LLCoros::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 = 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 ccf3620f35..05bd87dc3c 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   * @@ -152,23 +159,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 @@ -178,7 +225,7 @@ public:      ~Suspending();  private: -    LLCoros::coro::self* mSuspended; +    LLCoros::CoroData* mSuspended;  };  } // namespace llcoro diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index e400a052a1..a37b87f085 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -298,7 +298,7 @@ WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator  namespace llcoro  { -    boost::dcoroutines::future<LLEventWithID> future(llcoro::get_self()); +    boost::dcoroutines::future<LLEventWithID> future(LLCoros::get_self());      std::string name(listenerNameForCoro());                                      wfeoh(boost::dcoroutines::make_callback(future), 0)));                                      wfeoh(boost::dcoroutines::make_callback(future), 1))); diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index ba606b1f9a..b8d9d8002f 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -59,6 +59,7 @@  //  http://www.boost.org/LICENSE_1_0.txt)  /*****************************************************************************/ +#define BOOST_RESULT_OF_USE_TR1 1  // On some platforms, Boost.Coroutine must #define magic symbols before  // #including platform-API headers. Naturally, that's ineffective unless the  // Boost.Coroutine #include is the *first* #include of the platform header. diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a0b1ea13b1..9631e960c5 100755 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -134,9 +134,13 @@ if (LL_TESTS)        ${BOOST_THREAD_LIBRARY}        ) +  # If http_proxy is in the current environment (e.g. to fetch s3-proxy +  # autobuild packages), suppress it for this integration test: it screws up +  # the tests.    LL_ADD_INTEGRATION_TEST(llcorehttp                            "${llcorehttp_TEST_SOURCE_FILES}"                            "${test_libs}" +                          "-Dhttp_proxy"                            ${PYTHON_EXECUTABLE}                            "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py"                            ) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b256482289..291af28bc3 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2919,7 +2919,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)  	setCapability("Seed", url);      std::string coroname = -        LLCoros::instance().launch("LLEnvironmentRequest::environmentRequestCoro", +        LLCoros::instance().launch("LLViewerRegionImpl::requestBaseCapabilitiesCoro",          boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCoro, mImpl, getHandle()));      LL_INFOS("AppInit", "Capabilities") << "Launching " << coroname << " requesting seed capabilities from " << url << LL_ENDL; | 
