From 3b923962f8e0a065052f40d029bce42a6fe37e68 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 23 Mar 2015 12:23:35 -0700 Subject: Reogranized some headers for GCC added to the linden_common.h for shared_ptr --- indra/llcommon/linden_common.h | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 5cfcdab41c..e5a913a6a9 100755 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -51,6 +51,7 @@ #include #include #include +#include // Linden only libs in alpha-order other than stdtypes.h // *NOTE: Please keep includes here to a minimum, see above. -- cgit v1.2.3 From e28a5b6eadd4c38498665b44f1c7e197615139f2 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 9 Apr 2015 16:46:41 -0700 Subject: Added LL_WARNS_IF to llerror.h If the coro is given something other than a map from the http then move the return into a body section. Changed windlight to use a coroutine and the new LLCore::Http libarary. Extra comments into Event Polling. --- indra/llcommon/llerror.h | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 63040e1772..b1b5e9be7d 100755 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -354,6 +354,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__) #define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__) // alternative to llassert_always that prints explanatory message +#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(##__VA_ARGS__) << "(" #exp ")" #define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")" // Only print the log message once (good for warnings or infos that would otherwise -- cgit v1.2.3 From c437a9c4ec865c38366c8057010d24311888ecb1 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 20 May 2015 17:37:27 -0700 Subject: Webprofile converted to coroutine. Added JSON->LLSD converter Added corohandler for JSON data --- indra/llcommon/CMakeLists.txt | 5 +++ indra/llcommon/llsdjson.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++ indra/llcommon/llsdjson.h | 59 ++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 indra/llcommon/llsdjson.cpp create mode 100644 indra/llcommon/llsdjson.h (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1459b9ada2..9086691375 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -8,6 +8,7 @@ include(LLCommon) include(Linking) include(Boost) include(LLSharedLibs) +include(JsonCpp) include(GoogleBreakpad) include(GooglePerfTools) include(Copy3rdPartyLibs) @@ -17,6 +18,7 @@ include(URIPARSER) include_directories( ${EXPAT_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ${URIPARSER_INCLUDE_DIRS} @@ -85,6 +87,7 @@ set(llcommon_SOURCE_FILES llrefcount.cpp llrun.cpp llsd.cpp + llsdjson.cpp llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp @@ -193,6 +196,7 @@ set(llcommon_HEADER_FILES llrefcount.h llsafehandle.h llsd.h + llsdjson.h llsdparam.h llsdserialize.h llsdserialize_xml.h @@ -260,6 +264,7 @@ target_link_libraries( ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} + ${JSONCPP_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp new file mode 100644 index 0000000000..2afdba388a --- /dev/null +++ b/indra/llcommon/llsdjson.cpp @@ -0,0 +1,78 @@ +/** + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Must turn on conditional declarations in header file so definitions end up +// with proper linkage. +#define LLSD_DEBUG_INFO +#include "linden_common.h" + +#include "llsdjson.h" + +#include "llerror.h" +#include "../llmath/llmath.h" + +//========================================================================= +LLSD LlsdFromJson(const Json::Value &val) +{ + LLSD result; + + switch (val.type()) + { + default: + case Json::nullValue: + break; + case Json::intValue: + result = LLSD(static_cast(val.asInt())); + break; + case Json::uintValue: + result = LLSD(static_cast(val.asUInt())); + break; + case Json::realValue: + result = LLSD(static_cast(val.asDouble())); + break; + case Json::stringValue: + result = LLSD(static_cast(val.asString())); + break; + case Json::booleanValue: + result = LLSD(static_cast(val.asBool())); + break; + case Json::arrayValue: + result = LLSD::emptyArray(); + for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it) + { + result.append(LlsdFromJson((*it))); + } + break; + case Json::objectValue: + result = LLSD::emptyMap(); + for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it) + { + result[it.memberName()] = LlsdFromJson((*it)); + } + break; + } + return result; +} diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h new file mode 100644 index 0000000000..cdf9fed500 --- /dev/null +++ b/indra/llcommon/llsdjson.h @@ -0,0 +1,59 @@ +/** + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSDJSON_H +#define LL_LLSDJSON_H + +#include +#include +#include + +#include "stdtypes.h" + +#include "llsd.h" +#include "value.h" + +/// Convert a parsed JSON structure into LLSD maintaining member names and +/// array indexes. +/// JSON/JavaScript types are converted as follows: +/// +/// JSON Type | LLSD Type +/// --------------+-------------- +/// null | undefined +/// integer | LLSD::Integer +/// unsigned | LLSD::Integer +/// real/numeric | LLSD::Real +/// string | LLSD::String +/// boolean | LLSD::Boolean +/// array | LLSD::Array +/// object | LLSD::Map +/// +/// For maps and arrays child entries will be converted and added to the structure. +/// Order is preserved for an array but not for objects. +LLSD LlsdFromJson(const Json::Value &val); + + +#endif // LL_LLSDJSON_H -- cgit v1.2.3 From 4a4470af3210153e0909eb75d51de461d14a3128 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 17 Jun 2015 13:53:28 -0700 Subject: Coding policy fixes --- indra/llcommon/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 9086691375..9757679ce1 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -87,7 +87,7 @@ set(llcommon_SOURCE_FILES llrefcount.cpp llrun.cpp llsd.cpp - llsdjson.cpp + llsdjson.cpp llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp @@ -196,7 +196,7 @@ set(llcommon_HEADER_FILES llrefcount.h llsafehandle.h llsd.h - llsdjson.h + llsdjson.h llsdparam.h llsdserialize.h llsdserialize_xml.h -- cgit v1.2.3 From 8bd39583fe19eb8f372f59f9aaee6d7c16a6f4e1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 29 Jun 2015 15:32:47 -0400 Subject: MAINT-4952: Fix misplaced comments on intrusive_ptr_etc() overloads. --- indra/llcommon/llrefcount.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 3836a9b5fb..1107973569 100755 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -143,15 +143,10 @@ private: LLAtomic32< S32 > mRef; }; -/** - * intrusive pointer support - * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type - */ /** * intrusive pointer support for LLThreadSafeRefCount * this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type */ - inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p) { p->ref(); @@ -162,6 +157,10 @@ inline void intrusive_ptr_release(LLThreadSafeRefCount* p) p->unref(); } +/** + * intrusive pointer support + * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type + */ inline void intrusive_ptr_add_ref(LLRefCount* p) { p->ref(); -- cgit v1.2.3 From ddb63e7fb70eefea200fbb385efe86e50e5c1e12 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 30 Jun 2015 17:11:10 -0700 Subject: Initial checkin for uploading via coroutine. --- indra/llcommon/llstring.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 0177f48bf5..393f6d7a8c 100755 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -1394,6 +1394,7 @@ BOOL LLStringUtilBase::containsNonprintable(const string_type& string) return rv; } +// *TODO: reimplement in terms of algorithm //static template void LLStringUtilBase::stripNonprintable(string_type& string) @@ -1427,6 +1428,7 @@ void LLStringUtilBase::stripNonprintable(string_type& string) delete []c_string; } +// *TODO: reimplement in terms of algorithm template std::basic_string LLStringUtilBase::quote(const string_type& str, const string_type& triggers, -- cgit v1.2.3 From 41dff76327feffed798c8b24673885bc9ad61005 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 1 Jul 2015 14:04:23 -0400 Subject: MAINT-5351: Remove Boost Preprocessor headers from llcoros.h. These were used in a former iteration (when we explicitly forwarded parameters), but are no longer needed. --- indra/llcommon/llcoros.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 01ee11da1a..f07539ac54 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -33,9 +33,6 @@ #include "llsingleton.h" #include #include -#include -#include -#include #include /** -- cgit v1.2.3 From b262ded7e0cf21314524bf702b0e4fe28a3c3060 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 1 Jul 2015 18:33:29 -0400 Subject: MAINT-5351: Remove 'self' parameter from coroutine functions. lleventcoro_test.cpp runs clean (as modified for new API), and all the rest builds clean, but the resulting viewer is as yet untested. --- indra/llcommon/llcoros.cpp | 86 +++++++++++++-- indra/llcommon/llcoros.h | 64 +++++------ indra/llcommon/lleventcoro.cpp | 113 ++++++++++++++++--- indra/llcommon/lleventcoro.h | 176 ++++++------------------------ indra/llcommon/tests/lleventcoro_test.cpp | 174 +++++++++++------------------ 5 files changed, 308 insertions(+), 305 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index baaddcaed1..2d0c419ae0 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -34,11 +34,65 @@ // std headers // external library headers #include +#include // 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 +sCurrentSelf(no_cleanup); + +} // anonymous + +//static +LLCoros::coro::self& LLCoros::get_self() +{ + coro::self* current_self = sCurrentSelf.get(); + if (! current_self) + { + LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; + } + return *current_self; +} + +LLCoros::Suspending::Suspending(): + mSuspended(sCurrentSelf.get()) +{ + // For the duration of our time away from this coroutine, sCurrentSelf + // must NOT refer to this coroutine. + sCurrentSelf.reset(); +} + +LLCoros::Suspending::~Suspending() +{ + // Okay, we're back, reinstate previous value of sCurrentSelf. + sCurrentSelf.reset(mSuspended); +} + LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used @@ -60,7 +114,7 @@ bool LLCoros::cleanup(const LLSD&) // since last tick? if (mi->second->exited()) { - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << 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. @@ -94,7 +148,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const { if (mCoros.find(name) == mCoros.end()) { - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; return name; } } @@ -114,10 +168,10 @@ 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. + // 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) { namespace coro_private = boost::dcoroutines::detail; @@ -136,10 +190,24 @@ 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) +{ + sCurrentSelf.reset(&self); + callable(); + sCurrentSelf.reset(); +} + +} // anonymous + /***************************************************************************** * 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. @@ -150,9 +218,13 @@ void LLCoros::setStackSize(S32 stacksize) #pragma optimize("", off) #endif // LL_MSVC -std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) +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); + // Store it in our pointer map mCoros.insert(name, newCoro); /* Run the coroutine until its first wait, then return here */ (*newCoro)(std::nothrow); diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index f07539ac54..3f58a17aa9 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -32,6 +32,7 @@ #include #include "llsingleton.h" #include +#include #include #include @@ -77,8 +78,8 @@ class LL_COMMON_API LLCoros: public LLSingleton public: /// Canonical boost::dcoroutines::coroutine signature we use typedef boost::dcoroutines::coroutine coro; - /// Canonical 'self' type - typedef coro::self self; + /// Canonical callable type + typedef boost::function callable_t; /** * Create and start running a new coroutine with specified name. The name @@ -91,39 +92,33 @@ public: * { * public: * ... - * // Do NOT NOT NOT accept reference params other than 'self'! + * // Do NOT NOT NOT accept reference params! * // Pass by value only! - * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD); + * void myCoroutineMethod(std::string, LLSD); * ... * }; * ... * std::string name = LLCoros::instance().launch( - * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1, + * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, * "somestring", LLSD(17)); * @endcode * - * Your function/method must accept LLCoros::self& as its first parameter. - * It can accept any other parameters you want -- but ONLY BY VALUE! - * Other reference parameters are a BAD IDEA! You Have Been Warned. See + * Your function/method can accept any parameters you want -- but ONLY BY + * VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See * DEV-32777 comments for an explanation. * - * Pass a callable that accepts the single LLCoros::self& parameter. It - * may work to pass a free function whose only parameter is 'self'; for - * all other cases use boost::bind(). Of course, for a non-static class - * method, the first parameter must be the class instance. Use the - * placeholder _1 for the 'self' parameter. Any other parameters should be - * passed via the bind() expression. + * Pass a nullary callable. It works to directly pass a nullary free + * function (or static method); for all other cases use boost::bind(). Of + * course, for a non-static class method, the first parameter must be the + * class instance. Any other parameters should be passed via the bind() + * expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers * it with the tweaked name and runs it until its first wait. At that * point it returns the tweaked name. */ - template - std::string launch(const std::string& prefix, const CALLABLE& callable) - { - return launchImpl(prefix, new coro(callable, mStackSize)); - } + std::string launch(const std::string& prefix, const callable_t& callable); /** * Abort a running coroutine by name. Normally, when a coroutine either @@ -135,27 +130,34 @@ public: bool kill(const std::string& name); /** - * From within a coroutine, pass its @c self object to look up the - * (tweaked) name string by which this coroutine is registered. Returns - * the empty string if not found (e.g. if the coroutine was launched by - * hand rather than using LLCoros::launch()). + * From within a coroutine, look up the (tweaked) name string by which + * this coroutine is registered. Returns the empty string if not found + * (e.g. if the coroutine was launched by hand rather than using + * LLCoros::launch()). */ - template - std::string getName(const COROUTINE_SELF& self) const + std::string getName() const; + + /// get the current coro::self& for those who really really care + static 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 { - return getNameByID(self.get_id()); - } + public: + Suspending(); + ~Suspending(); - /// getName() by self.get_id() - std::string getNameByID(const void* self_id) const; + private: + coro::self* mSuspended; + }; /// for delayed initialization void setStackSize(S32 stacksize); private: - friend class LLSingleton; LLCoros(); - std::string launchImpl(const std::string& prefix, coro* newCoro); + friend class LLSingleton; std::string generateDistinctName(const std::string& prefix) const; bool cleanup(const LLSD&); diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 81cc33fbba..be93e9c83b 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,33 +34,24 @@ #include // std headers // external library headers +#include +#include // other Linden headers #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) +std::string LLEventDetail::listenerNameForCoro() { - // First, if this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getNameByID(self_id)); + // If this coroutine was launched by LLCoros::launch(), find that name. + std::string name(LLCoros::instance().getName()); if (! name.empty()) { return name; } - // Apparently this coroutine wasn't launched by LLCoros::launch(). Check - // whether we have a memo for this self_id. - typedef std::map MapType; - static MapType memo; - MapType::const_iterator found = memo.find(self_id); - if (found != memo.end()) - { - // this coroutine instance has called us before, reuse same name - return found->second; - } // this is the first time we've been called for this coroutine instance name = LLEventPump::inventName("coro"); - memo[self_id] = name; - LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '" + LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '" << name << "'" << LL_ENDL; return name; } @@ -118,6 +109,98 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +{ + // declare the future + boost::dcoroutines::future 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(LLEventDetail::listenerNameForCoro()); + LLTempBoundListener connection( + replyPump.getPump().listen(listenerName, + voidlistener(boost::dcoroutines::make_callback(future)))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If replyPumpNamePath is non-empty, store the replyPump name in the + // request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " posting to " << requestPump.getPump().getName() + << LL_ENDL; + + // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. + // << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getPump().getName() + << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLSD value; + { + // instantiate Suspending to manage the "current" coroutine + LLCoros::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; +} + +LLEventWithID postAndWait2(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump0, + const LLEventPumpOrPumpName& replyPump1, + const LLSD& replyPump0NamePath, + const LLSD& replyPump1NamePath) +{ + // declare the future + boost::dcoroutines::future future(LLCoros::get_self()); + // either callback will assign a value to this future; listen on + // each specified LLEventPump with a callback + std::string name(LLEventDetail::listenerNameForCoro()); + LLTempBoundListener connection0( + replyPump0.getPump().listen(name + "a", + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen(name + "b", + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If either replyPumpNamePath is non-empty, store the corresponding + // replyPump name in the request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLEventWithID value; + { + // instantiate Suspending to manage "current" coroutine + LLCoros::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; + // returning should disconnect both connections + return value; +} + LLSD errorException(const LLEventWithID& result, const std::string& desc) { // If the result arrived on the error pump (pump 1), instead of diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index abbeeaa373..daf9360a2e 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,8 +29,6 @@ #if ! defined(LL_LLEVENTCORO_H) #define LL_LLEVENTCORO_H -#include -#include #include #include #include @@ -102,9 +100,6 @@ LLVoidListener voidlistener(const LISTENER& listener) namespace LLEventDetail { - /// Implementation for listenerNameForCoro(), see below - LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id); - /** * waitForEventOn() permits a coroutine to temporarily listen on an * LLEventPump any number of times. We don't really want to have to ask @@ -116,21 +111,9 @@ namespace LLEventDetail * call waitForEventOn() any number of times, we don't really want to * consume an arbitrary number of generated inventName()s: that namespace, * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance (each different 'self' object). We - * can't know the type of 'self', because it depends on the coroutine - * body's signature. So we cast its address to void*, looking for distinct - * pointer values. Yes, that means that an early coroutine could cache a - * value here, then be destroyed, only to be supplanted by a later - * coroutine (of the same or different type), and we'll end up - * "recognizing" the second one and reusing the listener name -- but - * that's okay, since it won't collide with any listener name used by the - * earlier coroutine since that earlier coroutine no longer exists. + * each distinct coroutine instance. */ - template - std::string listenerNameForCoro(COROUTINE_SELF& self) - { - return listenerNameForCoroImpl(self.get_id()); - } + std::string listenerNameForCoro(); /** * Implement behavior described for postAndWait()'s @a replyPumpNamePath @@ -159,7 +142,7 @@ namespace LLEventDetail * convenience: the difference between this function and the sequence * @code * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(self, replyPump); + * LLSD reply = waitForEventOn(replyPump); * @endcode * is that the sequence above fails if the reply is posted immediately on * @a replyPump, that is, before requestPump.post() returns. In the @@ -201,51 +184,16 @@ namespace LLEventDetail * @a replyPumpNamePath specifies the entry in the lowest-level structure in * @a event into which to store replyPump.getName(). */ -template -LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()) -{ - // declare the future - boost::dcoroutines::future future(self); - // make a callback that will assign a value to the future, and listen on - // the specified LLEventPump with that callback - std::string listenerName(LLEventDetail::listenerNameForCoro(self)); - LLTempBoundListener connection( - replyPump.getPump().listen(listenerName, - voidlistener(boost::dcoroutines::make_callback(future)))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If replyPumpNamePath is non-empty, store the replyPump name in the - // request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " posting to " << requestPump.getPump().getName() - << LL_ENDL; - - // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. - // << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " about to wait on LLEventPump " << replyPump.getPump().getName() - << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLSD value(*future); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; - // returning should disconnect the connection - return value; -} +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()); /// Wait for the next event on the specified LLEventPump. Pass either the /// LLEventPump& or its string name. -template -LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) +inline +LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) { // This is now a convenience wrapper for postAndWait(). - return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); + return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); } /// return type for two-pump variant of waitForEventOn() @@ -313,7 +261,7 @@ namespace LLEventDetail * I'd have preferred to overload the name postAndWait() for both signatures. * But consider the following ambiguous call: * @code - * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); + * postAndWait(LLSD(), requestPump, replyPump, "someString"); * @endcode * "someString" could be converted to either LLSD (@a replyPumpNamePath for * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump @@ -322,69 +270,29 @@ namespace LLEventDetail * It seems less burdensome to write postAndWait2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -template -LLEventWithID postAndWait2(SELF& self, const LLSD& event, +LLEventWithID postAndWait2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) -{ - // declare the future - boost::dcoroutines::future future(self); - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro(self)); - LLTempBoundListener connection0( - replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value(*future); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections - return value; -} + const LLSD& replyPump1NamePath=LLSD()); /** * Wait for the next event on either of two specified LLEventPumps. */ -template +inline LLEventWithID -waitForEventOn(SELF& self, - const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { // This is now a convenience wrapper for postAndWait2(). - return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** * Helper for the two-pump variant of waitForEventOn(), e.g.: * * @code - * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), + * LLSD reply = errorException(waitForEventOn(replyPump, errorPump), * "error response from login.cgi"); * @endcode * @@ -454,26 +362,16 @@ public: /** * Wait for an event on this LLEventPump. - * - * @note - * The other major usage pattern we considered was to bind @c self at - * LLCoroEventPump construction time, which would avoid passing the - * parameter to each wait() call. But if we were going to bind @c self as - * a class member, we'd need to specify a class template parameter - * indicating its type. The big advantage of passing it to the wait() call - * is that the type can be implicit. */ - template - LLSD wait(SELF& self) + LLSD wait() { - return waitForEventOn(self, mPump); + return ::waitForEventOn(mPump); } - template - LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); + return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -509,55 +407,49 @@ public: /// request pump 1 LLEventPump& getPump1() { return mPump1; } - /// waitForEventOn(self, either of our two LLEventPumps) - template - LLEventWithID wait(SELF& self) + /// waitForEventOn(either of our two LLEventPumps) + LLEventWithID wait() { - return waitForEventOn(self, mPump0, mPump1); + return waitForEventOn(mPump0, mPump1); } - /// errorException(wait(self)) - template - LLSD waitWithException(SELF& self) + /// errorException(wait()) + LLSD waitWithException() { - return errorException(wait(self), std::string("Error event on ") + getName1()); + return errorException(wait(), std::string("Error event on ") + getName1()); } - /// errorLog(wait(self)) - template - LLSD waitWithLog(SELF& self) + /// errorLog(wait()) + LLSD waitWithLog() { - return errorLog(wait(self), std::string("Error event on ") + getName1()); + return errorLog(wait(), std::string("Error event on ") + getName1()); } - template - LLEventWithID postAndWait(SELF& self, const LLSD& event, + LLEventWithID postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(self, event, requestPump, mPump0, mPump1, + return postAndWait2(event, requestPump, mPump0, mPump1, replyPump0NamePath, replyPump1NamePath); } - template - LLSD postAndWaitWithException(SELF& self, const LLSD& event, + LLSD postAndWaitWithException(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(self, event, requestPump, + return errorException(postAndWait(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } - template - LLSD postAndWaitWithLog(SELF& self, const LLSD& event, + LLSD postAndWaitWithLog(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(self, event, requestPump, + return errorLog(postAndWait(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 2096807e53..31e9856477 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -82,6 +82,7 @@ #include "llevents.h" #include "tests/wrapllerrs.h" #include "stringize.h" +#include "llcoros.h" #include "lleventcoro.h" #include "../test/debug.h" @@ -121,9 +122,6 @@ typedef coroutine match_coroutine_type; /***************************************************************************** * Test helpers *****************************************************************************/ -// I suspect this will be typical of coroutines used in Linden software -typedef boost::dcoroutines::coroutine coroutine_type; - /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that /// distinguishes postAndWait() from calling post(), then calling @@ -166,7 +164,7 @@ namespace tut { // Define coroutine bodies as methods here so they can use ensure*() - void explicit_wait(coroutine_type::self& self) + void explicit_wait(boost::dcoroutines::coroutine::self& self) { BEGIN { @@ -187,20 +185,20 @@ namespace tut END } - void waitForEventOn1(coroutine_type::self& self) + void waitForEventOn1() { BEGIN { - result = waitForEventOn(self, "source"); + result = waitForEventOn("source"); } END } - void waitForEventOn2(coroutine_type::self& self) + void waitForEventOn2() { BEGIN { - LLEventWithID pair = waitForEventOn(self, "reply", "error"); + LLEventWithID pair = waitForEventOn("reply", "error"); result = pair.first; which = pair.second; debug(STRINGIZE("result = " << result << ", which = " << which)); @@ -208,12 +206,11 @@ namespace tut END } - void postAndWait1(coroutine_type::self& self) + void postAndWait1() { BEGIN { - result = postAndWait(self, - LLSDMap("value", 17), // request event + result = postAndWait(LLSDMap("value", 17), // request event immediateAPI.getPump(), // requestPump "reply1", // replyPump "reply"); // request["reply"] = name @@ -221,12 +218,11 @@ namespace tut END } - void postAndWait2(coroutine_type::self& self) + void postAndWait2() { BEGIN { - LLEventWithID pair = ::postAndWait2(self, - LLSDMap("value", 18), + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), immediateAPI.getPump(), "reply2", "error2", @@ -239,12 +235,11 @@ namespace tut END } - void postAndWait2_1(coroutine_type::self& self) + void postAndWait2_1() { BEGIN { - LLEventWithID pair = ::postAndWait2(self, - LLSDMap("value", 18)("fail", LLSD()), + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), immediateAPI.getPump(), "reply2", "error2", @@ -257,55 +252,55 @@ namespace tut END } - void coroPump(coroutine_type::self& self) + void coroPump() { BEGIN { LLCoroEventPump waiter; replyName = waiter.getName(); - result = waiter.wait(self); + result = waiter.wait(); } END } - void coroPumpPost(coroutine_type::self& self) + void coroPumpPost() { BEGIN { LLCoroEventPump waiter; - result = waiter.postAndWait(self, LLSDMap("value", 17), + result = waiter.postAndWait(LLSDMap("value", 17), immediateAPI.getPump(), "reply"); } END } - void coroPumps(coroutine_type::self& self) + void coroPumps() { BEGIN { LLCoroEventPumps waiter; replyName = waiter.getName0(); errorName = waiter.getName1(); - LLEventWithID pair(waiter.wait(self)); + LLEventWithID pair(waiter.wait()); result = pair.first; which = pair.second; } END } - void coroPumpsNoEx(coroutine_type::self& self) + void coroPumpsNoEx() { BEGIN { LLCoroEventPumps waiter; replyName = waiter.getName0(); errorName = waiter.getName1(); - result = waiter.waitWithException(self); + result = waiter.waitWithException(); } END } - void coroPumpsEx(coroutine_type::self& self) + void coroPumpsEx() { BEGIN { @@ -314,7 +309,7 @@ namespace tut errorName = waiter.getName1(); try { - result = waiter.waitWithException(self); + result = waiter.waitWithException(); debug("no exception"); } catch (const LLErrorEvent& e) @@ -326,19 +321,19 @@ namespace tut END } - void coroPumpsNoLog(coroutine_type::self& self) + void coroPumpsNoLog() { BEGIN { LLCoroEventPumps waiter; replyName = waiter.getName0(); errorName = waiter.getName1(); - result = waiter.waitWithLog(self); + result = waiter.waitWithLog(); } END } - void coroPumpsLog(coroutine_type::self& self) + void coroPumpsLog() { BEGIN { @@ -348,7 +343,7 @@ namespace tut WrapLLErrs capture; try { - result = waiter.waitWithLog(self); + result = waiter.waitWithLog(); debug("no exception"); } catch (const WrapLLErrs::FatalException& e) @@ -360,12 +355,12 @@ namespace tut END } - void coroPumpsPost(coroutine_type::self& self) + void coroPumpsPost() { BEGIN { LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23), + LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), immediateAPI.getPump(), "reply", "error")); result = pair.first; which = pair.second; @@ -373,13 +368,13 @@ namespace tut END } - void coroPumpsPost_1(coroutine_type::self& self) + void coroPumpsPost_1() { BEGIN { LLCoroEventPumps waiter; LLEventWithID pair( - waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()), + waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), immediateAPI.getPump(), "reply", "error")); result = pair.first; which = pair.second; @@ -387,25 +382,25 @@ namespace tut END } - void coroPumpsPostNoEx(coroutine_type::self& self) + void coroPumpsPostNoEx() { BEGIN { LLCoroEventPumps waiter; - result = waiter.postAndWaitWithException(self, LLSDMap("value", 8), + result = waiter.postAndWaitWithException(LLSDMap("value", 8), immediateAPI.getPump(), "reply", "error"); } END } - void coroPumpsPostEx(coroutine_type::self& self) + void coroPumpsPostEx() { BEGIN { LLCoroEventPumps waiter; try { - result = waiter.postAndWaitWithException(self, + result = waiter.postAndWaitWithException( LLSDMap("value", 9)("fail", LLSD()), immediateAPI.getPump(), "reply", "error"); debug("no exception"); @@ -419,18 +414,18 @@ namespace tut END } - void coroPumpsPostNoLog(coroutine_type::self& self) + void coroPumpsPostNoLog() { BEGIN { LLCoroEventPumps waiter; - result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30), + result = waiter.postAndWaitWithLog(LLSDMap("value", 30), immediateAPI.getPump(), "reply", "error"); } END } - void coroPumpsPostLog(coroutine_type::self& self) + void coroPumpsPostLog() { BEGIN { @@ -438,7 +433,7 @@ namespace tut WrapLLErrs capture; try { - result = waiter.postAndWaitWithLog(self, + result = waiter.postAndWaitWithLog( LLSDMap("value", 31)("fail", LLSD()), immediateAPI.getPump(), "reply", "error"); debug("no exception"); @@ -452,11 +447,6 @@ namespace tut END } - void ensure_done(coroutine_type& coro) - { - ensure("coroutine complete", ! coro); - } - ImmediateAPI immediateAPI; std::string replyName, errorName, threw; LLSD result, errordata; @@ -520,7 +510,8 @@ namespace tut // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + boost::dcoroutines::coroutine + coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. @@ -528,7 +519,7 @@ namespace tut // Satisfy the wait. LLEventPumps::instance().obtain("source").post("received"); // Now wait for the coroutine to complete. - ensure_done(coro); + ensure("coroutine complete", ! coro); // ensure the coroutine ran and woke up again with the intended result ensure_equals(result.asString(), "received"); } @@ -538,12 +529,11 @@ namespace tut { set_test_name("waitForEventOn1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<3>", + boost::bind(&coroutine_data::waitForEventOn1, this)); debug("about to send"); LLEventPumps::instance().obtain("source").post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } @@ -553,12 +543,10 @@ namespace tut set_test_name("waitForEventOn2 reply"); { DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<4>", boost::bind(&coroutine_data::waitForEventOn2, this)); debug("about to send"); LLEventPumps::instance().obtain("reply").post("received"); debug("back from send"); - ensure_done(coro); } ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); @@ -569,12 +557,10 @@ namespace tut { set_test_name("waitForEventOn2 error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<5>", boost::bind(&coroutine_data::waitForEventOn2, this)); debug("about to send"); LLEventPumps::instance().obtain("error").post("badness"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } @@ -584,12 +570,10 @@ namespace tut { set_test_name("coroPump"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<6>", boost::bind(&coroutine_data::coroPump, this)); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } @@ -598,12 +582,10 @@ namespace tut { set_test_name("coroPumps reply"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<7>", boost::bind(&coroutine_data::coroPumps, this)); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); } @@ -613,12 +595,10 @@ namespace tut { set_test_name("coroPumps error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<8>", boost::bind(&coroutine_data::coroPumps, this)); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } @@ -628,12 +608,10 @@ namespace tut { set_test_name("coroPumpsNoEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<9>", boost::bind(&coroutine_data::coroPumpsNoEx, this)); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } @@ -642,12 +620,10 @@ namespace tut { set_test_name("coroPumpsEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<10>", boost::bind(&coroutine_data::coroPumpsEx, this)); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asString(), "badness"); } @@ -657,12 +633,10 @@ namespace tut { set_test_name("coroPumpsNoLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<11>", boost::bind(&coroutine_data::coroPumpsNoLog, this)); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } @@ -671,12 +645,10 @@ namespace tut { set_test_name("coroPumpsLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<12>", boost::bind(&coroutine_data::coroPumpsLog, this)); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "badness"); } @@ -686,9 +658,7 @@ namespace tut { set_test_name("postAndWait1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<13>", boost::bind(&coroutine_data::postAndWait1, this)); ensure_equals(result.asInteger(), 18); } @@ -697,9 +667,7 @@ namespace tut { set_test_name("postAndWait2"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<14>", boost::bind(&coroutine_data::postAndWait2, this)); ensure_equals(result.asInteger(), 19); ensure_equals(which, 0); } @@ -709,9 +677,7 @@ namespace tut { set_test_name("postAndWait2_1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<15>", boost::bind(&coroutine_data::postAndWait2_1, this)); ensure_equals(result.asInteger(), 19); ensure_equals(which, 1); } @@ -721,9 +687,7 @@ namespace tut { set_test_name("coroPumpPost"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<16>", boost::bind(&coroutine_data::coroPumpPost, this)); ensure_equals(result.asInteger(), 18); } @@ -732,9 +696,7 @@ namespace tut { set_test_name("coroPumpsPost reply"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<17>", boost::bind(&coroutine_data::coroPumpsPost, this)); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 0); } @@ -744,9 +706,7 @@ namespace tut { set_test_name("coroPumpsPost error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<18>", boost::bind(&coroutine_data::coroPumpsPost_1, this)); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 1); } @@ -756,9 +716,8 @@ namespace tut { set_test_name("coroPumpsPostNoEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<19>", + boost::bind(&coroutine_data::coroPumpsPostNoEx, this)); ensure_equals(result.asInteger(), 9); } @@ -767,9 +726,7 @@ namespace tut { set_test_name("coroPumpsPostEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<20>", boost::bind(&coroutine_data::coroPumpsPostEx, this)); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asInteger(), 10); } @@ -779,9 +736,8 @@ namespace tut { set_test_name("coroPumpsPostNoLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<21>", + boost::bind(&coroutine_data::coroPumpsPostNoLog, this)); ensure_equals(result.asInteger(), 31); } @@ -790,9 +746,7 @@ namespace tut { set_test_name("coroPumpsPostLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<22>", boost::bind(&coroutine_data::coroPumpsPostLog, this)); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "32"); } -- cgit v1.2.3 From 7eaee4fde770edae6d64ddf07a917b2ae16a9928 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 1 Jul 2015 19:14:24 -0400 Subject: MAINT-5351: Reorganize lleventcoro_test.cpp to be more readable. Each test() method invokes a function from earlier in the source. It's much better if each of those functions immediately precedes the test that invokes it. --- indra/llcommon/tests/lleventcoro_test.cpp | 660 ++++++++++++++++-------------- 1 file changed, 344 insertions(+), 316 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 31e9856477..da927038ab 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -160,298 +160,7 @@ private: *****************************************************************************/ namespace tut { - struct coroutine_data - { - // Define coroutine bodies as methods here so they can use ensure*() - - void explicit_wait(boost::dcoroutines::coroutine::self& self) - { - BEGIN - { - // ... do whatever preliminary stuff must happen ... - - // declare the future - boost::dcoroutines::future future(self); - // tell the future what to wait for - LLTempBoundListener connection( - LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); - ensure("Not yet", ! future); - // attempting to dereference ("resolve") the future causes the calling - // coroutine to wait for it - debug("about to wait"); - result = *future; - ensure("Got it", future); - } - END - } - - void waitForEventOn1() - { - BEGIN - { - result = waitForEventOn("source"); - } - END - } - - void waitForEventOn2() - { - BEGIN - { - LLEventWithID pair = waitForEventOn("reply", "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void postAndWait1() - { - BEGIN - { - result = postAndWait(LLSDMap("value", 17), // request event - immediateAPI.getPump(), // requestPump - "reply1", // replyPump - "reply"); // request["reply"] = name - } - END - } - - void postAndWait2() - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void postAndWait2_1() - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void coroPump() - { - BEGIN - { - LLCoroEventPump waiter; - replyName = waiter.getName(); - result = waiter.wait(); - } - END - } - - void coroPumpPost() - { - BEGIN - { - LLCoroEventPump waiter; - result = waiter.postAndWait(LLSDMap("value", 17), - immediateAPI.getPump(), "reply"); - } - END - } - - void coroPumps() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - LLEventWithID pair(waiter.wait()); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithException(); - } - END - } - - void coroPumpsEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - try - { - result = waiter.waitWithException(); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - void coroPumpsNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithLog(); - } - END - } - - void coroPumpsLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - WrapLLErrs capture; - try - { - result = waiter.waitWithLog(); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - - void coroPumpsPost() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsPost_1() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair( - waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsPostNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithException(LLSDMap("value", 8), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - void coroPumpsPostEx() - { - BEGIN - { - LLCoroEventPumps waiter; - try - { - result = waiter.postAndWaitWithException( - LLSDMap("value", 9)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - void coroPumpsPostNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithLog(LLSDMap("value", 30), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - void coroPumpsPostLog() - { - BEGIN - { - LLCoroEventPumps waiter; - WrapLLErrs capture; - try - { - result = waiter.postAndWaitWithLog( - LLSDMap("value", 31)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - - ImmediateAPI immediateAPI; - std::string replyName, errorName, threw; - LLSD result, errordata; - int which; - }; + struct coroutine_data {}; typedef test_group coroutine_group; typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); @@ -501,9 +210,49 @@ namespace tut ensure("done", ! matcher); } + // use static data so we can intersperse coroutine functions with the + // tests that engage them + ImmediateAPI immediateAPI; + std::string replyName, errorName, threw; + LLSD result, errordata; + int which; + + // reinit vars at the start of each test + void clear() + { + replyName.clear(); + errorName.clear(); + threw.clear(); + result = LLSD(); + errordata = LLSD(); + which = 0; + } + + void explicit_wait(boost::dcoroutines::coroutine::self& self) + { + BEGIN + { + // ... do whatever preliminary stuff must happen ... + + // declare the future + boost::dcoroutines::future future(self); + // tell the future what to wait for + LLTempBoundListener connection( + LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); + ensure("Not yet", ! future); + // attempting to dereference ("resolve") the future causes the calling + // coroutine to wait for it + debug("about to wait"); + result = *future; + ensure("Got it", future); + } + END + } + template<> template<> void object::test<2>() { + clear(); set_test_name("explicit_wait"); DEBUG; @@ -511,7 +260,7 @@ namespace tut // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. boost::dcoroutines::coroutine - coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. @@ -524,26 +273,48 @@ namespace tut ensure_equals(result.asString(), "received"); } + void waitForEventOn1() + { + BEGIN + { + result = waitForEventOn("source"); + } + END + } + template<> template<> void object::test<3>() { + clear(); set_test_name("waitForEventOn1"); DEBUG; - LLCoros::instance().launch("test<3>", - boost::bind(&coroutine_data::waitForEventOn1, this)); + LLCoros::instance().launch("test<3>", waitForEventOn1); debug("about to send"); LLEventPumps::instance().obtain("source").post("received"); debug("back from send"); ensure_equals(result.asString(), "received"); } + void waitForEventOn2() + { + BEGIN + { + LLEventWithID pair = waitForEventOn("reply", "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<4>() { + clear(); set_test_name("waitForEventOn2 reply"); { DEBUG; - LLCoros::instance().launch("test<4>", boost::bind(&coroutine_data::waitForEventOn2, this)); + LLCoros::instance().launch("test<4>", waitForEventOn2); debug("about to send"); LLEventPumps::instance().obtain("reply").post("received"); debug("back from send"); @@ -555,9 +326,10 @@ namespace tut template<> template<> void object::test<5>() { + clear(); set_test_name("waitForEventOn2 error"); DEBUG; - LLCoros::instance().launch("test<5>", boost::bind(&coroutine_data::waitForEventOn2, this)); + LLCoros::instance().launch("test<5>", waitForEventOn2); debug("about to send"); LLEventPumps::instance().obtain("error").post("badness"); debug("back from send"); @@ -565,24 +337,51 @@ namespace tut ensure_equals("which pump", which, 1); } + void coroPump() + { + BEGIN + { + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.wait(); + } + END + } + template<> template<> void object::test<6>() { + clear(); set_test_name("coroPump"); DEBUG; - LLCoros::instance().launch("test<6>", boost::bind(&coroutine_data::coroPump, this)); + LLCoros::instance().launch("test<6>", coroPump); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); ensure_equals(result.asString(), "received"); } + void coroPumps() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + LLEventWithID pair(waiter.wait()); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<7>() { + clear(); set_test_name("coroPumps reply"); DEBUG; - LLCoros::instance().launch("test<7>", boost::bind(&coroutine_data::coroPumps, this)); + LLCoros::instance().launch("test<7>", coroPumps); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); @@ -593,9 +392,10 @@ namespace tut template<> template<> void object::test<8>() { + clear(); set_test_name("coroPumps error"); DEBUG; - LLCoros::instance().launch("test<8>", boost::bind(&coroutine_data::coroPumps, this)); + LLCoros::instance().launch("test<8>", coroPumps); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); @@ -603,24 +403,59 @@ namespace tut ensure_equals("which pump", which, 1); } + void coroPumpsNoEx() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithException(); + } + END + } + template<> template<> void object::test<9>() { + clear(); set_test_name("coroPumpsNoEx"); DEBUG; - LLCoros::instance().launch("test<9>", boost::bind(&coroutine_data::coroPumpsNoEx, this)); + LLCoros::instance().launch("test<9>", coroPumpsNoEx); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); ensure_equals(result.asString(), "received"); } + void coroPumpsEx() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + try + { + result = waiter.waitWithException(); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + template<> template<> void object::test<10>() { + clear(); set_test_name("coroPumpsEx"); DEBUG; - LLCoros::instance().launch("test<10>", boost::bind(&coroutine_data::coroPumpsEx, this)); + LLCoros::instance().launch("test<10>", coroPumpsEx); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); @@ -628,24 +463,60 @@ namespace tut ensure_equals("got error", errordata.asString(), "badness"); } + void coroPumpsNoLog() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithLog(); + } + END + } + template<> template<> void object::test<11>() { + clear(); set_test_name("coroPumpsNoLog"); DEBUG; - LLCoros::instance().launch("test<11>", boost::bind(&coroutine_data::coroPumpsNoLog, this)); + LLCoros::instance().launch("test<11>", coroPumpsNoLog); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); ensure_equals(result.asString(), "received"); } + void coroPumpsLog() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + WrapLLErrs capture; + try + { + result = waiter.waitWithLog(); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + template<> template<> void object::test<12>() { + clear(); set_test_name("coroPumpsLog"); DEBUG; - LLCoros::instance().launch("test<12>", boost::bind(&coroutine_data::coroPumpsLog, this)); + LLCoros::instance().launch("test<12>", coroPumpsLog); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); @@ -653,100 +524,257 @@ namespace tut ensure_contains("got error", threw, "badness"); } + void postAndWait1() + { + BEGIN + { + result = postAndWait(LLSDMap("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + } + END + } + template<> template<> void object::test<13>() { + clear(); set_test_name("postAndWait1"); DEBUG; - LLCoros::instance().launch("test<13>", boost::bind(&coroutine_data::postAndWait1, this)); + LLCoros::instance().launch("test<13>", postAndWait1); ensure_equals(result.asInteger(), 18); } + void postAndWait2() + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<14>() { + clear(); set_test_name("postAndWait2"); DEBUG; - LLCoros::instance().launch("test<14>", boost::bind(&coroutine_data::postAndWait2, this)); + LLCoros::instance().launch("test<14>", postAndWait2); ensure_equals(result.asInteger(), 19); ensure_equals(which, 0); } + void postAndWait2_1() + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<15>() { + clear(); set_test_name("postAndWait2_1"); DEBUG; - LLCoros::instance().launch("test<15>", boost::bind(&coroutine_data::postAndWait2_1, this)); + LLCoros::instance().launch("test<15>", postAndWait2_1); ensure_equals(result.asInteger(), 19); ensure_equals(which, 1); } + void coroPumpPost() + { + BEGIN + { + LLCoroEventPump waiter; + result = waiter.postAndWait(LLSDMap("value", 17), + immediateAPI.getPump(), "reply"); + } + END + } + template<> template<> void object::test<16>() { + clear(); set_test_name("coroPumpPost"); DEBUG; - LLCoros::instance().launch("test<16>", boost::bind(&coroutine_data::coroPumpPost, this)); + LLCoros::instance().launch("test<16>", coroPumpPost); ensure_equals(result.asInteger(), 18); } + void coroPumpsPost() + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<17>() { + clear(); set_test_name("coroPumpsPost reply"); DEBUG; - LLCoros::instance().launch("test<17>", boost::bind(&coroutine_data::coroPumpsPost, this)); + LLCoros::instance().launch("test<17>", coroPumpsPost); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 0); } + void coroPumpsPost_1() + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair( + waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<18>() { + clear(); set_test_name("coroPumpsPost error"); DEBUG; - LLCoros::instance().launch("test<18>", boost::bind(&coroutine_data::coroPumpsPost_1, this)); + LLCoros::instance().launch("test<18>", coroPumpsPost_1); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 1); } + void coroPumpsPostNoEx() + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithException(LLSDMap("value", 8), + immediateAPI.getPump(), "reply", "error"); + } + END + } + template<> template<> void object::test<19>() { + clear(); set_test_name("coroPumpsPostNoEx"); DEBUG; - LLCoros::instance().launch("test<19>", - boost::bind(&coroutine_data::coroPumpsPostNoEx, this)); + LLCoros::instance().launch("test<19>", coroPumpsPostNoEx); ensure_equals(result.asInteger(), 9); } + void coroPumpsPostEx() + { + BEGIN + { + LLCoroEventPumps waiter; + try + { + result = waiter.postAndWaitWithException( + LLSDMap("value", 9)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + template<> template<> void object::test<20>() { + clear(); set_test_name("coroPumpsPostEx"); DEBUG; - LLCoros::instance().launch("test<20>", boost::bind(&coroutine_data::coroPumpsPostEx, this)); + LLCoros::instance().launch("test<20>", coroPumpsPostEx); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asInteger(), 10); } + void coroPumpsPostNoLog() + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithLog(LLSDMap("value", 30), + immediateAPI.getPump(), "reply", "error"); + } + END + } + template<> template<> void object::test<21>() { + clear(); set_test_name("coroPumpsPostNoLog"); DEBUG; - LLCoros::instance().launch("test<21>", - boost::bind(&coroutine_data::coroPumpsPostNoLog, this)); + LLCoros::instance().launch("test<21>", coroPumpsPostNoLog); ensure_equals(result.asInteger(), 31); } + void coroPumpsPostLog() + { + BEGIN + { + LLCoroEventPumps waiter; + WrapLLErrs capture; + try + { + result = waiter.postAndWaitWithLog( + LLSDMap("value", 31)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + template<> template<> void object::test<22>() { + clear(); set_test_name("coroPumpsPostLog"); DEBUG; - LLCoros::instance().launch("test<22>", boost::bind(&coroutine_data::coroPumpsPostLog, this)); + LLCoros::instance().launch("test<22>", coroPumpsPostLog); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "32"); } -- cgit v1.2.3 From f90023fc0b3b61fd346a2b56e30e5f3c35814192 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 2 Jul 2015 17:00:32 -0400 Subject: MAINT-5357: Introduce and populate llcoro:: namespace. To date, the coroutine helper functions in lleventcoro.h have been in the global namespace. Migrate them into llcoro namespace, and fix references. Specifically, LLVoidListener => llcoro::VoidListener, and voidlistener(), postAndWait(), both waitForEventOn(), postAndWait2(), errorException() and errorLog() have been moved into llcoro. Also migrate new LLCoros::get_self() and Suspending to llcoro:: namespace. While at it, I realized that -- having converted several lleventcoro.h functions from templates (for arbitrary 'self' parameter type) to ordinary functions, having moved them from lleventcoro.h to lleventcoro.cpp, we can now migrate their helpers from lleventcoro.h to lleventcoro.cpp as well. This eliminates the need for the LLEventDetail namespace; the relevant helpers are now in an anonymous namespace in the .cpp file: listenerNameForCoro(), storeToLLSDPath(), WaitForEventOnHelper and wfeoh(). --- indra/llcommon/llcoros.cpp | 10 +-- indra/llcommon/llcoros.h | 35 +++++---- indra/llcommon/lleventcoro.cpp | 119 ++++++++++++++++++++++++----- indra/llcommon/lleventcoro.h | 122 +++++++----------------------- indra/llcommon/tests/lleventcoro_test.cpp | 5 +- 5 files changed, 156 insertions(+), 135 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 2d0c419ae0..957fe034e1 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -69,9 +69,9 @@ sCurrentSelf(no_cleanup); } // anonymous //static -LLCoros::coro::self& LLCoros::get_self() +LLCoros::coro::self& llcoro::get_self() { - coro::self* current_self = sCurrentSelf.get(); + LLCoros::coro::self* current_self = sCurrentSelf.get(); if (! current_self) { LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; @@ -79,7 +79,7 @@ LLCoros::coro::self& LLCoros::get_self() return *current_self; } -LLCoros::Suspending::Suspending(): +llcoro::Suspending::Suspending(): mSuspended(sCurrentSelf.get()) { // For the duration of our time away from this coroutine, sCurrentSelf @@ -87,7 +87,7 @@ LLCoros::Suspending::Suspending(): sCurrentSelf.reset(); } -LLCoros::Suspending::~Suspending() +llcoro::Suspending::~Suspending() { // Okay, we're back, reinstate previous value of sCurrentSelf. sCurrentSelf.reset(mSuspended); @@ -171,7 +171,7 @@ 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(); + void* self_id = llcoro::get_self().get_id(); for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) { namespace coro_private = boost::dcoroutines::detail; diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 3f58a17aa9..e478600f00 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -137,21 +137,6 @@ public: */ std::string getName() const; - /// get the current coro::self& for those who really really care - static 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 - { - public: - Suspending(); - ~Suspending(); - - private: - coro::self* mSuspended; - }; - /// for delayed initialization void setStackSize(S32 stacksize); @@ -166,4 +151,24 @@ private: CoroMap mCoros; }; +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 +{ +public: + Suspending(); + ~Suspending(); + +private: + LLCoros::coro::self* mSuspended; +}; + +} // namespace llcoro + #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index be93e9c83b..b7e7edec5a 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -41,7 +41,23 @@ #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoro() +namespace +{ + +/** + * waitForEventOn() permits a coroutine to temporarily listen on an + * LLEventPump any number of times. We don't really want to have to ask + * the caller to label each such call with a distinct string; the whole + * point of waitForEventOn() is to present a nice sequential interface to + * the underlying LLEventPump-with-named-listeners machinery. So we'll use + * LLEventPump::inventName() to generate a distinct name for each + * temporary listener. On the other hand, because a given coroutine might + * call waitForEventOn() any number of times, we don't really want to + * consume an arbitrary number of generated inventName()s: that namespace, + * though large, is nonetheless finite. So we memoize an invented name for + * each distinct coroutine instance. + */ +std::string listenerNameForCoro() { // If this coroutine was launched by LLCoros::launch(), find that name. std::string name(LLCoros::instance().getName()); @@ -56,7 +72,25 @@ std::string LLEventDetail::listenerNameForCoro() return name; } -void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +/** + * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * parameter: + * + * * If path.isUndefined(), do nothing. + * * If path.isString(), @a dest is an LLSD map: store @a value + * into dest[path.asString()]. + * * If path.isInteger(), @a dest is an LLSD array: store @a + * value into dest[path.asInteger()]. + * * If path.isArray(), iteratively apply the rules above to step + * down through the structure of @a dest. The last array entry in @a + * path specifies the entry in the lowest-level structure in @a dest + * into which to store @a value. + * + * @note + * In the degenerate case in which @a path is an empty array, @a dest will + * @em become @a value rather than @em containing it. + */ +void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) { if (rawPath.isUndefined()) { @@ -109,14 +143,16 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } -LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +} // anonymous + +LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + boost::dcoroutines::future future(llcoro::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(LLEventDetail::listenerNameForCoro()); + std::string listenerName(listenerNameForCoro()); LLTempBoundListener connection( replyPump.getPump().listen(listenerName, voidlistener(boost::dcoroutines::make_callback(future)))); @@ -126,7 +162,7 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, // If replyPumpNamePath is non-empty, store the replyPump name in the // request event. LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -142,7 +178,7 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, LLSD value; { // instantiate Suspending to manage the "current" coroutine - LLCoros::Suspending suspended; + llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName @@ -151,6 +187,53 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, return value; } +namespace +{ + +/** + * This helper is specifically for the two-pump version of waitForEventOn(). + * We use a single future object, but we want to listen on two pumps with it. + * Since we must still adapt from (the callable constructed by) + * boost::dcoroutines::make_callback() (void return) to provide an event + * listener (bool return), we've adapted VoidListener for the purpose. The + * basic idea is that we construct a distinct instance of WaitForEventOnHelper + * -- binding different instance data -- for each of the pumps. Then, when a + * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine + * that LLSD with its discriminator to feed the future object. + */ +template +class WaitForEventOnHelper +{ +public: + WaitForEventOnHelper(const LISTENER& listener, int discriminator): + mListener(listener), + mDiscrim(discriminator) + {} + // this signature is required for an LLEventPump listener + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + mListener(LLEventWithID(event, mDiscrim)); + // don't swallow the event, let other listeners see it + return false; + } +private: + LISTENER mListener; + const int mDiscrim; +}; + +/// WaitForEventOnHelper type-inference helper +template +WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) +{ + return WaitForEventOnHelper(listener, discriminator); +} + +} // anonymous + +namespace llcoro +{ + LLEventWithID postAndWait2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, @@ -159,26 +242,26 @@ LLEventWithID postAndWait2(const LLSD& event, const LLSD& replyPump1NamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + boost::dcoroutines::future future(llcoro::get_self()); // either callback will assign a value to this future; listen on // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro()); + std::string name(listenerNameForCoro()); LLTempBoundListener connection0( replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); + wfeoh(boost::dcoroutines::make_callback(future), 0))); LLTempBoundListener connection1( replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); + wfeoh(boost::dcoroutines::make_callback(future), 1))); // skip the "post" part if requestPump is default-constructed if (requestPump) { // If either replyPumpNamePath is non-empty, store the corresponding // replyPump name in the request event. LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); + storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name << " posting to " << requestPump.getPump().getName() << ": " << modevent << LL_ENDL; @@ -191,7 +274,7 @@ LLEventWithID postAndWait2(const LLSD& event, LLEventWithID value; { // instantiate Suspending to manage "current" coroutine - LLCoros::Suspending suspended; + llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name @@ -227,3 +310,5 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc) // A simple return must therefore be from the reply pump (pump 0). return result.first; } + +} // namespace llcoro diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index daf9360a2e..2a60f2b1e0 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -72,13 +72,16 @@ private: boost::optional mPump; }; +namespace llcoro +{ + /// This is an adapter for a signature like void LISTENER(const LLSD&), which /// isn't a valid LLEventPump listener: such listeners should return bool. template -class LLVoidListener +class VoidListener { public: - LLVoidListener(const LISTENER& listener): + VoidListener(const LISTENER& listener): mListener(listener) {} bool operator()(const LLSD& event) @@ -91,51 +94,13 @@ private: LISTENER mListener; }; -/// LLVoidListener helper function to infer the type of the LISTENER +/// VoidListener helper function to infer the type of the LISTENER template -LLVoidListener voidlistener(const LISTENER& listener) +VoidListener voidlistener(const LISTENER& listener) { - return LLVoidListener(listener); + return VoidListener(listener); } -namespace LLEventDetail -{ - /** - * waitForEventOn() permits a coroutine to temporarily listen on an - * LLEventPump any number of times. We don't really want to have to ask - * the caller to label each such call with a distinct string; the whole - * point of waitForEventOn() is to present a nice sequential interface to - * the underlying LLEventPump-with-named-listeners machinery. So we'll use - * LLEventPump::inventName() to generate a distinct name for each - * temporary listener. On the other hand, because a given coroutine might - * call waitForEventOn() any number of times, we don't really want to - * consume an arbitrary number of generated inventName()s: that namespace, - * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance. - */ - std::string listenerNameForCoro(); - - /** - * Implement behavior described for postAndWait()'s @a replyPumpNamePath - * parameter: - * - * * If path.isUndefined(), do nothing. - * * If path.isString(), @a dest is an LLSD map: store @a value - * into dest[path.asString()]. - * * If path.isInteger(), @a dest is an LLSD array: store @a - * value into dest[path.asInteger()]. - * * If path.isArray(), iteratively apply the rules above to step - * down through the structure of @a dest. The last array entry in @a - * path specifies the entry in the lowest-level structure in @a dest - * into which to store @a value. - * - * @note - * In the degenerate case in which @a path is an empty array, @a dest will - * @em become @a value rather than @em containing it. - */ - LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value); -} // namespace LLEventDetail - /** * Post specified LLSD event on the specified LLEventPump, then wait for a * response on specified other LLEventPump. This is more than mere @@ -196,50 +161,13 @@ LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); } +} // namespace llcoro + /// return type for two-pump variant of waitForEventOn() typedef std::pair LLEventWithID; -namespace LLEventDetail +namespace llcoro { - /** - * This helper is specifically for the two-pump version of waitForEventOn(). - * We use a single future object, but we want to listen on two pumps with it. - * Since we must still adapt from (the callable constructed by) - * boost::dcoroutines::make_callback() (void return) to provide an event - * listener (bool return), we've adapted LLVoidListener for the purpose. The - * basic idea is that we construct a distinct instance of WaitForEventOnHelper - * -- binding different instance data -- for each of the pumps. Then, when a - * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine - * that LLSD with its discriminator to feed the future object. - */ - template - class WaitForEventOnHelper - { - public: - WaitForEventOnHelper(const LISTENER& listener, int discriminator): - mListener(listener), - mDiscrim(discriminator) - {} - // this signature is required for an LLEventPump listener - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - mListener(LLEventWithID(event, mDiscrim)); - // don't swallow the event, let other listeners see it - return false; - } - private: - LISTENER mListener; - const int mDiscrim; - }; - - /// WaitForEventOnHelper type-inference helper - template - WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) - { - return WaitForEventOnHelper(listener, discriminator); - } -} // namespace LLEventDetail /** * This function waits for a reply on either of two specified LLEventPumps. @@ -334,6 +262,8 @@ private: */ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Certain event APIs require the name of an LLEventPump on which they should * post results. While it works to invent a distinct name and let @@ -365,13 +295,13 @@ public: */ LLSD wait() { - return ::waitForEventOn(mPump); + return llcoro::waitForEventOn(mPump); } LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -410,19 +340,19 @@ public: /// waitForEventOn(either of our two LLEventPumps) LLEventWithID wait() { - return waitForEventOn(mPump0, mPump1); + return llcoro::waitForEventOn(mPump0, mPump1); } /// errorException(wait()) LLSD waitWithException() { - return errorException(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); } /// errorLog(wait()) LLSD waitWithLog() { - return errorLog(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); } LLEventWithID postAndWait(const LLSD& event, @@ -430,8 +360,8 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); + return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); } LLSD postAndWaitWithException(const LLSD& event, @@ -439,9 +369,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorException(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } LLSD postAndWaitWithLog(const LLSD& event, @@ -449,9 +379,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorLog(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } private: diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index da927038ab..79bcfe58ed 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -86,6 +86,8 @@ #include "lleventcoro.h" #include "../test/debug.h" +using namespace llcoro; + /***************************************************************************** * from the banana.cpp example program borrowed for test<1>() *****************************************************************************/ @@ -259,8 +261,7 @@ namespace tut // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - boost::dcoroutines::coroutine - coro(explicit_wait); + boost::dcoroutines::coroutine coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. -- cgit v1.2.3 From d34476359e59a1902b1498fbed94daca3d20bff1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 3 Jul 2015 09:57:02 -0400 Subject: MAINT-5357: Extract LLErrorEvent to global namespace. This exception class got inadvertently swept up into the llcoro namespace. Its LLClassConvention name is intended for use in the global namespace. As there are no current references, this is a trivial change. --- indra/llcommon/lleventcoro.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 2a60f2b1e0..f1c5dc2fde 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -236,6 +236,8 @@ waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& */ LLSD errorException(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Exception thrown by errorException(). We don't call this LLEventError * because it's not an error in event processing: rather, this exception @@ -256,6 +258,9 @@ private: LLSD mData; }; +namespace llcoro +{ + /** * Like errorException(), save that this trips a fatal error using LL_ERRS * rather than throwing an exception. -- cgit v1.2.3 From 1d2514956f15f780aaead9bc123c1353a5b7fd20 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 4 Jul 2015 19:58:03 -0400 Subject: MAINT-5357: Add llcoro::yield() function to yield for one frame. --- indra/llcommon/lleventcoro.cpp | 8 ++++++++ indra/llcommon/lleventcoro.h | 7 +++++++ 2 files changed, 15 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index b7e7edec5a..ad02e139b8 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -145,6 +145,14 @@ void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) } // anonymous +void llcoro::yield() +{ + // By viewer convention, we post an event on the "mainloop" LLEventPump + // each iteration of the main event-handling loop. So waiting for a single + // event on "mainloop" gives us a one-frame yield. + waitForEventOn("mainloop"); +} + LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index f1c5dc2fde..e2ce4bb193 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -101,6 +101,13 @@ VoidListener voidlistener(const LISTENER& listener) return VoidListener(listener); } +/** + * Yield control from a coroutine for one "mainloop" tick. If your coroutine + * runs without suspending for nontrivial time, sprinkle in calls to this + * function to avoid stalling the rest of the viewer processing. + */ +void yield(); + /** * Post specified LLSD event on the specified LLEventPump, then wait for a * response on specified other LLEventPump. This is more than mere -- cgit v1.2.3 From 8487a19df1df6e9c279358a8694a729511f6dd85 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 6 Jul 2015 09:51:05 -0700 Subject: Force Boost result_of to use TR1 for tests. --- indra/llcommon/tests/lleventcoro_test.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 79bcfe58ed..807577a637 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -64,12 +64,14 @@ // Boost.Coroutine #include is the *first* #include of the platform header. // That means that client code must generally #include Boost.Coroutine headers // before anything else. +#define BOOST_RESULT_OF_USE_TR1 1 #include // Normally, lleventcoro.h obviates future.hpp. We only include this because // we implement a "by hand" test of future functionality. #include #include #include +#include #include "linden_common.h" -- cgit v1.2.3 From 0b9a641b17be2a22e2a274ce6ab18a84612454af Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 6 Jul 2015 15:05:27 -0700 Subject: Force Boost use of TR1 for result_of in tests --- indra/llcommon/tests/lleventcoro_test.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 79bcfe58ed..9e2bc0acee 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. -- cgit v1.2.3 From 4c1d47d4ae231c1141c6ecf707c033563c99382a Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 7 Jul 2015 19:31:34 +0100 Subject: Backed out selfless merge --- indra/llcommon/llcoros.cpp | 10 +-- indra/llcommon/llcoros.h | 35 ++++---- indra/llcommon/lleventcoro.cpp | 127 ++++------------------------ indra/llcommon/lleventcoro.h | 132 +++++++++++++++++++++--------- indra/llcommon/tests/lleventcoro_test.cpp | 5 +- 5 files changed, 134 insertions(+), 175 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 957fe034e1..2d0c419ae0 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -69,9 +69,9 @@ sCurrentSelf(no_cleanup); } // anonymous //static -LLCoros::coro::self& llcoro::get_self() +LLCoros::coro::self& LLCoros::get_self() { - LLCoros::coro::self* current_self = sCurrentSelf.get(); + coro::self* current_self = sCurrentSelf.get(); if (! current_self) { LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; @@ -79,7 +79,7 @@ LLCoros::coro::self& llcoro::get_self() return *current_self; } -llcoro::Suspending::Suspending(): +LLCoros::Suspending::Suspending(): mSuspended(sCurrentSelf.get()) { // For the duration of our time away from this coroutine, sCurrentSelf @@ -87,7 +87,7 @@ llcoro::Suspending::Suspending(): sCurrentSelf.reset(); } -llcoro::Suspending::~Suspending() +LLCoros::Suspending::~Suspending() { // Okay, we're back, reinstate previous value of sCurrentSelf. sCurrentSelf.reset(mSuspended); @@ -171,7 +171,7 @@ 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(); + void* self_id = get_self().get_id(); for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) { namespace coro_private = boost::dcoroutines::detail; diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index e478600f00..3f58a17aa9 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -137,6 +137,21 @@ public: */ std::string getName() const; + /// get the current coro::self& for those who really really care + static 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 + { + public: + Suspending(); + ~Suspending(); + + private: + coro::self* mSuspended; + }; + /// for delayed initialization void setStackSize(S32 stacksize); @@ -151,24 +166,4 @@ private: CoroMap mCoros; }; -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 -{ -public: - Suspending(); - ~Suspending(); - -private: - LLCoros::coro::self* mSuspended; -}; - -} // namespace llcoro - #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index ad02e139b8..be93e9c83b 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -41,23 +41,7 @@ #include "llerror.h" #include "llcoros.h" -namespace -{ - -/** - * waitForEventOn() permits a coroutine to temporarily listen on an - * LLEventPump any number of times. We don't really want to have to ask - * the caller to label each such call with a distinct string; the whole - * point of waitForEventOn() is to present a nice sequential interface to - * the underlying LLEventPump-with-named-listeners machinery. So we'll use - * LLEventPump::inventName() to generate a distinct name for each - * temporary listener. On the other hand, because a given coroutine might - * call waitForEventOn() any number of times, we don't really want to - * consume an arbitrary number of generated inventName()s: that namespace, - * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance. - */ -std::string listenerNameForCoro() +std::string LLEventDetail::listenerNameForCoro() { // If this coroutine was launched by LLCoros::launch(), find that name. std::string name(LLCoros::instance().getName()); @@ -72,25 +56,7 @@ std::string listenerNameForCoro() return name; } -/** - * Implement behavior described for postAndWait()'s @a replyPumpNamePath - * parameter: - * - * * If path.isUndefined(), do nothing. - * * If path.isString(), @a dest is an LLSD map: store @a value - * into dest[path.asString()]. - * * If path.isInteger(), @a dest is an LLSD array: store @a - * value into dest[path.asInteger()]. - * * If path.isArray(), iteratively apply the rules above to step - * down through the structure of @a dest. The last array entry in @a - * path specifies the entry in the lowest-level structure in @a dest - * into which to store @a value. - * - * @note - * In the degenerate case in which @a path is an empty array, @a dest will - * @em become @a value rather than @em containing it. - */ -void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) { if (rawPath.isUndefined()) { @@ -143,24 +109,14 @@ void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) *pdest = value; } -} // anonymous - -void llcoro::yield() -{ - // By viewer convention, we post an event on the "mainloop" LLEventPump - // each iteration of the main event-handling loop. So waiting for a single - // event on "mainloop" gives us a one-frame yield. - waitForEventOn("mainloop"); -} - -LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future - boost::dcoroutines::future future(llcoro::get_self()); + boost::dcoroutines::future 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()); + std::string listenerName(LLEventDetail::listenerNameForCoro()); LLTempBoundListener connection( replyPump.getPump().listen(listenerName, voidlistener(boost::dcoroutines::make_callback(future)))); @@ -170,7 +126,7 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request // If replyPumpNamePath is non-empty, store the replyPump name in the // request event. LLSD modevent(event); - storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -186,7 +142,7 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request LLSD value; { // instantiate Suspending to manage the "current" coroutine - llcoro::Suspending suspended; + LLCoros::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName @@ -195,53 +151,6 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request return value; } -namespace -{ - -/** - * This helper is specifically for the two-pump version of waitForEventOn(). - * We use a single future object, but we want to listen on two pumps with it. - * Since we must still adapt from (the callable constructed by) - * boost::dcoroutines::make_callback() (void return) to provide an event - * listener (bool return), we've adapted VoidListener for the purpose. The - * basic idea is that we construct a distinct instance of WaitForEventOnHelper - * -- binding different instance data -- for each of the pumps. Then, when a - * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine - * that LLSD with its discriminator to feed the future object. - */ -template -class WaitForEventOnHelper -{ -public: - WaitForEventOnHelper(const LISTENER& listener, int discriminator): - mListener(listener), - mDiscrim(discriminator) - {} - // this signature is required for an LLEventPump listener - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - mListener(LLEventWithID(event, mDiscrim)); - // don't swallow the event, let other listeners see it - return false; - } -private: - LISTENER mListener; - const int mDiscrim; -}; - -/// WaitForEventOnHelper type-inference helper -template -WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) -{ - return WaitForEventOnHelper(listener, discriminator); -} - -} // anonymous - -namespace llcoro -{ - LLEventWithID postAndWait2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, @@ -250,26 +159,26 @@ LLEventWithID postAndWait2(const LLSD& event, const LLSD& replyPump1NamePath) { // declare the future - boost::dcoroutines::future future(llcoro::get_self()); + boost::dcoroutines::future 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()); + std::string name(LLEventDetail::listenerNameForCoro()); LLTempBoundListener connection0( replyPump0.getPump().listen(name + "a", - wfeoh(boost::dcoroutines::make_callback(future), 0))); + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); LLTempBoundListener connection1( replyPump1.getPump().listen(name + "b", - wfeoh(boost::dcoroutines::make_callback(future), 1))); + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); // skip the "post" part if requestPump is default-constructed if (requestPump) { // If either replyPumpNamePath is non-empty, store the corresponding // replyPump name in the request event. LLSD modevent(event); - storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name << " posting to " << requestPump.getPump().getName() << ": " << modevent << LL_ENDL; @@ -282,7 +191,7 @@ LLEventWithID postAndWait2(const LLSD& event, LLEventWithID value; { // instantiate Suspending to manage "current" coroutine - llcoro::Suspending suspended; + LLCoros::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name @@ -318,5 +227,3 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc) // A simple return must therefore be from the reply pump (pump 0). return result.first; } - -} // namespace llcoro diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index e2ce4bb193..daf9360a2e 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -72,16 +72,13 @@ private: boost::optional mPump; }; -namespace llcoro -{ - /// This is an adapter for a signature like void LISTENER(const LLSD&), which /// isn't a valid LLEventPump listener: such listeners should return bool. template -class VoidListener +class LLVoidListener { public: - VoidListener(const LISTENER& listener): + LLVoidListener(const LISTENER& listener): mListener(listener) {} bool operator()(const LLSD& event) @@ -94,19 +91,50 @@ private: LISTENER mListener; }; -/// VoidListener helper function to infer the type of the LISTENER +/// LLVoidListener helper function to infer the type of the LISTENER template -VoidListener voidlistener(const LISTENER& listener) +LLVoidListener voidlistener(const LISTENER& listener) { - return VoidListener(listener); + return LLVoidListener(listener); } -/** - * Yield control from a coroutine for one "mainloop" tick. If your coroutine - * runs without suspending for nontrivial time, sprinkle in calls to this - * function to avoid stalling the rest of the viewer processing. - */ -void yield(); +namespace LLEventDetail +{ + /** + * waitForEventOn() permits a coroutine to temporarily listen on an + * LLEventPump any number of times. We don't really want to have to ask + * the caller to label each such call with a distinct string; the whole + * point of waitForEventOn() is to present a nice sequential interface to + * the underlying LLEventPump-with-named-listeners machinery. So we'll use + * LLEventPump::inventName() to generate a distinct name for each + * temporary listener. On the other hand, because a given coroutine might + * call waitForEventOn() any number of times, we don't really want to + * consume an arbitrary number of generated inventName()s: that namespace, + * though large, is nonetheless finite. So we memoize an invented name for + * each distinct coroutine instance. + */ + std::string listenerNameForCoro(); + + /** + * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * parameter: + * + * * If path.isUndefined(), do nothing. + * * If path.isString(), @a dest is an LLSD map: store @a value + * into dest[path.asString()]. + * * If path.isInteger(), @a dest is an LLSD array: store @a + * value into dest[path.asInteger()]. + * * If path.isArray(), iteratively apply the rules above to step + * down through the structure of @a dest. The last array entry in @a + * path specifies the entry in the lowest-level structure in @a dest + * into which to store @a value. + * + * @note + * In the degenerate case in which @a path is an empty array, @a dest will + * @em become @a value rather than @em containing it. + */ + LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value); +} // namespace LLEventDetail /** * Post specified LLSD event on the specified LLEventPump, then wait for a @@ -168,13 +196,50 @@ LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); } -} // namespace llcoro - /// return type for two-pump variant of waitForEventOn() typedef std::pair LLEventWithID; -namespace llcoro +namespace LLEventDetail { + /** + * This helper is specifically for the two-pump version of waitForEventOn(). + * We use a single future object, but we want to listen on two pumps with it. + * Since we must still adapt from (the callable constructed by) + * boost::dcoroutines::make_callback() (void return) to provide an event + * listener (bool return), we've adapted LLVoidListener for the purpose. The + * basic idea is that we construct a distinct instance of WaitForEventOnHelper + * -- binding different instance data -- for each of the pumps. Then, when a + * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine + * that LLSD with its discriminator to feed the future object. + */ + template + class WaitForEventOnHelper + { + public: + WaitForEventOnHelper(const LISTENER& listener, int discriminator): + mListener(listener), + mDiscrim(discriminator) + {} + // this signature is required for an LLEventPump listener + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + mListener(LLEventWithID(event, mDiscrim)); + // don't swallow the event, let other listeners see it + return false; + } + private: + LISTENER mListener; + const int mDiscrim; + }; + + /// WaitForEventOnHelper type-inference helper + template + WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) + { + return WaitForEventOnHelper(listener, discriminator); + } +} // namespace LLEventDetail /** * This function waits for a reply on either of two specified LLEventPumps. @@ -243,8 +308,6 @@ waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& */ LLSD errorException(const LLEventWithID& result, const std::string& desc); -} // namespace llcoro - /** * Exception thrown by errorException(). We don't call this LLEventError * because it's not an error in event processing: rather, this exception @@ -265,17 +328,12 @@ private: LLSD mData; }; -namespace llcoro -{ - /** * Like errorException(), save that this trips a fatal error using LL_ERRS * rather than throwing an exception. */ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); -} // namespace llcoro - /** * Certain event APIs require the name of an LLEventPump on which they should * post results. While it works to invent a distinct name and let @@ -307,13 +365,13 @@ public: */ LLSD wait() { - return llcoro::waitForEventOn(mPump); + return ::waitForEventOn(mPump); } LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); + return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -352,19 +410,19 @@ public: /// waitForEventOn(either of our two LLEventPumps) LLEventWithID wait() { - return llcoro::waitForEventOn(mPump0, mPump1); + return waitForEventOn(mPump0, mPump1); } /// errorException(wait()) LLSD waitWithException() { - return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); + return errorException(wait(), std::string("Error event on ") + getName1()); } /// errorLog(wait()) LLSD waitWithLog() { - return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); + return errorLog(wait(), std::string("Error event on ") + getName1()); } LLEventWithID postAndWait(const LLSD& event, @@ -372,8 +430,8 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); + return postAndWait2(event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); } LLSD postAndWaitWithException(const LLSD& event, @@ -381,9 +439,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::errorException(postAndWait(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return errorException(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } LLSD postAndWaitWithLog(const LLSD& event, @@ -391,9 +449,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::errorLog(postAndWait(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return errorLog(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } private: diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 79bcfe58ed..da927038ab 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -86,8 +86,6 @@ #include "lleventcoro.h" #include "../test/debug.h" -using namespace llcoro; - /***************************************************************************** * from the banana.cpp example program borrowed for test<1>() *****************************************************************************/ @@ -261,7 +259,8 @@ namespace tut // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - boost::dcoroutines::coroutine coro(explicit_wait); + boost::dcoroutines::coroutine + coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. -- cgit v1.2.3 From 247eb0c9c3418c10be8f2a0e3c8116758efa702f Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 7 Jul 2015 19:41:27 +0100 Subject: Backout selfles merge 738255dbbfd679d9e615baab3398e5e345bbb3c5 --- indra/llcommon/llcoros.cpp | 86 +--- indra/llcommon/llcoros.h | 67 +-- indra/llcommon/lleventcoro.cpp | 113 +---- indra/llcommon/lleventcoro.h | 176 ++++++-- indra/llcommon/tests/lleventcoro_test.cpp | 712 +++++++++++++++--------------- 5 files changed, 563 insertions(+), 591 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 2d0c419ae0..baaddcaed1 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -34,65 +34,11 @@ // std headers // external library headers #include -#include // 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 -sCurrentSelf(no_cleanup); - -} // anonymous - -//static -LLCoros::coro::self& LLCoros::get_self() -{ - coro::self* current_self = sCurrentSelf.get(); - if (! current_self) - { - LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; - } - return *current_self; -} - -LLCoros::Suspending::Suspending(): - mSuspended(sCurrentSelf.get()) -{ - // For the duration of our time away from this coroutine, sCurrentSelf - // must NOT refer to this coroutine. - sCurrentSelf.reset(); -} - -LLCoros::Suspending::~Suspending() -{ - // Okay, we're back, reinstate previous value of sCurrentSelf. - sCurrentSelf.reset(mSuspended); -} - LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used @@ -114,7 +60,7 @@ bool LLCoros::cleanup(const LLSD&) // since last tick? if (mi->second->exited()) { - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << 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. @@ -148,7 +94,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const { if (mCoros.find(name) == mCoros.end()) { - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; return name; } } @@ -168,10 +114,10 @@ bool LLCoros::kill(const std::string& name) return true; } -std::string LLCoros::getName() const +std::string LLCoros::getNameByID(const void* self_id) const { - // Walk the existing coroutines, looking for the current one. - void* self_id = get_self().get_id(); + // 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) { namespace coro_private = boost::dcoroutines::detail; @@ -190,24 +136,10 @@ 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) -{ - sCurrentSelf.reset(&self); - callable(); - sCurrentSelf.reset(); -} - -} // anonymous - /***************************************************************************** * MUST BE LAST *****************************************************************************/ -// Turn off MSVC optimizations for just LLCoros::launch() -- see +// Turn off MSVC optimizations for just LLCoros::launchImpl() -- 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. @@ -218,13 +150,9 @@ void toplevel(LLCoros::coro::self& self, const LLCoros::callable_t& callable) #pragma optimize("", off) #endif // LL_MSVC -std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) +std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) { 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); - // Store it in our pointer map mCoros.insert(name, newCoro); /* Run the coroutine until its first wait, then return here */ (*newCoro)(std::nothrow); diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 3f58a17aa9..01ee11da1a 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -32,8 +32,10 @@ #include #include "llsingleton.h" #include -#include #include +#include +#include +#include #include /** @@ -78,8 +80,8 @@ class LL_COMMON_API LLCoros: public LLSingleton public: /// Canonical boost::dcoroutines::coroutine signature we use typedef boost::dcoroutines::coroutine coro; - /// Canonical callable type - typedef boost::function callable_t; + /// Canonical 'self' type + typedef coro::self self; /** * Create and start running a new coroutine with specified name. The name @@ -92,33 +94,39 @@ public: * { * public: * ... - * // Do NOT NOT NOT accept reference params! + * // Do NOT NOT NOT accept reference params other than 'self'! * // Pass by value only! - * void myCoroutineMethod(std::string, LLSD); + * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD); * ... * }; * ... * std::string name = LLCoros::instance().launch( - * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, + * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1, * "somestring", LLSD(17)); * @endcode * - * Your function/method can accept any parameters you want -- but ONLY BY - * VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See + * Your function/method must accept LLCoros::self& as its first parameter. + * It can accept any other parameters you want -- but ONLY BY VALUE! + * Other reference parameters are a BAD IDEA! You Have Been Warned. See * DEV-32777 comments for an explanation. * - * Pass a nullary callable. It works to directly pass a nullary free - * function (or static method); for all other cases use boost::bind(). Of - * course, for a non-static class method, the first parameter must be the - * class instance. Any other parameters should be passed via the bind() - * expression. + * Pass a callable that accepts the single LLCoros::self& parameter. It + * may work to pass a free function whose only parameter is 'self'; for + * all other cases use boost::bind(). Of course, for a non-static class + * method, the first parameter must be the class instance. Use the + * placeholder _1 for the 'self' parameter. Any other parameters should be + * passed via the bind() expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers * it with the tweaked name and runs it until its first wait. At that * point it returns the tweaked name. */ - std::string launch(const std::string& prefix, const callable_t& callable); + template + std::string launch(const std::string& prefix, const CALLABLE& callable) + { + return launchImpl(prefix, new coro(callable, mStackSize)); + } /** * Abort a running coroutine by name. Normally, when a coroutine either @@ -130,34 +138,27 @@ public: bool kill(const std::string& name); /** - * From within a coroutine, look up the (tweaked) name string by which - * this coroutine is registered. Returns the empty string if not found - * (e.g. if the coroutine was launched by hand rather than using - * LLCoros::launch()). + * From within a coroutine, pass its @c self object to look up the + * (tweaked) name string by which this coroutine is registered. Returns + * the empty string if not found (e.g. if the coroutine was launched by + * hand rather than using LLCoros::launch()). */ - std::string getName() const; - - /// get the current coro::self& for those who really really care - static 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 + template + std::string getName(const COROUTINE_SELF& self) const { - public: - Suspending(); - ~Suspending(); + return getNameByID(self.get_id()); + } - private: - coro::self* mSuspended; - }; + /// getName() by self.get_id() + std::string getNameByID(const void* self_id) const; /// for delayed initialization void setStackSize(S32 stacksize); private: - LLCoros(); friend class LLSingleton; + LLCoros(); + std::string launchImpl(const std::string& prefix, coro* newCoro); std::string generateDistinctName(const std::string& prefix) const; bool cleanup(const LLSD&); diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index be93e9c83b..81cc33fbba 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,24 +34,33 @@ #include // std headers // external library headers -#include -#include // other Linden headers #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoro() +std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) { - // If this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getName()); + // First, if this coroutine was launched by LLCoros::launch(), find that name. + std::string name(LLCoros::instance().getNameByID(self_id)); if (! name.empty()) { return name; } + // Apparently this coroutine wasn't launched by LLCoros::launch(). Check + // whether we have a memo for this self_id. + typedef std::map MapType; + static MapType memo; + MapType::const_iterator found = memo.find(self_id); + if (found != memo.end()) + { + // this coroutine instance has called us before, reuse same name + return found->second; + } // this is the first time we've been called for this coroutine instance name = LLEventPump::inventName("coro"); - LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '" + memo[self_id] = name; + LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '" << name << "'" << LL_ENDL; return name; } @@ -109,98 +118,6 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } -LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) -{ - // declare the future - boost::dcoroutines::future 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(LLEventDetail::listenerNameForCoro()); - LLTempBoundListener connection( - replyPump.getPump().listen(listenerName, - voidlistener(boost::dcoroutines::make_callback(future)))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If replyPumpNamePath is non-empty, store the replyPump name in the - // request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " posting to " << requestPump.getPump().getName() - << LL_ENDL; - - // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. - // << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " about to wait on LLEventPump " << replyPump.getPump().getName() - << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLSD value; - { - // instantiate Suspending to manage the "current" coroutine - LLCoros::Suspending suspended; - value = *future; - } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; - // returning should disconnect the connection - return value; -} - -LLEventWithID postAndWait2(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump0, - const LLEventPumpOrPumpName& replyPump1, - const LLSD& replyPump0NamePath, - const LLSD& replyPump1NamePath) -{ - // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro()); - LLTempBoundListener connection0( - replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value; - { - // instantiate Suspending to manage "current" coroutine - LLCoros::Suspending suspended; - value = *future; - } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections - return value; -} - LLSD errorException(const LLEventWithID& result, const std::string& desc) { // If the result arrived on the error pump (pump 1), instead of diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index daf9360a2e..abbeeaa373 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,6 +29,8 @@ #if ! defined(LL_LLEVENTCORO_H) #define LL_LLEVENTCORO_H +#include +#include #include #include #include @@ -100,6 +102,9 @@ LLVoidListener voidlistener(const LISTENER& listener) namespace LLEventDetail { + /// Implementation for listenerNameForCoro(), see below + LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id); + /** * waitForEventOn() permits a coroutine to temporarily listen on an * LLEventPump any number of times. We don't really want to have to ask @@ -111,9 +116,21 @@ namespace LLEventDetail * call waitForEventOn() any number of times, we don't really want to * consume an arbitrary number of generated inventName()s: that namespace, * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance. + * each distinct coroutine instance (each different 'self' object). We + * can't know the type of 'self', because it depends on the coroutine + * body's signature. So we cast its address to void*, looking for distinct + * pointer values. Yes, that means that an early coroutine could cache a + * value here, then be destroyed, only to be supplanted by a later + * coroutine (of the same or different type), and we'll end up + * "recognizing" the second one and reusing the listener name -- but + * that's okay, since it won't collide with any listener name used by the + * earlier coroutine since that earlier coroutine no longer exists. */ - std::string listenerNameForCoro(); + template + std::string listenerNameForCoro(COROUTINE_SELF& self) + { + return listenerNameForCoroImpl(self.get_id()); + } /** * Implement behavior described for postAndWait()'s @a replyPumpNamePath @@ -142,7 +159,7 @@ namespace LLEventDetail * convenience: the difference between this function and the sequence * @code * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(replyPump); + * LLSD reply = waitForEventOn(self, replyPump); * @endcode * is that the sequence above fails if the reply is posted immediately on * @a replyPump, that is, before requestPump.post() returns. In the @@ -184,16 +201,51 @@ namespace LLEventDetail * @a replyPumpNamePath specifies the entry in the lowest-level structure in * @a event into which to store replyPump.getName(). */ -LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()); +template +LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()) +{ + // declare the future + boost::dcoroutines::future future(self); + // make a callback that will assign a value to the future, and listen on + // the specified LLEventPump with that callback + std::string listenerName(LLEventDetail::listenerNameForCoro(self)); + LLTempBoundListener connection( + replyPump.getPump().listen(listenerName, + voidlistener(boost::dcoroutines::make_callback(future)))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If replyPumpNamePath is non-empty, store the replyPump name in the + // request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " posting to " << requestPump.getPump().getName() + << LL_ENDL; + + // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. + // << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getPump().getName() + << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLSD value(*future); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; +} /// Wait for the next event on the specified LLEventPump. Pass either the /// LLEventPump& or its string name. -inline -LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) +template +LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) { // This is now a convenience wrapper for postAndWait(). - return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); + return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); } /// return type for two-pump variant of waitForEventOn() @@ -261,7 +313,7 @@ namespace LLEventDetail * I'd have preferred to overload the name postAndWait() for both signatures. * But consider the following ambiguous call: * @code - * postAndWait(LLSD(), requestPump, replyPump, "someString"); + * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); * @endcode * "someString" could be converted to either LLSD (@a replyPumpNamePath for * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump @@ -270,29 +322,69 @@ namespace LLEventDetail * It seems less burdensome to write postAndWait2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -LLEventWithID postAndWait2(const LLSD& event, +template +LLEventWithID postAndWait2(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()); + const LLSD& replyPump1NamePath=LLSD()) +{ + // declare the future + boost::dcoroutines::future future(self); + // either callback will assign a value to this future; listen on + // each specified LLEventPump with a callback + std::string name(LLEventDetail::listenerNameForCoro(self)); + LLTempBoundListener connection0( + replyPump0.getPump().listen(name + "a", + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen(name + "b", + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If either replyPumpNamePath is non-empty, store the corresponding + // replyPump name in the request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLEventWithID value(*future); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; + // returning should disconnect both connections + return value; +} /** * Wait for the next event on either of two specified LLEventPumps. */ -inline +template LLEventWithID -waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +waitForEventOn(SELF& self, + const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { // This is now a convenience wrapper for postAndWait2(). - return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** * Helper for the two-pump variant of waitForEventOn(), e.g.: * * @code - * LLSD reply = errorException(waitForEventOn(replyPump, errorPump), + * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), * "error response from login.cgi"); * @endcode * @@ -362,16 +454,26 @@ public: /** * Wait for an event on this LLEventPump. + * + * @note + * The other major usage pattern we considered was to bind @c self at + * LLCoroEventPump construction time, which would avoid passing the + * parameter to each wait() call. But if we were going to bind @c self as + * a class member, we'd need to specify a class template parameter + * indicating its type. The big advantage of passing it to the wait() call + * is that the type can be implicit. */ - LLSD wait() + template + LLSD wait(SELF& self) { - return ::waitForEventOn(mPump); + return waitForEventOn(self, mPump); } - LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + template + LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); + return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); } private: @@ -407,49 +509,55 @@ public: /// request pump 1 LLEventPump& getPump1() { return mPump1; } - /// waitForEventOn(either of our two LLEventPumps) - LLEventWithID wait() + /// waitForEventOn(self, either of our two LLEventPumps) + template + LLEventWithID wait(SELF& self) { - return waitForEventOn(mPump0, mPump1); + return waitForEventOn(self, mPump0, mPump1); } - /// errorException(wait()) - LLSD waitWithException() + /// errorException(wait(self)) + template + LLSD waitWithException(SELF& self) { - return errorException(wait(), std::string("Error event on ") + getName1()); + return errorException(wait(self), std::string("Error event on ") + getName1()); } - /// errorLog(wait()) - LLSD waitWithLog() + /// errorLog(wait(self)) + template + LLSD waitWithLog(SELF& self) { - return errorLog(wait(), std::string("Error event on ") + getName1()); + return errorLog(wait(self), std::string("Error event on ") + getName1()); } - LLEventWithID postAndWait(const LLSD& event, + template + LLEventWithID postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(event, requestPump, mPump0, mPump1, + return postAndWait2(self, event, requestPump, mPump0, mPump1, replyPump0NamePath, replyPump1NamePath); } - LLSD postAndWaitWithException(const LLSD& event, + template + LLSD postAndWaitWithException(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(event, requestPump, + return errorException(postAndWait(self, event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } - LLSD postAndWaitWithLog(const LLSD& event, + template + LLSD postAndWaitWithLog(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(event, requestPump, + return errorLog(postAndWait(self, event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index da927038ab..2096807e53 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -82,7 +82,6 @@ #include "llevents.h" #include "tests/wrapllerrs.h" #include "stringize.h" -#include "llcoros.h" #include "lleventcoro.h" #include "../test/debug.h" @@ -122,6 +121,9 @@ typedef coroutine match_coroutine_type; /***************************************************************************** * Test helpers *****************************************************************************/ +// I suspect this will be typical of coroutines used in Linden software +typedef boost::dcoroutines::coroutine coroutine_type; + /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that /// distinguishes postAndWait() from calling post(), then calling @@ -160,7 +162,306 @@ private: *****************************************************************************/ namespace tut { - struct coroutine_data {}; + struct coroutine_data + { + // Define coroutine bodies as methods here so they can use ensure*() + + void explicit_wait(coroutine_type::self& self) + { + BEGIN + { + // ... do whatever preliminary stuff must happen ... + + // declare the future + boost::dcoroutines::future future(self); + // tell the future what to wait for + LLTempBoundListener connection( + LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); + ensure("Not yet", ! future); + // attempting to dereference ("resolve") the future causes the calling + // coroutine to wait for it + debug("about to wait"); + result = *future; + ensure("Got it", future); + } + END + } + + void waitForEventOn1(coroutine_type::self& self) + { + BEGIN + { + result = waitForEventOn(self, "source"); + } + END + } + + void waitForEventOn2(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = waitForEventOn(self, "reply", "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void postAndWait1(coroutine_type::self& self) + { + BEGIN + { + result = postAndWait(self, + LLSDMap("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + } + END + } + + void postAndWait2(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(self, + LLSDMap("value", 18), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void postAndWait2_1(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(self, + LLSDMap("value", 18)("fail", LLSD()), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void coroPump(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.wait(self); + } + END + } + + void coroPumpPost(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPump waiter; + result = waiter.postAndWait(self, LLSDMap("value", 17), + immediateAPI.getPump(), "reply"); + } + END + } + + void coroPumps(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + LLEventWithID pair(waiter.wait(self)); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsNoEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithException(self); + } + END + } + + void coroPumpsEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + try + { + result = waiter.waitWithException(self); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + + void coroPumpsNoLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithLog(self); + } + END + } + + void coroPumpsLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + WrapLLErrs capture; + try + { + result = waiter.waitWithLog(self); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + + void coroPumpsPost(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsPost_1(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair( + waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsPostNoEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithException(self, LLSDMap("value", 8), + immediateAPI.getPump(), "reply", "error"); + } + END + } + + void coroPumpsPostEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + try + { + result = waiter.postAndWaitWithException(self, + LLSDMap("value", 9)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + + void coroPumpsPostNoLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30), + immediateAPI.getPump(), "reply", "error"); + } + END + } + + void coroPumpsPostLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + WrapLLErrs capture; + try + { + result = waiter.postAndWaitWithLog(self, + LLSDMap("value", 31)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + + void ensure_done(coroutine_type& coro) + { + ensure("coroutine complete", ! coro); + } + + ImmediateAPI immediateAPI; + std::string replyName, errorName, threw; + LLSD result, errordata; + int which; + }; typedef test_group coroutine_group; typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); @@ -210,57 +511,16 @@ namespace tut ensure("done", ! matcher); } - // use static data so we can intersperse coroutine functions with the - // tests that engage them - ImmediateAPI immediateAPI; - std::string replyName, errorName, threw; - LLSD result, errordata; - int which; - - // reinit vars at the start of each test - void clear() - { - replyName.clear(); - errorName.clear(); - threw.clear(); - result = LLSD(); - errordata = LLSD(); - which = 0; - } - - void explicit_wait(boost::dcoroutines::coroutine::self& self) - { - BEGIN - { - // ... do whatever preliminary stuff must happen ... - - // declare the future - boost::dcoroutines::future future(self); - // tell the future what to wait for - LLTempBoundListener connection( - LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); - ensure("Not yet", ! future); - // attempting to dereference ("resolve") the future causes the calling - // coroutine to wait for it - debug("about to wait"); - result = *future; - ensure("Got it", future); - } - END - } - template<> template<> void object::test<2>() { - clear(); set_test_name("explicit_wait"); DEBUG; // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - boost::dcoroutines::coroutine - coro(explicit_wait); + coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. @@ -268,56 +528,37 @@ namespace tut // Satisfy the wait. LLEventPumps::instance().obtain("source").post("received"); // Now wait for the coroutine to complete. - ensure("coroutine complete", ! coro); + ensure_done(coro); // ensure the coroutine ran and woke up again with the intended result ensure_equals(result.asString(), "received"); } - void waitForEventOn1() - { - BEGIN - { - result = waitForEventOn("source"); - } - END - } - template<> template<> void object::test<3>() { - clear(); set_test_name("waitForEventOn1"); DEBUG; - LLCoros::instance().launch("test<3>", waitForEventOn1); + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain("source").post("received"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "received"); } - void waitForEventOn2() - { - BEGIN - { - LLEventWithID pair = waitForEventOn("reply", "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - template<> template<> void object::test<4>() { - clear(); set_test_name("waitForEventOn2 reply"); { DEBUG; - LLCoros::instance().launch("test<4>", waitForEventOn2); + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain("reply").post("received"); debug("back from send"); + ensure_done(coro); } ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); @@ -326,65 +567,43 @@ namespace tut template<> template<> void object::test<5>() { - clear(); set_test_name("waitForEventOn2 error"); DEBUG; - LLCoros::instance().launch("test<5>", waitForEventOn2); + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain("error").post("badness"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } - void coroPump() - { - BEGIN - { - LLCoroEventPump waiter; - replyName = waiter.getName(); - result = waiter.wait(); - } - END - } - template<> template<> void object::test<6>() { - clear(); set_test_name("coroPump"); DEBUG; - LLCoros::instance().launch("test<6>", coroPump); + coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "received"); } - void coroPumps() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - LLEventWithID pair(waiter.wait()); - result = pair.first; - which = pair.second; - } - END - } - template<> template<> void object::test<7>() { - clear(); set_test_name("coroPumps reply"); DEBUG; - LLCoros::instance().launch("test<7>", coroPumps); + coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); } @@ -392,389 +611,188 @@ namespace tut template<> template<> void object::test<8>() { - clear(); set_test_name("coroPumps error"); DEBUG; - LLCoros::instance().launch("test<8>", coroPumps); + coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } - void coroPumpsNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithException(); - } - END - } - template<> template<> void object::test<9>() { - clear(); set_test_name("coroPumpsNoEx"); DEBUG; - LLCoros::instance().launch("test<9>", coroPumpsNoEx); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "received"); } - void coroPumpsEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - try - { - result = waiter.waitWithException(); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - template<> template<> void object::test<10>() { - clear(); set_test_name("coroPumpsEx"); DEBUG; - LLCoros::instance().launch("test<10>", coroPumpsEx); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); + ensure_done(coro); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asString(), "badness"); } - void coroPumpsNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithLog(); - } - END - } - template<> template<> void object::test<11>() { - clear(); set_test_name("coroPumpsNoLog"); DEBUG; - LLCoros::instance().launch("test<11>", coroPumpsNoLog); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); + ensure_done(coro); ensure_equals(result.asString(), "received"); } - void coroPumpsLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - WrapLLErrs capture; - try - { - result = waiter.waitWithLog(); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - template<> template<> void object::test<12>() { - clear(); set_test_name("coroPumpsLog"); DEBUG; - LLCoros::instance().launch("test<12>", coroPumpsLog); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); + coro(std::nothrow); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); + ensure_done(coro); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "badness"); } - void postAndWait1() - { - BEGIN - { - result = postAndWait(LLSDMap("value", 17), // request event - immediateAPI.getPump(), // requestPump - "reply1", // replyPump - "reply"); // request["reply"] = name - } - END - } - template<> template<> void object::test<13>() { - clear(); set_test_name("postAndWait1"); DEBUG; - LLCoros::instance().launch("test<13>", postAndWait1); + coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 18); } - void postAndWait2() - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - template<> template<> void object::test<14>() { - clear(); set_test_name("postAndWait2"); DEBUG; - LLCoros::instance().launch("test<14>", postAndWait2); + coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 19); ensure_equals(which, 0); } - void postAndWait2_1() - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - template<> template<> void object::test<15>() { - clear(); set_test_name("postAndWait2_1"); DEBUG; - LLCoros::instance().launch("test<15>", postAndWait2_1); + coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 19); ensure_equals(which, 1); } - void coroPumpPost() - { - BEGIN - { - LLCoroEventPump waiter; - result = waiter.postAndWait(LLSDMap("value", 17), - immediateAPI.getPump(), "reply"); - } - END - } - template<> template<> void object::test<16>() { - clear(); set_test_name("coroPumpPost"); DEBUG; - LLCoros::instance().launch("test<16>", coroPumpPost); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 18); } - void coroPumpsPost() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - template<> template<> void object::test<17>() { - clear(); set_test_name("coroPumpsPost reply"); DEBUG; - LLCoros::instance().launch("test<17>", coroPumpsPost); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 0); } - void coroPumpsPost_1() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair( - waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - template<> template<> void object::test<18>() { - clear(); set_test_name("coroPumpsPost error"); DEBUG; - LLCoros::instance().launch("test<18>", coroPumpsPost_1); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 1); } - void coroPumpsPostNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithException(LLSDMap("value", 8), - immediateAPI.getPump(), "reply", "error"); - } - END - } - template<> template<> void object::test<19>() { - clear(); set_test_name("coroPumpsPostNoEx"); DEBUG; - LLCoros::instance().launch("test<19>", coroPumpsPostNoEx); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 9); } - void coroPumpsPostEx() - { - BEGIN - { - LLCoroEventPumps waiter; - try - { - result = waiter.postAndWaitWithException( - LLSDMap("value", 9)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - template<> template<> void object::test<20>() { - clear(); set_test_name("coroPumpsPostEx"); DEBUG; - LLCoros::instance().launch("test<20>", coroPumpsPostEx); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asInteger(), 10); } - void coroPumpsPostNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithLog(LLSDMap("value", 30), - immediateAPI.getPump(), "reply", "error"); - } - END - } - template<> template<> void object::test<21>() { - clear(); set_test_name("coroPumpsPostNoLog"); DEBUG; - LLCoros::instance().launch("test<21>", coroPumpsPostNoLog); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure_equals(result.asInteger(), 31); } - void coroPumpsPostLog() - { - BEGIN - { - LLCoroEventPumps waiter; - WrapLLErrs capture; - try - { - result = waiter.postAndWaitWithLog( - LLSDMap("value", 31)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - template<> template<> void object::test<22>() { - clear(); set_test_name("coroPumpsPostLog"); DEBUG; - LLCoros::instance().launch("test<22>", coroPumpsPostLog); + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); + coro(std::nothrow); + ensure_done(coro); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "32"); } -- cgit v1.2.3 From 0c915913fd05007bb3fb6cc84be89799b7d85ae5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 Jul 2015 16:01:15 -0400 Subject: MAINT-5351: Improve management of "current" coroutine information. Our first cut at tracking the "current" coroutine simply reset the pointer to NULL every time we context-switched away. But that strategy doesn't handle the case of coroutine A launching coroutine B. Introduce LLCoros::CoroData to track, among other things, the previous value of the current-coroutine pointer each time we switch into a coroutine. Restore THAT value when we switch back out. --- indra/llcommon/llcoros.cpp | 111 +++++++++++++++++++---------------------- indra/llcommon/llcoros.h | 57 +++++++++++++++++++-- indra/llcommon/lleventcoro.cpp | 4 +- 3 files changed, 106 insertions(+), 66 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 957fe034e1..d76401d01b 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -34,63 +34,47 @@ // std headers // external library headers #include -#include // 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 -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::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(coro_private::coroutine_accessor::get_impl(const_cast(*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 e478600f00..56eed8cafe 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -33,9 +33,16 @@ #include "llsingleton.h" #include #include +#include #include #include +// 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; + 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 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 CoroMap; CoroMap mCoros; + + // identify the current coroutine's CoroData + static boost::thread_specific_ptr 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 diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index ad02e139b8..85af0d9b9f 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 future(llcoro::get_self()); + boost::dcoroutines::future 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 future(llcoro::get_self()); + boost::dcoroutines::future 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()); -- cgit v1.2.3 From 6f9f89ee71751a0e88bbda91fef1a575a5a68ed9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 Jul 2015 16:47:07 -0400 Subject: Backed out changeset 6e1fa9518747: reapply 'selfless' changes --- indra/llcommon/llcoros.cpp | 10 +-- indra/llcommon/llcoros.h | 35 ++++---- indra/llcommon/lleventcoro.cpp | 127 ++++++++++++++++++++++++---- indra/llcommon/lleventcoro.h | 132 +++++++++--------------------- indra/llcommon/tests/lleventcoro_test.cpp | 5 +- 5 files changed, 175 insertions(+), 134 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 2d0c419ae0..957fe034e1 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -69,9 +69,9 @@ sCurrentSelf(no_cleanup); } // anonymous //static -LLCoros::coro::self& LLCoros::get_self() +LLCoros::coro::self& llcoro::get_self() { - coro::self* current_self = sCurrentSelf.get(); + LLCoros::coro::self* current_self = sCurrentSelf.get(); if (! current_self) { LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; @@ -79,7 +79,7 @@ LLCoros::coro::self& LLCoros::get_self() return *current_self; } -LLCoros::Suspending::Suspending(): +llcoro::Suspending::Suspending(): mSuspended(sCurrentSelf.get()) { // For the duration of our time away from this coroutine, sCurrentSelf @@ -87,7 +87,7 @@ LLCoros::Suspending::Suspending(): sCurrentSelf.reset(); } -LLCoros::Suspending::~Suspending() +llcoro::Suspending::~Suspending() { // Okay, we're back, reinstate previous value of sCurrentSelf. sCurrentSelf.reset(mSuspended); @@ -171,7 +171,7 @@ 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(); + void* self_id = llcoro::get_self().get_id(); for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) { namespace coro_private = boost::dcoroutines::detail; diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 3f58a17aa9..e478600f00 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -137,21 +137,6 @@ public: */ std::string getName() const; - /// get the current coro::self& for those who really really care - static 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 - { - public: - Suspending(); - ~Suspending(); - - private: - coro::self* mSuspended; - }; - /// for delayed initialization void setStackSize(S32 stacksize); @@ -166,4 +151,24 @@ private: CoroMap mCoros; }; +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 +{ +public: + Suspending(); + ~Suspending(); + +private: + LLCoros::coro::self* mSuspended; +}; + +} // namespace llcoro + #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index be93e9c83b..ad02e139b8 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -41,7 +41,23 @@ #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoro() +namespace +{ + +/** + * waitForEventOn() permits a coroutine to temporarily listen on an + * LLEventPump any number of times. We don't really want to have to ask + * the caller to label each such call with a distinct string; the whole + * point of waitForEventOn() is to present a nice sequential interface to + * the underlying LLEventPump-with-named-listeners machinery. So we'll use + * LLEventPump::inventName() to generate a distinct name for each + * temporary listener. On the other hand, because a given coroutine might + * call waitForEventOn() any number of times, we don't really want to + * consume an arbitrary number of generated inventName()s: that namespace, + * though large, is nonetheless finite. So we memoize an invented name for + * each distinct coroutine instance. + */ +std::string listenerNameForCoro() { // If this coroutine was launched by LLCoros::launch(), find that name. std::string name(LLCoros::instance().getName()); @@ -56,7 +72,25 @@ std::string LLEventDetail::listenerNameForCoro() return name; } -void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +/** + * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * parameter: + * + * * If path.isUndefined(), do nothing. + * * If path.isString(), @a dest is an LLSD map: store @a value + * into dest[path.asString()]. + * * If path.isInteger(), @a dest is an LLSD array: store @a + * value into dest[path.asInteger()]. + * * If path.isArray(), iteratively apply the rules above to step + * down through the structure of @a dest. The last array entry in @a + * path specifies the entry in the lowest-level structure in @a dest + * into which to store @a value. + * + * @note + * In the degenerate case in which @a path is an empty array, @a dest will + * @em become @a value rather than @em containing it. + */ +void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) { if (rawPath.isUndefined()) { @@ -109,14 +143,24 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } -LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +} // anonymous + +void llcoro::yield() +{ + // By viewer convention, we post an event on the "mainloop" LLEventPump + // each iteration of the main event-handling loop. So waiting for a single + // event on "mainloop" gives us a one-frame yield. + waitForEventOn("mainloop"); +} + +LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + boost::dcoroutines::future future(llcoro::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(LLEventDetail::listenerNameForCoro()); + std::string listenerName(listenerNameForCoro()); LLTempBoundListener connection( replyPump.getPump().listen(listenerName, voidlistener(boost::dcoroutines::make_callback(future)))); @@ -126,7 +170,7 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, // If replyPumpNamePath is non-empty, store the replyPump name in the // request event. LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -142,7 +186,7 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, LLSD value; { // instantiate Suspending to manage the "current" coroutine - LLCoros::Suspending suspended; + llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName @@ -151,6 +195,53 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, return value; } +namespace +{ + +/** + * This helper is specifically for the two-pump version of waitForEventOn(). + * We use a single future object, but we want to listen on two pumps with it. + * Since we must still adapt from (the callable constructed by) + * boost::dcoroutines::make_callback() (void return) to provide an event + * listener (bool return), we've adapted VoidListener for the purpose. The + * basic idea is that we construct a distinct instance of WaitForEventOnHelper + * -- binding different instance data -- for each of the pumps. Then, when a + * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine + * that LLSD with its discriminator to feed the future object. + */ +template +class WaitForEventOnHelper +{ +public: + WaitForEventOnHelper(const LISTENER& listener, int discriminator): + mListener(listener), + mDiscrim(discriminator) + {} + // this signature is required for an LLEventPump listener + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + mListener(LLEventWithID(event, mDiscrim)); + // don't swallow the event, let other listeners see it + return false; + } +private: + LISTENER mListener; + const int mDiscrim; +}; + +/// WaitForEventOnHelper type-inference helper +template +WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) +{ + return WaitForEventOnHelper(listener, discriminator); +} + +} // anonymous + +namespace llcoro +{ + LLEventWithID postAndWait2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, @@ -159,26 +250,26 @@ LLEventWithID postAndWait2(const LLSD& event, const LLSD& replyPump1NamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + boost::dcoroutines::future future(llcoro::get_self()); // either callback will assign a value to this future; listen on // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro()); + std::string name(listenerNameForCoro()); LLTempBoundListener connection0( replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); + wfeoh(boost::dcoroutines::make_callback(future), 0))); LLTempBoundListener connection1( replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); + wfeoh(boost::dcoroutines::make_callback(future), 1))); // skip the "post" part if requestPump is default-constructed if (requestPump) { // If either replyPumpNamePath is non-empty, store the corresponding // replyPump name in the request event. LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); + storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name << " posting to " << requestPump.getPump().getName() << ": " << modevent << LL_ENDL; @@ -191,7 +282,7 @@ LLEventWithID postAndWait2(const LLSD& event, LLEventWithID value; { // instantiate Suspending to manage "current" coroutine - LLCoros::Suspending suspended; + llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name @@ -227,3 +318,5 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc) // A simple return must therefore be from the reply pump (pump 0). return result.first; } + +} // namespace llcoro diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index daf9360a2e..e2ce4bb193 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -72,13 +72,16 @@ private: boost::optional mPump; }; +namespace llcoro +{ + /// This is an adapter for a signature like void LISTENER(const LLSD&), which /// isn't a valid LLEventPump listener: such listeners should return bool. template -class LLVoidListener +class VoidListener { public: - LLVoidListener(const LISTENER& listener): + VoidListener(const LISTENER& listener): mListener(listener) {} bool operator()(const LLSD& event) @@ -91,50 +94,19 @@ private: LISTENER mListener; }; -/// LLVoidListener helper function to infer the type of the LISTENER +/// VoidListener helper function to infer the type of the LISTENER template -LLVoidListener voidlistener(const LISTENER& listener) +VoidListener voidlistener(const LISTENER& listener) { - return LLVoidListener(listener); + return VoidListener(listener); } -namespace LLEventDetail -{ - /** - * waitForEventOn() permits a coroutine to temporarily listen on an - * LLEventPump any number of times. We don't really want to have to ask - * the caller to label each such call with a distinct string; the whole - * point of waitForEventOn() is to present a nice sequential interface to - * the underlying LLEventPump-with-named-listeners machinery. So we'll use - * LLEventPump::inventName() to generate a distinct name for each - * temporary listener. On the other hand, because a given coroutine might - * call waitForEventOn() any number of times, we don't really want to - * consume an arbitrary number of generated inventName()s: that namespace, - * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance. - */ - std::string listenerNameForCoro(); - - /** - * Implement behavior described for postAndWait()'s @a replyPumpNamePath - * parameter: - * - * * If path.isUndefined(), do nothing. - * * If path.isString(), @a dest is an LLSD map: store @a value - * into dest[path.asString()]. - * * If path.isInteger(), @a dest is an LLSD array: store @a - * value into dest[path.asInteger()]. - * * If path.isArray(), iteratively apply the rules above to step - * down through the structure of @a dest. The last array entry in @a - * path specifies the entry in the lowest-level structure in @a dest - * into which to store @a value. - * - * @note - * In the degenerate case in which @a path is an empty array, @a dest will - * @em become @a value rather than @em containing it. - */ - LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value); -} // namespace LLEventDetail +/** + * Yield control from a coroutine for one "mainloop" tick. If your coroutine + * runs without suspending for nontrivial time, sprinkle in calls to this + * function to avoid stalling the rest of the viewer processing. + */ +void yield(); /** * Post specified LLSD event on the specified LLEventPump, then wait for a @@ -196,50 +168,13 @@ LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); } +} // namespace llcoro + /// return type for two-pump variant of waitForEventOn() typedef std::pair LLEventWithID; -namespace LLEventDetail +namespace llcoro { - /** - * This helper is specifically for the two-pump version of waitForEventOn(). - * We use a single future object, but we want to listen on two pumps with it. - * Since we must still adapt from (the callable constructed by) - * boost::dcoroutines::make_callback() (void return) to provide an event - * listener (bool return), we've adapted LLVoidListener for the purpose. The - * basic idea is that we construct a distinct instance of WaitForEventOnHelper - * -- binding different instance data -- for each of the pumps. Then, when a - * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine - * that LLSD with its discriminator to feed the future object. - */ - template - class WaitForEventOnHelper - { - public: - WaitForEventOnHelper(const LISTENER& listener, int discriminator): - mListener(listener), - mDiscrim(discriminator) - {} - // this signature is required for an LLEventPump listener - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - mListener(LLEventWithID(event, mDiscrim)); - // don't swallow the event, let other listeners see it - return false; - } - private: - LISTENER mListener; - const int mDiscrim; - }; - - /// WaitForEventOnHelper type-inference helper - template - WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) - { - return WaitForEventOnHelper(listener, discriminator); - } -} // namespace LLEventDetail /** * This function waits for a reply on either of two specified LLEventPumps. @@ -308,6 +243,8 @@ waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& */ LLSD errorException(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Exception thrown by errorException(). We don't call this LLEventError * because it's not an error in event processing: rather, this exception @@ -328,12 +265,17 @@ private: LLSD mData; }; +namespace llcoro +{ + /** * Like errorException(), save that this trips a fatal error using LL_ERRS * rather than throwing an exception. */ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Certain event APIs require the name of an LLEventPump on which they should * post results. While it works to invent a distinct name and let @@ -365,13 +307,13 @@ public: */ LLSD wait() { - return ::waitForEventOn(mPump); + return llcoro::waitForEventOn(mPump); } LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -410,19 +352,19 @@ public: /// waitForEventOn(either of our two LLEventPumps) LLEventWithID wait() { - return waitForEventOn(mPump0, mPump1); + return llcoro::waitForEventOn(mPump0, mPump1); } /// errorException(wait()) LLSD waitWithException() { - return errorException(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); } /// errorLog(wait()) LLSD waitWithLog() { - return errorLog(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); } LLEventWithID postAndWait(const LLSD& event, @@ -430,8 +372,8 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); + return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); } LLSD postAndWaitWithException(const LLSD& event, @@ -439,9 +381,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorException(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } LLSD postAndWaitWithLog(const LLSD& event, @@ -449,9 +391,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorLog(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } private: diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index da927038ab..79bcfe58ed 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -86,6 +86,8 @@ #include "lleventcoro.h" #include "../test/debug.h" +using namespace llcoro; + /***************************************************************************** * from the banana.cpp example program borrowed for test<1>() *****************************************************************************/ @@ -259,8 +261,7 @@ namespace tut // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - boost::dcoroutines::coroutine - coro(explicit_wait); + boost::dcoroutines::coroutine coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. -- cgit v1.2.3 From efa9a0f99c17b2b937120bcad6e3d45944122ed9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 Jul 2015 19:30:10 -0400 Subject: Backed out changeset bab1000e1b2d: restore 'selfless' changes --- indra/llcommon/llcoros.cpp | 86 +++- indra/llcommon/llcoros.h | 67 ++- indra/llcommon/lleventcoro.cpp | 113 ++++- indra/llcommon/lleventcoro.h | 176 ++------ indra/llcommon/tests/lleventcoro_test.cpp | 712 +++++++++++++++--------------- 5 files changed, 591 insertions(+), 563 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index baaddcaed1..2d0c419ae0 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -34,11 +34,65 @@ // std headers // external library headers #include +#include // 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 +sCurrentSelf(no_cleanup); + +} // anonymous + +//static +LLCoros::coro::self& LLCoros::get_self() +{ + coro::self* current_self = sCurrentSelf.get(); + if (! current_self) + { + LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; + } + return *current_self; +} + +LLCoros::Suspending::Suspending(): + mSuspended(sCurrentSelf.get()) +{ + // For the duration of our time away from this coroutine, sCurrentSelf + // must NOT refer to this coroutine. + sCurrentSelf.reset(); +} + +LLCoros::Suspending::~Suspending() +{ + // Okay, we're back, reinstate previous value of sCurrentSelf. + sCurrentSelf.reset(mSuspended); +} + LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used @@ -60,7 +114,7 @@ bool LLCoros::cleanup(const LLSD&) // since last tick? if (mi->second->exited()) { - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << 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. @@ -94,7 +148,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const { if (mCoros.find(name) == mCoros.end()) { - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; return name; } } @@ -114,10 +168,10 @@ 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. + // 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) { namespace coro_private = boost::dcoroutines::detail; @@ -136,10 +190,24 @@ 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) +{ + sCurrentSelf.reset(&self); + callable(); + sCurrentSelf.reset(); +} + +} // anonymous + /***************************************************************************** * 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. @@ -150,9 +218,13 @@ void LLCoros::setStackSize(S32 stacksize) #pragma optimize("", off) #endif // LL_MSVC -std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) +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); + // Store it in our pointer map mCoros.insert(name, newCoro); /* Run the coroutine until its first wait, then return here */ (*newCoro)(std::nothrow); diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 01ee11da1a..3f58a17aa9 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -32,10 +32,8 @@ #include #include "llsingleton.h" #include +#include #include -#include -#include -#include #include /** @@ -80,8 +78,8 @@ class LL_COMMON_API LLCoros: public LLSingleton public: /// Canonical boost::dcoroutines::coroutine signature we use typedef boost::dcoroutines::coroutine coro; - /// Canonical 'self' type - typedef coro::self self; + /// Canonical callable type + typedef boost::function callable_t; /** * Create and start running a new coroutine with specified name. The name @@ -94,39 +92,33 @@ public: * { * public: * ... - * // Do NOT NOT NOT accept reference params other than 'self'! + * // Do NOT NOT NOT accept reference params! * // Pass by value only! - * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD); + * void myCoroutineMethod(std::string, LLSD); * ... * }; * ... * std::string name = LLCoros::instance().launch( - * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1, + * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, * "somestring", LLSD(17)); * @endcode * - * Your function/method must accept LLCoros::self& as its first parameter. - * It can accept any other parameters you want -- but ONLY BY VALUE! - * Other reference parameters are a BAD IDEA! You Have Been Warned. See + * Your function/method can accept any parameters you want -- but ONLY BY + * VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See * DEV-32777 comments for an explanation. * - * Pass a callable that accepts the single LLCoros::self& parameter. It - * may work to pass a free function whose only parameter is 'self'; for - * all other cases use boost::bind(). Of course, for a non-static class - * method, the first parameter must be the class instance. Use the - * placeholder _1 for the 'self' parameter. Any other parameters should be - * passed via the bind() expression. + * Pass a nullary callable. It works to directly pass a nullary free + * function (or static method); for all other cases use boost::bind(). Of + * course, for a non-static class method, the first parameter must be the + * class instance. Any other parameters should be passed via the bind() + * expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers * it with the tweaked name and runs it until its first wait. At that * point it returns the tweaked name. */ - template - std::string launch(const std::string& prefix, const CALLABLE& callable) - { - return launchImpl(prefix, new coro(callable, mStackSize)); - } + std::string launch(const std::string& prefix, const callable_t& callable); /** * Abort a running coroutine by name. Normally, when a coroutine either @@ -138,27 +130,34 @@ public: bool kill(const std::string& name); /** - * From within a coroutine, pass its @c self object to look up the - * (tweaked) name string by which this coroutine is registered. Returns - * the empty string if not found (e.g. if the coroutine was launched by - * hand rather than using LLCoros::launch()). + * From within a coroutine, look up the (tweaked) name string by which + * this coroutine is registered. Returns the empty string if not found + * (e.g. if the coroutine was launched by hand rather than using + * LLCoros::launch()). */ - template - std::string getName(const COROUTINE_SELF& self) const + std::string getName() const; + + /// get the current coro::self& for those who really really care + static 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 { - return getNameByID(self.get_id()); - } + public: + Suspending(); + ~Suspending(); - /// getName() by self.get_id() - std::string getNameByID(const void* self_id) const; + private: + coro::self* mSuspended; + }; /// for delayed initialization void setStackSize(S32 stacksize); private: - friend class LLSingleton; LLCoros(); - std::string launchImpl(const std::string& prefix, coro* newCoro); + friend class LLSingleton; std::string generateDistinctName(const std::string& prefix) const; bool cleanup(const LLSD&); diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 81cc33fbba..be93e9c83b 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,33 +34,24 @@ #include // std headers // external library headers +#include +#include // other Linden headers #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) +std::string LLEventDetail::listenerNameForCoro() { - // First, if this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getNameByID(self_id)); + // If this coroutine was launched by LLCoros::launch(), find that name. + std::string name(LLCoros::instance().getName()); if (! name.empty()) { return name; } - // Apparently this coroutine wasn't launched by LLCoros::launch(). Check - // whether we have a memo for this self_id. - typedef std::map MapType; - static MapType memo; - MapType::const_iterator found = memo.find(self_id); - if (found != memo.end()) - { - // this coroutine instance has called us before, reuse same name - return found->second; - } // this is the first time we've been called for this coroutine instance name = LLEventPump::inventName("coro"); - memo[self_id] = name; - LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '" + LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '" << name << "'" << LL_ENDL; return name; } @@ -118,6 +109,98 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +{ + // declare the future + boost::dcoroutines::future 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(LLEventDetail::listenerNameForCoro()); + LLTempBoundListener connection( + replyPump.getPump().listen(listenerName, + voidlistener(boost::dcoroutines::make_callback(future)))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If replyPumpNamePath is non-empty, store the replyPump name in the + // request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " posting to " << requestPump.getPump().getName() + << LL_ENDL; + + // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. + // << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getPump().getName() + << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLSD value; + { + // instantiate Suspending to manage the "current" coroutine + LLCoros::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; +} + +LLEventWithID postAndWait2(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump0, + const LLEventPumpOrPumpName& replyPump1, + const LLSD& replyPump0NamePath, + const LLSD& replyPump1NamePath) +{ + // declare the future + boost::dcoroutines::future future(LLCoros::get_self()); + // either callback will assign a value to this future; listen on + // each specified LLEventPump with a callback + std::string name(LLEventDetail::listenerNameForCoro()); + LLTempBoundListener connection0( + replyPump0.getPump().listen(name + "a", + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen(name + "b", + LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If either replyPumpNamePath is non-empty, store the corresponding + // replyPump name in the request event. + LLSD modevent(event); + LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLEventWithID value; + { + // instantiate Suspending to manage "current" coroutine + LLCoros::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; + // returning should disconnect both connections + return value; +} + LLSD errorException(const LLEventWithID& result, const std::string& desc) { // If the result arrived on the error pump (pump 1), instead of diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index abbeeaa373..daf9360a2e 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,8 +29,6 @@ #if ! defined(LL_LLEVENTCORO_H) #define LL_LLEVENTCORO_H -#include -#include #include #include #include @@ -102,9 +100,6 @@ LLVoidListener voidlistener(const LISTENER& listener) namespace LLEventDetail { - /// Implementation for listenerNameForCoro(), see below - LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id); - /** * waitForEventOn() permits a coroutine to temporarily listen on an * LLEventPump any number of times. We don't really want to have to ask @@ -116,21 +111,9 @@ namespace LLEventDetail * call waitForEventOn() any number of times, we don't really want to * consume an arbitrary number of generated inventName()s: that namespace, * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance (each different 'self' object). We - * can't know the type of 'self', because it depends on the coroutine - * body's signature. So we cast its address to void*, looking for distinct - * pointer values. Yes, that means that an early coroutine could cache a - * value here, then be destroyed, only to be supplanted by a later - * coroutine (of the same or different type), and we'll end up - * "recognizing" the second one and reusing the listener name -- but - * that's okay, since it won't collide with any listener name used by the - * earlier coroutine since that earlier coroutine no longer exists. + * each distinct coroutine instance. */ - template - std::string listenerNameForCoro(COROUTINE_SELF& self) - { - return listenerNameForCoroImpl(self.get_id()); - } + std::string listenerNameForCoro(); /** * Implement behavior described for postAndWait()'s @a replyPumpNamePath @@ -159,7 +142,7 @@ namespace LLEventDetail * convenience: the difference between this function and the sequence * @code * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(self, replyPump); + * LLSD reply = waitForEventOn(replyPump); * @endcode * is that the sequence above fails if the reply is posted immediately on * @a replyPump, that is, before requestPump.post() returns. In the @@ -201,51 +184,16 @@ namespace LLEventDetail * @a replyPumpNamePath specifies the entry in the lowest-level structure in * @a event into which to store replyPump.getName(). */ -template -LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()) -{ - // declare the future - boost::dcoroutines::future future(self); - // make a callback that will assign a value to the future, and listen on - // the specified LLEventPump with that callback - std::string listenerName(LLEventDetail::listenerNameForCoro(self)); - LLTempBoundListener connection( - replyPump.getPump().listen(listenerName, - voidlistener(boost::dcoroutines::make_callback(future)))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If replyPumpNamePath is non-empty, store the replyPump name in the - // request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " posting to " << requestPump.getPump().getName() - << LL_ENDL; - - // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. - // << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " about to wait on LLEventPump " << replyPump.getPump().getName() - << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLSD value(*future); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; - // returning should disconnect the connection - return value; -} +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()); /// Wait for the next event on the specified LLEventPump. Pass either the /// LLEventPump& or its string name. -template -LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) +inline +LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) { // This is now a convenience wrapper for postAndWait(). - return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); + return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); } /// return type for two-pump variant of waitForEventOn() @@ -313,7 +261,7 @@ namespace LLEventDetail * I'd have preferred to overload the name postAndWait() for both signatures. * But consider the following ambiguous call: * @code - * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); + * postAndWait(LLSD(), requestPump, replyPump, "someString"); * @endcode * "someString" could be converted to either LLSD (@a replyPumpNamePath for * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump @@ -322,69 +270,29 @@ namespace LLEventDetail * It seems less burdensome to write postAndWait2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -template -LLEventWithID postAndWait2(SELF& self, const LLSD& event, +LLEventWithID postAndWait2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) -{ - // declare the future - boost::dcoroutines::future future(self); - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro(self)); - LLTempBoundListener connection0( - replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value(*future); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections - return value; -} + const LLSD& replyPump1NamePath=LLSD()); /** * Wait for the next event on either of two specified LLEventPumps. */ -template +inline LLEventWithID -waitForEventOn(SELF& self, - const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { // This is now a convenience wrapper for postAndWait2(). - return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** * Helper for the two-pump variant of waitForEventOn(), e.g.: * * @code - * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), + * LLSD reply = errorException(waitForEventOn(replyPump, errorPump), * "error response from login.cgi"); * @endcode * @@ -454,26 +362,16 @@ public: /** * Wait for an event on this LLEventPump. - * - * @note - * The other major usage pattern we considered was to bind @c self at - * LLCoroEventPump construction time, which would avoid passing the - * parameter to each wait() call. But if we were going to bind @c self as - * a class member, we'd need to specify a class template parameter - * indicating its type. The big advantage of passing it to the wait() call - * is that the type can be implicit. */ - template - LLSD wait(SELF& self) + LLSD wait() { - return waitForEventOn(self, mPump); + return ::waitForEventOn(mPump); } - template - LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); + return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -509,55 +407,49 @@ public: /// request pump 1 LLEventPump& getPump1() { return mPump1; } - /// waitForEventOn(self, either of our two LLEventPumps) - template - LLEventWithID wait(SELF& self) + /// waitForEventOn(either of our two LLEventPumps) + LLEventWithID wait() { - return waitForEventOn(self, mPump0, mPump1); + return waitForEventOn(mPump0, mPump1); } - /// errorException(wait(self)) - template - LLSD waitWithException(SELF& self) + /// errorException(wait()) + LLSD waitWithException() { - return errorException(wait(self), std::string("Error event on ") + getName1()); + return errorException(wait(), std::string("Error event on ") + getName1()); } - /// errorLog(wait(self)) - template - LLSD waitWithLog(SELF& self) + /// errorLog(wait()) + LLSD waitWithLog() { - return errorLog(wait(self), std::string("Error event on ") + getName1()); + return errorLog(wait(), std::string("Error event on ") + getName1()); } - template - LLEventWithID postAndWait(SELF& self, const LLSD& event, + LLEventWithID postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(self, event, requestPump, mPump0, mPump1, + return postAndWait2(event, requestPump, mPump0, mPump1, replyPump0NamePath, replyPump1NamePath); } - template - LLSD postAndWaitWithException(SELF& self, const LLSD& event, + LLSD postAndWaitWithException(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(self, event, requestPump, + return errorException(postAndWait(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } - template - LLSD postAndWaitWithLog(SELF& self, const LLSD& event, + LLSD postAndWaitWithLog(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(self, event, requestPump, + return errorLog(postAndWait(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 2096807e53..da927038ab 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -82,6 +82,7 @@ #include "llevents.h" #include "tests/wrapllerrs.h" #include "stringize.h" +#include "llcoros.h" #include "lleventcoro.h" #include "../test/debug.h" @@ -121,9 +122,6 @@ typedef coroutine match_coroutine_type; /***************************************************************************** * Test helpers *****************************************************************************/ -// I suspect this will be typical of coroutines used in Linden software -typedef boost::dcoroutines::coroutine coroutine_type; - /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that /// distinguishes postAndWait() from calling post(), then calling @@ -162,306 +160,7 @@ private: *****************************************************************************/ namespace tut { - struct coroutine_data - { - // Define coroutine bodies as methods here so they can use ensure*() - - void explicit_wait(coroutine_type::self& self) - { - BEGIN - { - // ... do whatever preliminary stuff must happen ... - - // declare the future - boost::dcoroutines::future future(self); - // tell the future what to wait for - LLTempBoundListener connection( - LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); - ensure("Not yet", ! future); - // attempting to dereference ("resolve") the future causes the calling - // coroutine to wait for it - debug("about to wait"); - result = *future; - ensure("Got it", future); - } - END - } - - void waitForEventOn1(coroutine_type::self& self) - { - BEGIN - { - result = waitForEventOn(self, "source"); - } - END - } - - void waitForEventOn2(coroutine_type::self& self) - { - BEGIN - { - LLEventWithID pair = waitForEventOn(self, "reply", "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void postAndWait1(coroutine_type::self& self) - { - BEGIN - { - result = postAndWait(self, - LLSDMap("value", 17), // request event - immediateAPI.getPump(), // requestPump - "reply1", // replyPump - "reply"); // request["reply"] = name - } - END - } - - void postAndWait2(coroutine_type::self& self) - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(self, - LLSDMap("value", 18), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void postAndWait2_1(coroutine_type::self& self) - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(self, - LLSDMap("value", 18)("fail", LLSD()), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void coroPump(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPump waiter; - replyName = waiter.getName(); - result = waiter.wait(self); - } - END - } - - void coroPumpPost(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPump waiter; - result = waiter.postAndWait(self, LLSDMap("value", 17), - immediateAPI.getPump(), "reply"); - } - END - } - - void coroPumps(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - LLEventWithID pair(waiter.wait(self)); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsNoEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithException(self); - } - END - } - - void coroPumpsEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - try - { - result = waiter.waitWithException(self); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - void coroPumpsNoLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithLog(self); - } - END - } - - void coroPumpsLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - WrapLLErrs capture; - try - { - result = waiter.waitWithLog(self); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - - void coroPumpsPost(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsPost_1(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair( - waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsPostNoEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithException(self, LLSDMap("value", 8), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - void coroPumpsPostEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - try - { - result = waiter.postAndWaitWithException(self, - LLSDMap("value", 9)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - void coroPumpsPostNoLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - void coroPumpsPostLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - WrapLLErrs capture; - try - { - result = waiter.postAndWaitWithLog(self, - LLSDMap("value", 31)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - - void ensure_done(coroutine_type& coro) - { - ensure("coroutine complete", ! coro); - } - - ImmediateAPI immediateAPI; - std::string replyName, errorName, threw; - LLSD result, errordata; - int which; - }; + struct coroutine_data {}; typedef test_group coroutine_group; typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); @@ -511,16 +210,57 @@ namespace tut ensure("done", ! matcher); } + // use static data so we can intersperse coroutine functions with the + // tests that engage them + ImmediateAPI immediateAPI; + std::string replyName, errorName, threw; + LLSD result, errordata; + int which; + + // reinit vars at the start of each test + void clear() + { + replyName.clear(); + errorName.clear(); + threw.clear(); + result = LLSD(); + errordata = LLSD(); + which = 0; + } + + void explicit_wait(boost::dcoroutines::coroutine::self& self) + { + BEGIN + { + // ... do whatever preliminary stuff must happen ... + + // declare the future + boost::dcoroutines::future future(self); + // tell the future what to wait for + LLTempBoundListener connection( + LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); + ensure("Not yet", ! future); + // attempting to dereference ("resolve") the future causes the calling + // coroutine to wait for it + debug("about to wait"); + result = *future; + ensure("Got it", future); + } + END + } + template<> template<> void object::test<2>() { + clear(); set_test_name("explicit_wait"); DEBUG; // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + boost::dcoroutines::coroutine + coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. @@ -528,37 +268,56 @@ namespace tut // Satisfy the wait. LLEventPumps::instance().obtain("source").post("received"); // Now wait for the coroutine to complete. - ensure_done(coro); + ensure("coroutine complete", ! coro); // ensure the coroutine ran and woke up again with the intended result ensure_equals(result.asString(), "received"); } + void waitForEventOn1() + { + BEGIN + { + result = waitForEventOn("source"); + } + END + } + template<> template<> void object::test<3>() { + clear(); set_test_name("waitForEventOn1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<3>", waitForEventOn1); debug("about to send"); LLEventPumps::instance().obtain("source").post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void waitForEventOn2() + { + BEGIN + { + LLEventWithID pair = waitForEventOn("reply", "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<4>() { + clear(); set_test_name("waitForEventOn2 reply"); { DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<4>", waitForEventOn2); debug("about to send"); LLEventPumps::instance().obtain("reply").post("received"); debug("back from send"); - ensure_done(coro); } ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); @@ -567,43 +326,65 @@ namespace tut template<> template<> void object::test<5>() { + clear(); set_test_name("waitForEventOn2 error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<5>", waitForEventOn2); debug("about to send"); LLEventPumps::instance().obtain("error").post("badness"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } + void coroPump() + { + BEGIN + { + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.wait(); + } + END + } + template<> template<> void object::test<6>() { + clear(); set_test_name("coroPump"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<6>", coroPump); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void coroPumps() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + LLEventWithID pair(waiter.wait()); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<7>() { + clear(); set_test_name("coroPumps reply"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<7>", coroPumps); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); } @@ -611,188 +392,389 @@ namespace tut template<> template<> void object::test<8>() { + clear(); set_test_name("coroPumps error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<8>", coroPumps); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } + void coroPumpsNoEx() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithException(); + } + END + } + template<> template<> void object::test<9>() { + clear(); set_test_name("coroPumpsNoEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<9>", coroPumpsNoEx); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void coroPumpsEx() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + try + { + result = waiter.waitWithException(); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + template<> template<> void object::test<10>() { + clear(); set_test_name("coroPumpsEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<10>", coroPumpsEx); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asString(), "badness"); } + void coroPumpsNoLog() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithLog(); + } + END + } + template<> template<> void object::test<11>() { + clear(); set_test_name("coroPumpsNoLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<11>", coroPumpsNoLog); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void coroPumpsLog() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + WrapLLErrs capture; + try + { + result = waiter.waitWithLog(); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + template<> template<> void object::test<12>() { + clear(); set_test_name("coroPumpsLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<12>", coroPumpsLog); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "badness"); } + void postAndWait1() + { + BEGIN + { + result = postAndWait(LLSDMap("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + } + END + } + template<> template<> void object::test<13>() { + clear(); set_test_name("postAndWait1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<13>", postAndWait1); ensure_equals(result.asInteger(), 18); } + void postAndWait2() + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<14>() { + clear(); set_test_name("postAndWait2"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<14>", postAndWait2); ensure_equals(result.asInteger(), 19); ensure_equals(which, 0); } + void postAndWait2_1() + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<15>() { + clear(); set_test_name("postAndWait2_1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<15>", postAndWait2_1); ensure_equals(result.asInteger(), 19); ensure_equals(which, 1); } + void coroPumpPost() + { + BEGIN + { + LLCoroEventPump waiter; + result = waiter.postAndWait(LLSDMap("value", 17), + immediateAPI.getPump(), "reply"); + } + END + } + template<> template<> void object::test<16>() { + clear(); set_test_name("coroPumpPost"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<16>", coroPumpPost); ensure_equals(result.asInteger(), 18); } + void coroPumpsPost() + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<17>() { + clear(); set_test_name("coroPumpsPost reply"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<17>", coroPumpsPost); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 0); } + void coroPumpsPost_1() + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair( + waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<18>() { + clear(); set_test_name("coroPumpsPost error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<18>", coroPumpsPost_1); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 1); } + void coroPumpsPostNoEx() + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithException(LLSDMap("value", 8), + immediateAPI.getPump(), "reply", "error"); + } + END + } + template<> template<> void object::test<19>() { + clear(); set_test_name("coroPumpsPostNoEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<19>", coroPumpsPostNoEx); ensure_equals(result.asInteger(), 9); } + void coroPumpsPostEx() + { + BEGIN + { + LLCoroEventPumps waiter; + try + { + result = waiter.postAndWaitWithException( + LLSDMap("value", 9)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + template<> template<> void object::test<20>() { + clear(); set_test_name("coroPumpsPostEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<20>", coroPumpsPostEx); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asInteger(), 10); } + void coroPumpsPostNoLog() + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithLog(LLSDMap("value", 30), + immediateAPI.getPump(), "reply", "error"); + } + END + } + template<> template<> void object::test<21>() { + clear(); set_test_name("coroPumpsPostNoLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<21>", coroPumpsPostNoLog); ensure_equals(result.asInteger(), 31); } + void coroPumpsPostLog() + { + BEGIN + { + LLCoroEventPumps waiter; + WrapLLErrs capture; + try + { + result = waiter.postAndWaitWithLog( + LLSDMap("value", 31)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + template<> template<> void object::test<22>() { + clear(); set_test_name("coroPumpsPostLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<22>", coroPumpsPostLog); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "32"); } -- cgit v1.2.3 From 68f42e4e102bd578386aeec3485233e332cbdcff Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 Jul 2015 19:44:47 -0400 Subject: MAINT-5351: Finish cleaning up messy merge from backing out backout --- indra/llcommon/llcoros.cpp | 4 +- indra/llcommon/llcoros.h | 12 --- indra/llcommon/lleventcoro.cpp | 127 +++++++++++++----------------- indra/llcommon/lleventcoro.h | 18 ++--- indra/llcommon/tests/lleventcoro_test.cpp | 3 +- 5 files changed, 67 insertions(+), 97 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 6cca5e7c60..d76401d01b 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -61,7 +61,7 @@ LLCoros::coro::self& LLCoros::get_self() return *current->mSelf; } -LLCoros::Suspending::Suspending(): +llcoro::Suspending::Suspending(): mSuspended(LLCoros::sCurrentCoro.get()) { // Revert mCurrentCoro to the value it had at the moment we last switched @@ -69,7 +69,7 @@ LLCoros::Suspending::Suspending(): LLCoros::sCurrentCoro.reset(mSuspended->mPrev); } -LLCoros::Suspending::~Suspending() +llcoro::Suspending::~Suspending() { // Okay, we're back, update our mPrev mSuspended->mPrev = LLCoros::sCurrentCoro.get(); diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 05bd87dc3c..56eed8cafe 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -144,18 +144,6 @@ public: */ std::string getName() const; - /// get the current coro::self& for those who really really care - static 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 - public: - Suspending(); - ~Suspending(); - private: - coro::self* mSuspended; - }; /// for delayed initialization void setStackSize(S32 stacksize); diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index a37b87f085..66cc7cada0 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -41,7 +41,7 @@ #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoro() +namespace { /** @@ -143,14 +143,24 @@ void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) *pdest = value; } -LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, +} // anonymous + +void llcoro::yield() +{ + // By viewer convention, we post an event on the "mainloop" LLEventPump + // each iteration of the main event-handling loop. So waiting for a single + // event on "mainloop" gives us a one-frame yield. + waitForEventOn("mainloop"); +} + +LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future boost::dcoroutines::future 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(LLEventDetail::listenerNameForCoro()); + std::string listenerName(listenerNameForCoro()); LLTempBoundListener connection( replyPump.getPump().listen(listenerName, voidlistener(boost::dcoroutines::make_callback(future)))); @@ -160,7 +170,7 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, // If replyPumpNamePath is non-empty, store the replyPump name in the // request event. LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -176,7 +186,7 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, LLSD value; { // instantiate Suspending to manage the "current" coroutine - LLCoros::Suspending suspended; + llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName @@ -185,72 +195,6 @@ LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, return value; } -LLEventWithID postAndWait2(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump0, - const LLEventPumpOrPumpName& replyPump1, - const LLSD& replyPump0NamePath, - const LLSD& replyPump1NamePath) -{ - // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro()); - LLTempBoundListener connection0( - replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value; - { - // instantiate Suspending to manage "current" coroutine - LLCoros::Suspending suspended; - value = *future; - } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections - return value; -} - -} // anonymous - -void llcoro::yield() -{ - // By viewer convention, we post an event on the "mainloop" LLEventPump - // each iteration of the main event-handling loop. So waiting for a single - // event on "mainloop" gives us a one-frame yield. - waitForEventOn("mainloop"); -} - -LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) - boost::dcoroutines::future future(llcoro::get_self()); - std::string listenerName(listenerNameForCoro()); - storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - llcoro::Suspending suspended; namespace { @@ -298,15 +242,56 @@ WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator namespace llcoro { +LLEventWithID postAndWait2(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump0, + const LLEventPumpOrPumpName& replyPump1, + const LLSD& replyPump0NamePath, + const LLSD& replyPump1NamePath) +{ + // declare the future boost::dcoroutines::future 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()); + LLTempBoundListener connection0( + replyPump0.getPump().listen(name + "a", wfeoh(boost::dcoroutines::make_callback(future), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen(name + "b", wfeoh(boost::dcoroutines::make_callback(future), 1))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If either replyPumpNamePath is non-empty, store the corresponding + // replyPump name in the request event. + LLSD modevent(event); storeToLLSDPath(modevent, replyPump0NamePath, replyPump0.getPump().getName()); storeToLLSDPath(modevent, replyPump1NamePath, replyPump1.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLEventWithID value; + { + // instantiate Suspending to manage "current" coroutine llcoro::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; + // returning should disconnect both connections + return value; +} + LLSD errorException(const LLEventWithID& result, const std::string& desc) { // If the result arrived on the error pump (pump 1), instead of diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 20fc422a68..e2ce4bb193 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -101,8 +101,6 @@ VoidListener voidlistener(const LISTENER& listener) return VoidListener(listener); } - * each distinct coroutine instance. - std::string listenerNameForCoro(); /** * Yield control from a coroutine for one "mainloop" tick. If your coroutine * runs without suspending for nontrivial time, sprinkle in calls to this @@ -309,13 +307,13 @@ public: */ LLSD wait() { - return ::waitForEventOn(mPump); + return llcoro::waitForEventOn(mPump); } LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -354,19 +352,19 @@ public: /// waitForEventOn(either of our two LLEventPumps) LLEventWithID wait() { - return waitForEventOn(mPump0, mPump1); + return llcoro::waitForEventOn(mPump0, mPump1); } /// errorException(wait()) LLSD waitWithException() { - return errorException(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); } /// errorLog(wait()) LLSD waitWithLog() { - return errorLog(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); } LLEventWithID postAndWait(const LLSD& event, @@ -374,7 +372,7 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(event, requestPump, mPump0, mPump1, + return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, replyPump0NamePath, replyPump1NamePath); } @@ -383,7 +381,7 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(event, requestPump, + return llcoro::errorException(postAndWait(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } @@ -393,7 +391,7 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(event, requestPump, + return llcoro::errorLog(postAndWait(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index b8d9d8002f..00be5035f2 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -264,8 +264,7 @@ namespace tut // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - boost::dcoroutines::coroutine - coro(explicit_wait); + boost::dcoroutines::coroutine coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. -- cgit v1.2.3 From ec466b2f5ea5149a4d5d5ead12891d308a96b8ff Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sun, 12 Jul 2015 14:38:06 -0400 Subject: MAINT-5351: glh-linear 'equivalent' macro has been destroyed. Hopefully we no longer need to brute-force remove any more macros with the same name. --- indra/llcommon/fix_macros.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h index ef959decff..43c09c54bc 100755 --- a/indra/llcommon/fix_macros.h +++ b/indra/llcommon/fix_macros.h @@ -16,10 +16,6 @@ // these macros all over again. // who injects MACROS with such generic names?! Grr. -#ifdef equivalent -#undef equivalent -#endif - #ifdef check #undef check #endif -- cgit v1.2.3 From c4267e0284bb0765470ef2e88000c2b5e0c7c8e7 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 19 Aug 2015 10:30:41 -0700 Subject: Swap BOOST_COROUTINE and BOOST_CONTEXT --- indra/llcommon/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 9757679ce1..d33c3a20c8 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -311,7 +311,7 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}") LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}") - LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_COROUTINE_LIBRARY};${BOOST_SYSTEM_LIBRARY}") + LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_COROUTINE_LIBRARY};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_SYSTEM_LIBRARY}") LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") -- cgit v1.2.3 From 7e1efcd47423e66e3262dff137c793d9ef690a1b Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 25 Aug 2015 16:41:25 -0700 Subject: Reenable and correct link issue on linux llappearanceutility. --- indra/llcommon/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index d33c3a20c8..e529cea29c 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -267,8 +267,11 @@ target_link_libraries( ${JSONCPP_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} ${URIPARSER_LIBRARIES} ) -- cgit v1.2.3 From 6a204b1bddc711b768d598c6ac0a16413f48d3c3 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 10 Sep 2015 16:48:01 -0700 Subject: MAINT-5575: Finished converting experience cache to singleton MAINT-4952: Coverted VMM to coroutines --- indra/llcommon/llsdjson.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++ indra/llcommon/llsdjson.h | 18 +++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp index 2afdba388a..8caaaee534 100644 --- a/indra/llcommon/llsdjson.cpp +++ b/indra/llcommon/llsdjson.cpp @@ -76,3 +76,51 @@ LLSD LlsdFromJson(const Json::Value &val) } return result; } + +//========================================================================= +Json::Value LlsdToJson(const LLSD &val) +{ + Json::Value result; + + switch (val.type()) + { + case LLSD::TypeUndefined: + result = Json::Value::null; + break; + case LLSD::TypeBoolean: + result = Json::Value(static_cast(val.asBoolean())); + break; + case LLSD::TypeInteger: + result = Json::Value(static_cast(val.asInteger())); + break; + case LLSD::TypeReal: + result = Json::Value(static_cast(val.asReal())); + break; + case LLSD::TypeURI: + case LLSD::TypeDate: + case LLSD::TypeUUID: + case LLSD::TypeString: + result = Json::Value(val.asString()); + break; + case LLSD::TypeMap: + result = Json::Value(Json::objectValue); + for (LLSD::map_const_iterator it = val.beginMap(); it != val.endMap(); ++it) + { + result[it->first] = LlsdToJson(it->second); + } + break; + case LLSD::TypeArray: + result = Json::Value(Json::arrayValue); + for (LLSD::array_const_iterator it = val.beginArray(); it != val.endArray(); ++it) + { + result.append(LlsdToJson(*it)); + } + break; + case LLSD::TypeBinary: + default: + LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL; + break; + } + + return result; +} diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h index cdf9fed500..2be7112404 100644 --- a/indra/llcommon/llsdjson.h +++ b/indra/llcommon/llsdjson.h @@ -55,5 +55,23 @@ /// Order is preserved for an array but not for objects. LLSD LlsdFromJson(const Json::Value &val); +/// Convert an LLSD object into Parsed JSON object maintaining member names and +/// array indexs. +/// +/// Types are converted as follows: +/// LLSD Type | JSON Type +/// --------------+---------------- +/// TypeUndefined | null +/// TypeBoolean | boolean +/// TypeInteger | integer +/// TypeReal | real/numeric +/// TypeString | string +/// TypeURI | string +/// TypeDate | string +/// TypeUUID | string +/// TypeMap | object +/// TypeArray | array +/// TypeBinary | unsupported +Json::Value LlsdToJson(const LLSD &val); #endif // LL_LLSDJSON_H -- cgit v1.2.3 From 2108b3e540406daf08d2845ac395ad4bb1633b35 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 18 Sep 2015 13:11:58 -0400 Subject: MAINT-5351: Zap one more vestigial reference to coroutine 'self' param. --- indra/llcommon/lleventcoro.h | 1 - 1 file changed, 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index e2ce4bb193..8f3ee38fb4 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -126,7 +126,6 @@ void yield(); * By contrast, postAndWait() listens on the @a replyPump @em before posting * the specified LLSD event on the specified @a requestPump. * - * @param self The @c self object passed into a coroutine * @param event LLSD data to be posted on @a requestPump * @param requestPump an LLEventPump on which to post @a event. Pass either * the LLEventPump& or its string name. However, if you pass a -- cgit v1.2.3 From 75c6549fde060e974c90636685962ee373f94202 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 18 Sep 2015 11:39:22 -0700 Subject: Set consistent terminology for yield/wait -> suspend for coroutines. --- indra/llcommon/lleventcoro.cpp | 32 ++++++------ indra/llcommon/lleventcoro.h | 86 +++++++++++++++---------------- indra/llcommon/llprocess.cpp | 8 +-- indra/llcommon/tests/lleventcoro_test.cpp | 50 +++++++++--------- 4 files changed, 88 insertions(+), 88 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 66cc7cada0..031c2cffb0 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -45,14 +45,14 @@ namespace { /** - * waitForEventOn() permits a coroutine to temporarily listen on an + * suspendUntilEventOn() permits a coroutine to temporarily listen on an * LLEventPump any number of times. We don't really want to have to ask * the caller to label each such call with a distinct string; the whole - * point of waitForEventOn() is to present a nice sequential interface to + * point of suspendUntilEventOn() is to present a nice sequential interface to * the underlying LLEventPump-with-named-listeners machinery. So we'll use * LLEventPump::inventName() to generate a distinct name for each * temporary listener. On the other hand, because a given coroutine might - * call waitForEventOn() any number of times, we don't really want to + * call suspendUntilEventOn() any number of times, we don't really want to * consume an arbitrary number of generated inventName()s: that namespace, * though large, is nonetheless finite. So we memoize an invented name for * each distinct coroutine instance. @@ -73,7 +73,7 @@ std::string listenerNameForCoro() } /** - * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * Implement behavior described for postEventAndSuspend()'s @a replyPumpNamePath * parameter: * * * If path.isUndefined(), do nothing. @@ -145,15 +145,15 @@ void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) } // anonymous -void llcoro::yield() +void llcoro::suspend() { // By viewer convention, we post an event on the "mainloop" LLEventPump // each iteration of the main event-handling loop. So waiting for a single - // event on "mainloop" gives us a one-frame yield. - waitForEventOn("mainloop"); + // event on "mainloop" gives us a one-frame suspend. + suspendUntilEventOn("mainloop"); } -LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, +LLSD llcoro::postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future @@ -171,7 +171,7 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request // request event. LLSD modevent(event); storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -179,7 +179,7 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request // << ": " << modevent << LL_ENDL; requestPump.getPump().post(modevent); } - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << listenerName << " about to wait on LLEventPump " << replyPump.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it @@ -189,7 +189,7 @@ LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& request llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << listenerName << " resuming with " << value << LL_ENDL; // returning should disconnect the connection return value; @@ -199,7 +199,7 @@ namespace { /** - * This helper is specifically for the two-pump version of waitForEventOn(). + * This helper is specifically for the two-pump version of suspendUntilEventOn(). * We use a single future object, but we want to listen on two pumps with it. * Since we must still adapt from (the callable constructed by) * boost::dcoroutines::make_callback() (void return) to provide an event @@ -242,7 +242,7 @@ WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator namespace llcoro { -LLEventWithID postAndWait2(const LLSD& event, +LLEventWithID postEventAndSuspend2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, @@ -270,12 +270,12 @@ LLEventWithID postAndWait2(const LLSD& event, replyPump0.getPump().getName()); storeToLLSDPath(modevent, replyPump1NamePath, replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + LL_DEBUGS("lleventcoro") << "postEventAndSuspend2(): coroutine " << name << " posting to " << requestPump.getPump().getName() << ": " << modevent << LL_ENDL; requestPump.getPump().post(modevent); } - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + LL_DEBUGS("lleventcoro") << "postEventAndSuspend2(): coroutine " << name << " about to wait on LLEventPumps " << replyPump0.getPump().getName() << ", " << replyPump1.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it @@ -285,7 +285,7 @@ LLEventWithID postAndWait2(const LLSD& event, llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << name << " resuming with (" << value.first << ", " << value.second << ")" << LL_ENDL; // returning should disconnect both connections diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 8f3ee38fb4..a09759c9cf 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -106,33 +106,33 @@ VoidListener voidlistener(const LISTENER& listener) * runs without suspending for nontrivial time, sprinkle in calls to this * function to avoid stalling the rest of the viewer processing. */ -void yield(); +void suspend(); /** - * Post specified LLSD event on the specified LLEventPump, then wait for a + * Post specified LLSD event on the specified LLEventPump, then suspend for a * response on specified other LLEventPump. This is more than mere * convenience: the difference between this function and the sequence * @code * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(replyPump); + * LLSD reply = suspendUntilEventOn(replyPump); * @endcode * is that the sequence above fails if the reply is posted immediately on * @a replyPump, that is, before requestPump.post() returns. In the * sequence above, the running coroutine isn't even listening on @a replyPump - * until requestPump.post() returns and @c waitForEventOn() is + * until requestPump.post() returns and @c suspendUntilEventOn() is * entered. Therefore, the coroutine completely misses an immediate reply - * event, making it wait indefinitely. + * event, making it suspend indefinitely. * - * By contrast, postAndWait() listens on the @a replyPump @em before posting + * By contrast, postEventAndSuspend() listens on the @a replyPump @em before posting * the specified LLSD event on the specified @a requestPump. * * @param event LLSD data to be posted on @a requestPump * @param requestPump an LLEventPump on which to post @a event. Pass either * the LLEventPump& or its string name. However, if you pass a * default-constructed @c LLEventPumpOrPumpName, we skip the post() call. - * @param replyPump an LLEventPump on which postAndWait() will listen for a + * @param replyPump an LLEventPump on which postEventAndSuspend() will listen for a * reply. Pass either the LLEventPump& or its string name. The calling - * coroutine will wait until that reply arrives. (If you're concerned about a + * coroutine will suspend until that reply arrives. (If you're concerned about a * reply that might not arrive, please see also LLEventTimeout.) * @param replyPumpNamePath specifies the location within @a event in which to * store replyPump.getName(). This is a strictly optional convenience @@ -155,21 +155,21 @@ void yield(); * @a replyPumpNamePath specifies the entry in the lowest-level structure in * @a event into which to store replyPump.getName(). */ -LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, +LLSD postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()); /// Wait for the next event on the specified LLEventPump. Pass either the /// LLEventPump& or its string name. inline -LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) +LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump) { - // This is now a convenience wrapper for postAndWait(). - return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); + // This is now a convenience wrapper for postEventAndSuspend(). + return postEventAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump); } } // namespace llcoro -/// return type for two-pump variant of waitForEventOn() +/// return type for two-pump variant of suspendUntilEventOn() typedef std::pair LLEventWithID; namespace llcoro @@ -177,7 +177,7 @@ namespace llcoro /** * This function waits for a reply on either of two specified LLEventPumps. - * Otherwise, it closely resembles postAndWait(); please see the documentation + * Otherwise, it closely resembles postAndSuspend(); please see the documentation * for that function for detailed parameter info. * * While we could have implemented the single-pump variant in terms of this @@ -192,19 +192,19 @@ namespace llcoro * the index of the pump on which it arrived (0 or 1). * * @note - * I'd have preferred to overload the name postAndWait() for both signatures. + * I'd have preferred to overload the name postAndSuspend() for both signatures. * But consider the following ambiguous call: * @code - * postAndWait(LLSD(), requestPump, replyPump, "someString"); + * postAndSuspend(LLSD(), requestPump, replyPump, "someString"); * @endcode * "someString" could be converted to either LLSD (@a replyPumpNamePath for * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump * function). * - * It seems less burdensome to write postAndWait2() than to write either + * It seems less burdensome to write postEventAndSuspend2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -LLEventWithID postAndWait2(const LLSD& event, +LLEventWithID postEventAndSuspend2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, @@ -216,17 +216,17 @@ LLEventWithID postAndWait2(const LLSD& event, */ inline LLEventWithID -waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { - // This is now a convenience wrapper for postAndWait2(). - return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + // This is now a convenience wrapper for postEventAndSuspend2(). + return postEventAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** - * Helper for the two-pump variant of waitForEventOn(), e.g.: + * Helper for the two-pump variant of suspendUntilEventOn(), e.g.: * * @code - * LLSD reply = errorException(waitForEventOn(replyPump, errorPump), + * LLSD reply = errorException(suspendUntilEventOn(replyPump, errorPump), * "error response from login.cgi"); * @endcode * @@ -286,7 +286,7 @@ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc * 2. Provide its actual name to the event API in question as the name of the * reply LLEventPump. * 3. Initiate the request to the event API. - * 4. Call your LLEventTempStream's wait() method to wait for the reply. + * 4. Call your LLEventTempStream's suspend() method to suspend for the reply. * 5. Let the LLCoroEventPump go out of scope. */ class LL_COMMON_API LLCoroEventPump @@ -304,15 +304,15 @@ public: /** * Wait for an event on this LLEventPump. */ - LLSD wait() + LLSD suspend() { - return llcoro::waitForEventOn(mPump); + return llcoro::suspendUntilEventOn(mPump); } - LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); + return llcoro::postEventAndSuspend(event, requestPump, mPump, replyPumpNamePath); } private: @@ -348,49 +348,49 @@ public: /// request pump 1 LLEventPump& getPump1() { return mPump1; } - /// waitForEventOn(either of our two LLEventPumps) - LLEventWithID wait() + /// suspendUntilEventOn(either of our two LLEventPumps) + LLEventWithID suspend() { - return llcoro::waitForEventOn(mPump0, mPump1); + return llcoro::suspendUntilEventOn(mPump0, mPump1); } - /// errorException(wait()) - LLSD waitWithException() + /// errorException(suspend()) + LLSD suspendWithException() { - return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorException(suspend(), std::string("Error event on ") + getName1()); } - /// errorLog(wait()) - LLSD waitWithLog() + /// errorLog(suspend()) + LLSD suspendWithLog() { - return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); + return llcoro::errorLog(suspend(), std::string("Error event on ") + getName1()); } - LLEventWithID postAndWait(const LLSD& event, + LLEventWithID postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, + return llcoro::postEventAndSuspend2(event, requestPump, mPump0, mPump1, replyPump0NamePath, replyPump1NamePath); } - LLSD postAndWaitWithException(const LLSD& event, + LLSD postAndSuspendWithException(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::errorException(postAndWait(event, requestPump, + return llcoro::errorException(postAndSuspend(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } - LLSD postAndWaitWithLog(const LLSD& event, + LLSD postAndSuspendWithLog(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::errorLog(postAndWait(event, requestPump, + return llcoro::errorLog(postAndSuspend(event, requestPump, replyPump0NamePath, replyPump1NamePath), std::string("Error event on ") + getName1()); } diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 715df36f39..a8a772b7df 100755 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -710,7 +710,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): // Tie the lifespan of this child process to the lifespan of our APR // pool: on destruction of the pool, forcibly kill the process. Tell - // APR to try SIGTERM and wait 3 seconds. If that didn't work, use + // APR to try SIGTERM and suspend 3 seconds. If that didn't work, use // SIGKILL. apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT); |*==========================================================================*/ @@ -986,9 +986,9 @@ void LLProcess::handle_status(int reason, int status) // wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT); // It's just wrong to call apr_proc_wait() here. The only way APR knows to // call us with APR_OC_REASON_DEATH is that it's already reaped this child - // process, so calling wait() will only produce "huh?" from the OS. We + // process, so calling suspend() will only produce "huh?" from the OS. We // must rely on the status param passed in, which unfortunately comes - // straight from the OS wait() call, which means we have to decode it by + // straight from the OS suspend() call, which means we have to decode it by // hand. mStatus = interpret_status(status); LL_INFOS("LLProcess") << getStatusString() << LL_ENDL; @@ -1231,7 +1231,7 @@ static std::string WindowsErrorString(const std::string& operation) #include #include #include -#include +#include void LLProcess::autokill() { diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 00be5035f2..5cced69670 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -129,8 +129,8 @@ typedef coroutine match_coroutine_type; *****************************************************************************/ /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that -/// distinguishes postAndWait() from calling post(), then calling -/// waitForEventOn(). +/// distinguishes postEventAndSuspend() from calling post(), then calling +/// suspendUntilEventOn(). class ImmediateAPI { public: @@ -241,13 +241,13 @@ namespace tut // declare the future boost::dcoroutines::future future(self); - // tell the future what to wait for + // tell the future what to suspend for LLTempBoundListener connection( LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); ensure("Not yet", ! future); // attempting to dereference ("resolve") the future causes the calling - // coroutine to wait for it - debug("about to wait"); + // coroutine to suspend for it + debug("about to suspend"); result = *future; ensure("Got it", future); } @@ -269,9 +269,9 @@ namespace tut coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. debug("about to send"); - // Satisfy the wait. + // Satisfy the suspend. LLEventPumps::instance().obtain("source").post("received"); - // Now wait for the coroutine to complete. + // Now suspend for the coroutine to complete. ensure("coroutine complete", ! coro); // ensure the coroutine ran and woke up again with the intended result ensure_equals(result.asString(), "received"); @@ -281,7 +281,7 @@ namespace tut { BEGIN { - result = waitForEventOn("source"); + result = suspendUntilEventOn("source"); } END } @@ -303,7 +303,7 @@ namespace tut { BEGIN { - LLEventWithID pair = waitForEventOn("reply", "error"); + LLEventWithID pair = suspendUntilEventOn("reply", "error"); result = pair.first; which = pair.second; debug(STRINGIZE("result = " << result << ", which = " << which)); @@ -347,7 +347,7 @@ namespace tut { LLCoroEventPump waiter; replyName = waiter.getName(); - result = waiter.wait(); + result = waiter.suspend(); } END } @@ -372,7 +372,7 @@ namespace tut LLCoroEventPumps waiter; replyName = waiter.getName0(); errorName = waiter.getName1(); - LLEventWithID pair(waiter.wait()); + LLEventWithID pair(waiter.suspend()); result = pair.first; which = pair.second; } @@ -414,7 +414,7 @@ namespace tut LLCoroEventPumps waiter; replyName = waiter.getName0(); errorName = waiter.getName1(); - result = waiter.waitWithException(); + result = waiter.suspendWithException(); } END } @@ -441,7 +441,7 @@ namespace tut errorName = waiter.getName1(); try { - result = waiter.waitWithException(); + result = waiter.suspendWithException(); debug("no exception"); } catch (const LLErrorEvent& e) @@ -474,7 +474,7 @@ namespace tut LLCoroEventPumps waiter; replyName = waiter.getName0(); errorName = waiter.getName1(); - result = waiter.waitWithLog(); + result = waiter.suspendWithLog(); } END } @@ -502,7 +502,7 @@ namespace tut WrapLLErrs capture; try { - result = waiter.waitWithLog(); + result = waiter.suspendWithLog(); debug("no exception"); } catch (const WrapLLErrs::FatalException& e) @@ -532,7 +532,7 @@ namespace tut { BEGIN { - result = postAndWait(LLSDMap("value", 17), // request event + result = postEventAndSuspend(LLSDMap("value", 17), // request event immediateAPI.getPump(), // requestPump "reply1", // replyPump "reply"); // request["reply"] = name @@ -554,7 +554,7 @@ namespace tut { BEGIN { - LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), + LLEventWithID pair = ::postEventAndSuspend2(LLSDMap("value", 18), immediateAPI.getPump(), "reply2", "error2", @@ -582,7 +582,7 @@ namespace tut { BEGIN { - LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), + LLEventWithID pair = ::postEventAndSuspend2(LLSDMap("value", 18)("fail", LLSD()), immediateAPI.getPump(), "reply2", "error2", @@ -611,7 +611,7 @@ namespace tut BEGIN { LLCoroEventPump waiter; - result = waiter.postAndWait(LLSDMap("value", 17), + result = waiter.postAndSuspend(LLSDMap("value", 17), immediateAPI.getPump(), "reply"); } END @@ -632,7 +632,7 @@ namespace tut BEGIN { LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), + LLEventWithID pair(waiter.postAndSuspend(LLSDMap("value", 23), immediateAPI.getPump(), "reply", "error")); result = pair.first; which = pair.second; @@ -657,7 +657,7 @@ namespace tut { LLCoroEventPumps waiter; LLEventWithID pair( - waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), + waiter.postAndSuspend(LLSDMap("value", 23)("fail", LLSD()), immediateAPI.getPump(), "reply", "error")); result = pair.first; which = pair.second; @@ -681,7 +681,7 @@ namespace tut BEGIN { LLCoroEventPumps waiter; - result = waiter.postAndWaitWithException(LLSDMap("value", 8), + result = waiter.postAndSuspendWithException(LLSDMap("value", 8), immediateAPI.getPump(), "reply", "error"); } END @@ -704,7 +704,7 @@ namespace tut LLCoroEventPumps waiter; try { - result = waiter.postAndWaitWithException( + result = waiter.postAndSuspendWithException( LLSDMap("value", 9)("fail", LLSD()), immediateAPI.getPump(), "reply", "error"); debug("no exception"); @@ -734,7 +734,7 @@ namespace tut BEGIN { LLCoroEventPumps waiter; - result = waiter.postAndWaitWithLog(LLSDMap("value", 30), + result = waiter.postAndSuspendWithLog(LLSDMap("value", 30), immediateAPI.getPump(), "reply", "error"); } END @@ -758,7 +758,7 @@ namespace tut WrapLLErrs capture; try { - result = waiter.postAndWaitWithLog( + result = waiter.postAndSuspendWithLog( LLSDMap("value", 31)("fail", LLSD()), immediateAPI.getPump(), "reply", "error"); debug("no exception"); -- cgit v1.2.3 From a8ab084678aa36b38e1df74c8addf666a2774d3b Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 18 Sep 2015 11:54:38 -0700 Subject: Tweek to naming postEventAndSuspend -> postAndSuspend --- indra/llcommon/lleventcoro.cpp | 18 +++++++++--------- indra/llcommon/lleventcoro.h | 22 +++++++++++----------- indra/llcommon/tests/lleventcoro_test.cpp | 8 ++++---- 3 files changed, 24 insertions(+), 24 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 031c2cffb0..c9bfcacedc 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -73,7 +73,7 @@ std::string listenerNameForCoro() } /** - * Implement behavior described for postEventAndSuspend()'s @a replyPumpNamePath + * Implement behavior described for postAndSuspend()'s @a replyPumpNamePath * parameter: * * * If path.isUndefined(), do nothing. @@ -153,7 +153,7 @@ void llcoro::suspend() suspendUntilEventOn("mainloop"); } -LLSD llcoro::postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, +LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future @@ -171,7 +171,7 @@ LLSD llcoro::postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& // request event. LLSD modevent(event); storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -179,7 +179,7 @@ LLSD llcoro::postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& // << ": " << modevent << LL_ENDL; requestPump.getPump().post(modevent); } - LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName << " about to wait on LLEventPump " << replyPump.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it @@ -189,7 +189,7 @@ LLSD llcoro::postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName << " resuming with " << value << LL_ENDL; // returning should disconnect the connection return value; @@ -242,7 +242,7 @@ WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator namespace llcoro { -LLEventWithID postEventAndSuspend2(const LLSD& event, +LLEventWithID postAndSuspend2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, @@ -270,12 +270,12 @@ LLEventWithID postEventAndSuspend2(const LLSD& event, replyPump0.getPump().getName()); storeToLLSDPath(modevent, replyPump1NamePath, replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postEventAndSuspend2(): coroutine " << name + LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name << " posting to " << requestPump.getPump().getName() << ": " << modevent << LL_ENDL; requestPump.getPump().post(modevent); } - LL_DEBUGS("lleventcoro") << "postEventAndSuspend2(): coroutine " << name + LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name << " about to wait on LLEventPumps " << replyPump0.getPump().getName() << ", " << replyPump1.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it @@ -285,7 +285,7 @@ LLEventWithID postEventAndSuspend2(const LLSD& event, llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back - LL_DEBUGS("lleventcoro") << "postEventAndSuspend(): coroutine " << name + LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name << " resuming with (" << value.first << ", " << value.second << ")" << LL_ENDL; // returning should disconnect both connections diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index a09759c9cf..6fda3d2572 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -123,14 +123,14 @@ void suspend(); * entered. Therefore, the coroutine completely misses an immediate reply * event, making it suspend indefinitely. * - * By contrast, postEventAndSuspend() listens on the @a replyPump @em before posting + * By contrast, postAndSuspend() listens on the @a replyPump @em before posting * the specified LLSD event on the specified @a requestPump. * * @param event LLSD data to be posted on @a requestPump * @param requestPump an LLEventPump on which to post @a event. Pass either * the LLEventPump& or its string name. However, if you pass a * default-constructed @c LLEventPumpOrPumpName, we skip the post() call. - * @param replyPump an LLEventPump on which postEventAndSuspend() will listen for a + * @param replyPump an LLEventPump on which postAndSuspend() will listen for a * reply. Pass either the LLEventPump& or its string name. The calling * coroutine will suspend until that reply arrives. (If you're concerned about a * reply that might not arrive, please see also LLEventTimeout.) @@ -155,7 +155,7 @@ void suspend(); * @a replyPumpNamePath specifies the entry in the lowest-level structure in * @a event into which to store replyPump.getName(). */ -LLSD postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, +LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()); /// Wait for the next event on the specified LLEventPump. Pass either the @@ -163,8 +163,8 @@ LLSD postEventAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& request inline LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump) { - // This is now a convenience wrapper for postEventAndSuspend(). - return postEventAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump); + // This is now a convenience wrapper for postAndSuspend(). + return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump); } } // namespace llcoro @@ -201,10 +201,10 @@ namespace llcoro * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump * function). * - * It seems less burdensome to write postEventAndSuspend2() than to write either + * It seems less burdensome to write postAndSuspend2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -LLEventWithID postEventAndSuspend2(const LLSD& event, +LLEventWithID postAndSuspend2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, @@ -218,8 +218,8 @@ inline LLEventWithID suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { - // This is now a convenience wrapper for postEventAndSuspend2(). - return postEventAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + // This is now a convenience wrapper for postAndSuspend2(). + return postAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** @@ -312,7 +312,7 @@ public: LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return llcoro::postEventAndSuspend(event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndSuspend(event, requestPump, mPump, replyPumpNamePath); } private: @@ -371,7 +371,7 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return llcoro::postEventAndSuspend2(event, requestPump, mPump0, mPump1, + return llcoro::postAndSuspend2(event, requestPump, mPump0, mPump1, replyPump0NamePath, replyPump1NamePath); } diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 5cced69670..1ee79e9eb6 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -129,7 +129,7 @@ typedef coroutine match_coroutine_type; *****************************************************************************/ /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that -/// distinguishes postEventAndSuspend() from calling post(), then calling +/// distinguishes postAndSuspend() from calling post(), then calling /// suspendUntilEventOn(). class ImmediateAPI { @@ -532,7 +532,7 @@ namespace tut { BEGIN { - result = postEventAndSuspend(LLSDMap("value", 17), // request event + result = postAndSuspend(LLSDMap("value", 17), // request event immediateAPI.getPump(), // requestPump "reply1", // replyPump "reply"); // request["reply"] = name @@ -554,7 +554,7 @@ namespace tut { BEGIN { - LLEventWithID pair = ::postEventAndSuspend2(LLSDMap("value", 18), + LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18), immediateAPI.getPump(), "reply2", "error2", @@ -582,7 +582,7 @@ namespace tut { BEGIN { - LLEventWithID pair = ::postEventAndSuspend2(LLSDMap("value", 18)("fail", LLSD()), + LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18)("fail", LLSD()), immediateAPI.getPump(), "reply2", "error2", -- cgit v1.2.3 From 96c87f326aee7b6499aae5ff3f6e208f2db6cd0e Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 18 Sep 2015 12:31:03 -0700 Subject: Shouldn't have renamed this include. --- indra/llcommon/llprocess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index a8a772b7df..db29504b42 100755 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -1231,7 +1231,7 @@ static std::string WindowsErrorString(const std::string& operation) #include #include #include -#include +#include void LLProcess::autokill() { -- cgit v1.2.3 From 2af14639de9f575ac9a2766835206e5c6ffb46c8 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 20 Nov 2015 12:24:53 -0800 Subject: MAINT-5835: Cut down on log spam from coros and voice. --- indra/llcommon/llcoros.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index d76401d01b..548a6d22be 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -91,6 +91,8 @@ 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; ) { @@ -98,7 +100,24 @@ bool LLCoros::cleanup(const LLSD&) // since last tick? 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. @@ -116,6 +135,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()) { @@ -132,7 +154,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; } } -- cgit v1.2.3 From 2763bbd97519d35a43aedf279751e7b1045581dc Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 4 Dec 2015 14:27:22 -0800 Subject: Initial changes for Vivox/Azumarill merge. Lots of temporary code and conditional compile switches. Begin switch from statemachine to coroutine. --- indra/llcommon/lleventcoro.cpp | 10 ++++++++++ indra/llcommon/lleventcoro.h | 5 +++++ indra/llcommon/llevents.cpp | 11 +++++++++++ indra/llcommon/llevents.h | 12 ++++++++++++ 4 files changed, 38 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index c9bfcacedc..1c3fb4325d 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -41,6 +41,8 @@ #include "llerror.h" #include "llcoros.h" +#include "lleventfilter.h" + namespace { @@ -153,6 +155,14 @@ void llcoro::suspend() suspendUntilEventOn("mainloop"); } +void llcoro::suspendUntilTimeout(float seconds) +{ + LLEventTimeout timeout; + + timeout.eventAfter(seconds, LLSD()); + llcoro::suspendUntilEventOn(timeout); +} + LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 6fda3d2572..bcc8cdda1d 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -108,6 +108,11 @@ VoidListener voidlistener(const LISTENER& listener) */ void suspend(); +/** + * Yield control from a coroutine for at least the specified number of seconds + */ +void suspendUntilTimeout(float seconds); + /** * Post specified LLSD event on the specified LLEventPump, then suspend for a * response on specified other LLEventPump. This is more than mere diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 1c928b3db8..bb3a137815 100755 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -132,6 +132,17 @@ LLEventPump& LLEventPumps::obtain(const std::string& name) return *newInstance; } +bool LLEventPumps::post(const std::string&name, const LLSD&message) +{ + PumpMap::iterator found = mPumpMap.find(name); + + if (found == mPumpMap.end()) + return false; + + return (*found).second->post(message); +} + + void LLEventPumps::flush() { // Flush every known LLEventPump instance. Leave it up to each instance to diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 0cbd1da32d..8d4fa68350 100755 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -217,6 +217,18 @@ public: * an instance without conferring @em ownership. */ LLEventPump& obtain(const std::string& name); + + /** + * Find the named LLEventPump instance. If it exists post the message to it. + * If the pump does not exist, do nothing. + * + * returns the result of the LLEventPump::post. If no pump exists returns false. + * + * This is syntactically similar to LLEventPumps::instance().post(name, message), + * however if the pump does not already exist it will not be created. + */ + bool post(const std::string&, const LLSD&); + /** * Flush all known LLEventPump instances */ -- cgit v1.2.3 From 6a062089b86c092d6227a64502c4829fc6f0c586 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 16 Dec 2015 17:13:04 -0500 Subject: MAINT-5976: Introduce LLCoros::set_consuming(bool). set_consuming(true) tells each postAndSuspend() call to consume the event for which it is suspending. --- indra/llcommon/llcoros.cpp | 26 ++++++++++++++++++++--- indra/llcommon/llcoros.h | 15 ++++++++++++++ indra/llcommon/lleventcoro.cpp | 34 +++++++++++++++++++------------ indra/llcommon/lleventcoro.h | 11 +++++++--- indra/llcommon/tests/lleventcoro_test.cpp | 4 ++-- 5 files changed, 69 insertions(+), 21 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 548a6d22be..d16bf0160b 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -51,14 +51,32 @@ boost::thread_specific_ptr LLCoros::sCurrentCoro(LLCoros::no_cleanup); //static -LLCoros::coro::self& LLCoros::get_self() +LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) { CoroData* current = sCurrentCoro.get(); if (! current) { - LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; + LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL; } - return *current->mSelf; + 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(): @@ -245,6 +263,8 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& 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) { } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 56eed8cafe..0b1f58f48e 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -150,6 +150,18 @@ public: /// get the current coro::self& for those who really really care static coro::self& get_self(); + /** + * Most coroutines, most of the time, don't "consume" the events for which + * they're suspending. This way, an arbitrary number of listeners (whether + * coroutines or simple callbacks) can be registered on a particular + * LLEventPump, every listener responding to each of the events on that + * LLEventPump. But a particular coroutine can assert that it will consume + * each event for which it suspends. (See also llcoro::postAndSuspend(), + * llcoro::VoidListener) + */ + static void set_consuming(bool consuming); + static bool get_consuming(); + private: LLCoros(); friend class LLSingleton; @@ -159,6 +171,7 @@ private: struct CoroData; static void no_cleanup(CoroData*); static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); + static CoroData& get_CoroData(const std::string& caller); S32 mStackSize; @@ -178,6 +191,8 @@ private: const std::string mName; // the actual coroutine instance LLCoros::coro mCoro; + // set_consuming() state + bool mConsuming; // 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, diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 1c3fb4325d..9b7e8fb65f 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -167,7 +167,7 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + boost::dcoroutines::future 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()); @@ -193,21 +193,25 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ << " about to wait on LLEventPump " << replyPump.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it - LLSD value; + LLSD_consumed value; { // instantiate Suspending to manage the "current" coroutine llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; + << " resuming with " << value.first << LL_ENDL; + // immediately set consumed according to consuming + *value.second = LLCoros::get_consuming(); // returning should disconnect the connection - return value; + return value.first; } namespace { +typedef std::pair LLEventWithID_consumed; + /** * This helper is specifically for the two-pump version of suspendUntilEventOn(). * We use a single future object, but we want to listen on two pumps with it. @@ -227,13 +231,15 @@ public: mListener(listener), mDiscrim(discriminator) {} + // this signature is required for an LLEventPump listener bool operator()(const LLSD& event) { - // our future object is defined to accept LLEventWithID - mListener(LLEventWithID(event, mDiscrim)); - // don't swallow the event, let other listeners see it - return false; + bool consumed = false; + // our future object is defined to accept LLEventWithID_consumed + mListener(LLEventWithID_consumed(LLEventWithID(event, mDiscrim), &consumed)); + // tell LLEventPump whether or not event was consumed + return consumed; } private: LISTENER mListener; @@ -260,7 +266,7 @@ LLEventWithID postAndSuspend2(const LLSD& event, const LLSD& replyPump1NamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + boost::dcoroutines::future 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()); @@ -289,17 +295,19 @@ LLEventWithID postAndSuspend2(const LLSD& event, << " about to wait on LLEventPumps " << replyPump0.getPump().getName() << ", " << replyPump1.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value; + LLEventWithID_consumed value; { // instantiate Suspending to manage "current" coroutine llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; + << " resuming with (" << value.first.first + << ", " << value.first.second << ")" << LL_ENDL; + // tell LLEventPump whether we're consuming + *value.second = LLCoros::get_consuming(); // returning should disconnect both connections - return value; + return value.first; } LLSD errorException(const LLEventWithID& result, const std::string& desc) diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index bcc8cdda1d..acf2ad24a4 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -32,6 +32,7 @@ #include #include #include +#include // std::pair #include "llevents.h" #include "llerror.h" @@ -75,6 +76,8 @@ private: namespace llcoro { +typedef std::pair LLSD_consumed; + /// This is an adapter for a signature like void LISTENER(const LLSD&), which /// isn't a valid LLEventPump listener: such listeners should return bool. template @@ -84,11 +87,13 @@ public: VoidListener(const LISTENER& listener): mListener(listener) {} + bool operator()(const LLSD& event) { - mListener(event); - // don't swallow the event, let other listeners see it - return false; + bool consumed = false; + mListener(LLSD_consumed(event, &consumed)); + // tell upstream LLEventPump whether listener consumed + return consumed; } private: LISTENER mListener; diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 1ee79e9eb6..da1439418f 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -240,7 +240,7 @@ namespace tut // ... do whatever preliminary stuff must happen ... // declare the future - boost::dcoroutines::future future(self); + boost::dcoroutines::future future(self); // tell the future what to suspend for LLTempBoundListener connection( LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); @@ -248,7 +248,7 @@ namespace tut // attempting to dereference ("resolve") the future causes the calling // coroutine to suspend for it debug("about to suspend"); - result = *future; + result = (*future).first; ensure("Got it", future); } END -- cgit v1.2.3 From 83c9c6df07ab602bdbd2487e587956d0e67993e2 Mon Sep 17 00:00:00 2001 From: rider Date: Thu, 17 Dec 2015 09:06:19 -0800 Subject: MAINT-5976: Adding MailDrop type event Queue --- indra/llcommon/llevents.cpp | 31 +++++++++++++++++++++++++++++++ indra/llcommon/llevents.h | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 4 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index bb3a137815..080830a134 100755 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -507,6 +507,37 @@ bool LLEventStream::post(const LLSD& event) return (*signal)(event); } +/***************************************************************************** + * LLEventMailDrop + *****************************************************************************/ +bool LLEventMailDrop::post(const LLSD& event) +{ + bool posted = LLEventPump::post(event); + + if (!posted) + { + mEventHistory.push_back(event); + } + + return posted; +} + +LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, + const LLEventListener& listener, + const NameList& after, + const NameList& before) +{ + LLBoundListener bndlistener = LLEventPump::listen_impl(name, listener, after, before); + + return bndlistener; +} + +// typedef std::list EventList; +// EventList mEventHistory; + + + + /***************************************************************************** * LLEventQueue *****************************************************************************/ diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 8d4fa68350..b07db889c0 100755 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -554,12 +554,13 @@ private: virtual void reset(); private: - virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, - const NameList& after, - const NameList& before); std::string mName; protected: + virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, + const NameList& after, + const NameList& before); + /// implement the dispatching boost::shared_ptr mSignal; @@ -597,11 +598,39 @@ public: virtual bool post(const LLSD& event); }; +/***************************************************************************** + * LLEventMailDrop + *****************************************************************************/ +/** + * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally, + * however if no listeners return that they have handled the event it is placed in + * a list. Subsequent attaching listeners will recieve stored events until a + * listener indicates that the event has been handled. + */ +class LL_COMMON_API LLEventMailDrop: public LLEventPump +{ +public: + LLEventMailDrop(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} + virtual ~LLEventMailDrop() {} + + /// Post an event to all listeners + virtual bool post(const LLSD& event); + +protected: + virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, + const NameList& after, + const NameList& before); + +private: + typedef std::list EventList; + EventList mEventHistory; +}; + /***************************************************************************** * LLEventQueue *****************************************************************************/ /** - * LLEventQueue isa LLEventPump whose post() method defers calling registered + * LLEventQueue is a LLEventPump whose post() method defers calling registered * listeners until flush() is called. */ class LL_COMMON_API LLEventQueue: public LLEventPump -- cgit v1.2.3 From 2c9097aa28d65eeddcfb60b9ac93495723ed6419 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 17 Dec 2015 14:09:51 -0800 Subject: MAINT-5977: Finish implementation of MailBox event pump type for guaranteed delivery --- indra/llcommon/llevents.cpp | 21 ++++++++++++--------- indra/llcommon/llevents.h | 20 +++++++++++++++----- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 080830a134..fc2d8d5e23 100755 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -512,10 +512,11 @@ bool LLEventStream::post(const LLSD& event) *****************************************************************************/ bool LLEventMailDrop::post(const LLSD& event) { - bool posted = LLEventPump::post(event); + bool posted = LLEventStream::post(event); if (!posted) - { + { // if the event was not handled we will save it for later so that it can + // be posted to any future listeners when they attach. mEventHistory.push_back(event); } @@ -527,15 +528,17 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, const NameList& after, const NameList& before) { - LLBoundListener bndlistener = LLEventPump::listen_impl(name, listener, after, before); - - return bndlistener; -} - -// typedef std::list EventList; -// EventList mEventHistory; + if (!mEventHistory.empty()) + { + if (listener(mEventHistory.front())) + { + mEventHistory.pop_front(); + } + } + return LLEventStream::listen_impl(name, listener, after, before); +} /***************************************************************************** diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index b07db889c0..6175329a9d 100755 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -508,7 +508,7 @@ public: // at the boost::bind object itself before that happens. return LLEventDetail::visit_and_connect(name, listener, - boost::bind(&LLEventPump::listen_impl, + boost::bind(&LLEventPump::listen_invoke, this, name, _1, @@ -553,7 +553,16 @@ private: virtual void reset(); + + private: + LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener, + const NameList& after, + const NameList& before) + { + return this->listen_impl(name, listener, after, before); + } + std::string mName; protected: @@ -604,13 +613,14 @@ public: /** * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally, * however if no listeners return that they have handled the event it is placed in - * a list. Subsequent attaching listeners will recieve stored events until a - * listener indicates that the event has been handled. + * a queue. Subsequent attaching listeners will receive stored events from the queue + * until a listener indicates that the event has been handled. In order to receive + * multiple events from a mail drop the listener must disconnect and reconnect. */ -class LL_COMMON_API LLEventMailDrop: public LLEventPump +class LL_COMMON_API LLEventMailDrop : public LLEventStream { public: - LLEventMailDrop(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} + LLEventMailDrop(const std::string& name, bool tweak = false) : LLEventStream(name, tweak) {} virtual ~LLEventMailDrop() {} /// Post an event to all listeners -- cgit v1.2.3 From 6851dd30cae7c748f4fc953de268f7989787501c Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 17 Dec 2015 14:53:21 -0800 Subject: MAINT-5977: Check get_consuming() as well as listener return when draining queue. --- indra/llcommon/llevents.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index fc2d8d5e23..0c5e55dc76 100755 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -57,6 +57,7 @@ #include "stringize.h" #include "llerror.h" #include "llsdutil.h" +#include "llcoros.h" #if LL_MSVC #pragma warning (disable : 4702) #endif @@ -512,7 +513,10 @@ bool LLEventStream::post(const LLSD& event) *****************************************************************************/ bool LLEventMailDrop::post(const LLSD& event) { - bool posted = LLEventStream::post(event); + bool posted = false; + + if (!mSignal->empty()) + posted = LLEventStream::post(event); if (!posted) { // if the event was not handled we will save it for later so that it can @@ -530,7 +534,7 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, { if (!mEventHistory.empty()) { - if (listener(mEventHistory.front())) + if (listener(mEventHistory.front()) || LLCoros::get_consuming()) { mEventHistory.pop_front(); } -- cgit v1.2.3 From 2ee22ed26483381bb13bc840090bad1897b4fd27 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 18 Dec 2015 18:06:32 -0500 Subject: MAINT-5976: Fix bug in LLCoros::set_consuming() mechanism. The original implementation of set_consuming() involved a bool* pointing to a local bool in VoidListener::operator()()'s stack frame. postAndSuspend() would set that bool (through the pointer) as soon as it returned from suspension. The trouble with that is that LLEventMailDrop potentially calls its new listener (fulfilling the future) immediately in the listen_impl() override -- in other words, way up at the top of postAndSuspend(), well before the code that sets the relevant bool. Instead, make the adapter formerly known as VoidListener bind the coroutine's get_consuming() value at adapter construction time (before listening on the LLEventPump), so that its operator()() has the coroutine's correct get_consuming() value to return. Eliminating the bool* makes the code both simpler AND more correct! This change makes that adapter very specific to coroutine usage. Rename it FutureListener and migrate it from lleventcoros.h into the .cpp file. Nobody else was using it anyway. Make corresponding changes to postAndSuspend2() and its WaitForEventOnHelper class -- whose name no longer corresponds to the function as it used to. Rename that one FutureListener2. The new FutureListener functionality, common to both these adapters, makes it useful to derive FutureListener2 from FutureListener. Introduce llmake(), a generic function to deduce template type arguments from function parameter types. This allows us to remove the voidlistener() and wfeoh() helper functions. Hiding VoidListener broke one of the lleventcoro_test.cpp tests. But that test was sort of a lame recap of an earlier implementation of postAndSuspend(), based on LLEventPump events. Recast that test to illustrate how to use a coroutine future to suspend a coroutine for something other than an LLEventPump. But that rubbed my nose in the fact that we MUST wrap future's context switching with proper management of the current coroutine. Introduce LLCoros::Future, which wraps boost::dcoroutines::future. Use LLCoros::Future in postAndSuspend() and postAndSuspend2(). --- indra/llcommon/llcoros.h | 48 +++++++++++ indra/llcommon/lleventcoro.cpp | 135 ++++++++++++++++-------------- indra/llcommon/lleventcoro.h | 30 ------- indra/llcommon/llmake.h | 63 ++++++++++++++ indra/llcommon/tests/lleventcoro_test.cpp | 62 +++++++------- 5 files changed, 219 insertions(+), 119 deletions(-) create mode 100644 indra/llcommon/llmake.h (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 0b1f58f48e..f2fb7f1e70 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -30,6 +30,7 @@ #define LL_LLCOROS_H #include +#include #include "llsingleton.h" #include #include @@ -162,6 +163,15 @@ public: static void set_consuming(bool consuming); static bool get_consuming(); + /** + * Please do NOT directly use boost::dcoroutines::future! It is essential + * to maintain the "current" coroutine at every context switch. This + * Future wraps the essential boost::dcoroutines::future functionality + * with that maintenance. + */ + template + class Future; + private: LLCoros(); friend class LLSingleton; @@ -233,4 +243,42 @@ private: } // namespace llcoro +template +class LLCoros::Future +{ + typedef boost::dcoroutines::future dfuture; + +public: + Future(): + mFuture(get_self()) + {} + + typedef typename boost::dcoroutines::make_callback_result::type callback_t; + + callback_t make_callback() + { + return boost::dcoroutines::make_callback(mFuture); + } + + explicit operator bool() const + { + return bool(mFuture); + } + + bool operator!() const + { + return ! mFuture; + } + + T get() + { + // instantiate Suspending to manage the "current" coroutine + llcoro::Suspending suspended; + return *mFuture; + } + +private: + dfuture mFuture; +}; + #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 9b7e8fb65f..578a2b62c8 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,12 +34,11 @@ #include // std headers // external library headers -#include -#include // other Linden headers #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" +#include "llmake.h" #include "lleventfilter.h" @@ -145,6 +144,36 @@ void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) *pdest = value; } +/// For LLCoros::Future::make_callback(), the callback has a signature +/// like void callback(LLSD), which isn't a valid LLEventPump listener: such +/// listeners must return bool. +template +class FutureListener +{ +public: + // FutureListener is instantiated on the coroutine stack: the stack, in + // other words, that wants to suspend. + FutureListener(const LISTENER& listener): + mListener(listener), + // Capture the suspending coroutine's flag as a consuming or + // non-consuming listener. + mConsume(LLCoros::get_consuming()) + {} + + // operator()() is called on the main stack: the stack on which the + // expected event is fired. + bool operator()(const LLSD& event) + { + mListener(event); + // tell upstream LLEventPump whether listener consumed + return mConsume; + } + +protected: + LISTENER mListener; + bool mConsume; +}; + } // anonymous void llcoro::suspend() @@ -167,13 +196,13 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + LLCoros::Future future; // make a callback that will assign a value to the future, and listen on // the specified LLEventPump with that callback std::string listenerName(listenerNameForCoro()); LLTempBoundListener connection( replyPump.getPump().listen(listenerName, - voidlistener(boost::dcoroutines::make_callback(future)))); + llmake(future.make_callback()))); // skip the "post" part if requestPump is default-constructed if (requestPump) { @@ -192,67 +221,56 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName << " about to wait on LLEventPump " << replyPump.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLSD_consumed value; - { - // instantiate Suspending to manage the "current" coroutine - llcoro::Suspending suspended; - value = *future; - } // destroy Suspending as soon as we're back + // calling get() on the future makes us wait for it + LLSD value(future.get()); LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName - << " resuming with " << value.first << LL_ENDL; - // immediately set consumed according to consuming - *value.second = LLCoros::get_consuming(); + << " resuming with " << value << LL_ENDL; // returning should disconnect the connection - return value.first; + return value; } namespace { -typedef std::pair LLEventWithID_consumed; - /** - * This helper is specifically for the two-pump version of suspendUntilEventOn(). - * We use a single future object, but we want to listen on two pumps with it. - * Since we must still adapt from (the callable constructed by) - * boost::dcoroutines::make_callback() (void return) to provide an event - * listener (bool return), we've adapted VoidListener for the purpose. The - * basic idea is that we construct a distinct instance of WaitForEventOnHelper - * -- binding different instance data -- for each of the pumps. Then, when a - * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine - * that LLSD with its discriminator to feed the future object. + * This helper is specifically for postAndSuspend2(). We use a single future + * object, but we want to listen on two pumps with it. Since we must still + * adapt from the callable constructed by boost::dcoroutines::make_callback() + * (void return) to provide an event listener (bool return), we've adapted + * FutureListener for the purpose. The basic idea is that we construct a + * distinct instance of FutureListener2 -- binding different instance data -- + * for each of the pumps. Then, when a pump delivers an LLSD value to either + * FutureListener2, it can combine that LLSD with its discriminator to feed + * the future object. + * + * DISCRIM is a template argument so we can use llmake() rather than + * having to write our own argument-deducing helper function. */ -template -class WaitForEventOnHelper +template +class FutureListener2: public FutureListener { + typedef FutureListener super; + public: - WaitForEventOnHelper(const LISTENER& listener, int discriminator): - mListener(listener), + // instantiated on coroutine stack: the stack about to suspend + FutureListener2(const LISTENER& listener, DISCRIM discriminator): + super(listener), mDiscrim(discriminator) {} - // this signature is required for an LLEventPump listener + // called on main stack: the stack on which event is fired bool operator()(const LLSD& event) { - bool consumed = false; - // our future object is defined to accept LLEventWithID_consumed - mListener(LLEventWithID_consumed(LLEventWithID(event, mDiscrim), &consumed)); + // our future object is defined to accept LLEventWithID + super::mListener(LLEventWithID(event, mDiscrim)); // tell LLEventPump whether or not event was consumed - return consumed; + return super::mConsume; } + private: - LISTENER mListener; - const int mDiscrim; + const DISCRIM mDiscrim; }; -/// WaitForEventOnHelper type-inference helper -template -WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator) -{ - return WaitForEventOnHelper(listener, discriminator); -} - } // anonymous namespace llcoro @@ -266,16 +284,18 @@ LLEventWithID postAndSuspend2(const LLSD& event, const LLSD& replyPump1NamePath) { // declare the future - boost::dcoroutines::future future(LLCoros::get_self()); + LLCoros::Future future; // either callback will assign a value to this future; listen on // each specified LLEventPump with a callback std::string name(listenerNameForCoro()); LLTempBoundListener connection0( - replyPump0.getPump().listen(name + "a", - wfeoh(boost::dcoroutines::make_callback(future), 0))); + replyPump0.getPump().listen( + name + "a", + llmake(future.make_callback(), 0))); LLTempBoundListener connection1( - replyPump1.getPump().listen(name + "b", - wfeoh(boost::dcoroutines::make_callback(future), 1))); + replyPump1.getPump().listen( + name + "b", + llmake(future.make_callback(), 1))); // skip the "post" part if requestPump is default-constructed if (requestPump) { @@ -294,20 +314,13 @@ LLEventWithID postAndSuspend2(const LLSD& event, LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name << " about to wait on LLEventPumps " << replyPump0.getPump().getName() << ", " << replyPump1.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID_consumed value; - { - // instantiate Suspending to manage "current" coroutine - llcoro::Suspending suspended; - value = *future; - } // destroy Suspending as soon as we're back + // calling get() on the future makes us wait for it + LLEventWithID value(future.get()); LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name - << " resuming with (" << value.first.first - << ", " << value.first.second << ")" << LL_ENDL; - // tell LLEventPump whether we're consuming - *value.second = LLCoros::get_consuming(); + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; // returning should disconnect both connections - return value.first; + return value; } LLSD errorException(const LLEventWithID& result, const std::string& desc) diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index acf2ad24a4..2105faf861 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -76,36 +76,6 @@ private: namespace llcoro { -typedef std::pair LLSD_consumed; - -/// This is an adapter for a signature like void LISTENER(const LLSD&), which -/// isn't a valid LLEventPump listener: such listeners should return bool. -template -class VoidListener -{ -public: - VoidListener(const LISTENER& listener): - mListener(listener) - {} - - bool operator()(const LLSD& event) - { - bool consumed = false; - mListener(LLSD_consumed(event, &consumed)); - // tell upstream LLEventPump whether listener consumed - return consumed; - } -private: - LISTENER mListener; -}; - -/// VoidListener helper function to infer the type of the LISTENER -template -VoidListener voidlistener(const LISTENER& listener) -{ - return VoidListener(listener); -} - /** * Yield control from a coroutine for one "mainloop" tick. If your coroutine * runs without suspending for nontrivial time, sprinkle in calls to this diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h new file mode 100644 index 0000000000..9a662a0640 --- /dev/null +++ b/indra/llcommon/llmake.h @@ -0,0 +1,63 @@ +/** + * @file llmake.h + * @author Nat Goodspeed + * @date 2015-12-18 + * @brief Generic llmake