diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2015-12-18 18:06:32 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2015-12-18 18:06:32 -0500 |
commit | 2ee22ed26483381bb13bc840090bad1897b4fd27 (patch) | |
tree | 06e7f052fd44cfa5f848a613b9b3179d2ec27a6e /indra/llcommon/tests/lleventcoro_test.cpp | |
parent | 4e8edebe13de9d638f1236bfd6aeddadf508de38 (diff) |
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<T>, which wraps boost::dcoroutines::future<T>.
Use LLCoros::Future<T> in postAndSuspend() and postAndSuspend2().
Diffstat (limited to 'indra/llcommon/tests/lleventcoro_test.cpp')
-rwxr-xr-x | indra/llcommon/tests/lleventcoro_test.cpp | 62 |
1 files changed, 34 insertions, 28 deletions
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index da1439418f..a459d17fb8 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -65,14 +65,11 @@ // 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 <boost/dcoroutine/coroutine.hpp> -// Normally, lleventcoro.h obviates future.hpp. We only include this because -// we implement a "by hand" test of future functionality. -#include <boost/dcoroutine/future.hpp> #include <boost/bind.hpp> #include <boost/range.hpp> #include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> #include "linden_common.h" @@ -218,7 +215,7 @@ namespace tut // use static data so we can intersperse coroutine functions with the // tests that engage them ImmediateAPI immediateAPI; - std::string replyName, errorName, threw; + std::string replyName, errorName, threw, stringdata; LLSD result, errordata; int which; @@ -228,28 +225,40 @@ namespace tut replyName.clear(); errorName.clear(); threw.clear(); + stringdata.clear(); result = LLSD(); errordata = LLSD(); which = 0; } - void explicit_wait(boost::dcoroutines::coroutine<void()>::self& self) + void explicit_wait(boost::shared_ptr<LLCoros::Future<std::string>::callback_t>& cbp) { BEGIN { - // ... do whatever preliminary stuff must happen ... + // The point of this test is to verify / illustrate suspending a + // coroutine for something other than an LLEventPump. In other + // words, this shows how to adapt to any async operation that + // provides a callback-style notification (and prove that it + // works). + + LLCoros::Future<std::string> future; + // get the callback from that future + LLCoros::Future<std::string>::callback_t callback(future.make_callback()); + + // Perhaps we would send a request to a remote server and arrange + // for 'callback' to be called on response. Of course that might + // involve an adapter object from the actual callback signature to + // the signature of 'callback' -- in this case, void(std::string). + // For test purposes, instead of handing 'callback' (or the + // adapter) off to some I/O subsystem, we'll just pass it back to + // our caller. + cbp.reset(new LLCoros::Future<std::string>::callback_t(callback)); - // declare the future - boost::dcoroutines::future<llcoro::LLSD_consumed> future(self); - // 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 suspend for it + // calling get() on the future causes us to suspend debug("about to suspend"); - result = (*future).first; - ensure("Got it", future); + stringdata = future.get(); + ensure("Got it", bool(future)); } END } @@ -262,19 +271,16 @@ namespace tut 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<void()> coro(explicit_wait); - // Start the coroutine - coro(std::nothrow); - // When the coroutine waits for the event pump, it returns here. - debug("about to send"); - // Satisfy the suspend. - LLEventPumps::instance().obtain("source").post("received"); - // Now suspend for the coroutine to complete. - ensure("coroutine complete", ! coro); + boost::shared_ptr<LLCoros::Future<std::string>::callback_t> respond; + LLCoros::instance().launch("test<2>", + boost::bind(explicit_wait, boost::ref(respond))); + // When the coroutine waits for the future, it returns here. + debug("about to respond"); + // Now we're the I/O subsystem delivering a result. This immediately + // transfers control back to the coroutine. + (*respond)("received"); // ensure the coroutine ran and woke up again with the intended result - ensure_equals(result.asString(), "received"); + ensure_equals(stringdata, "received"); } void waitForEventOn1() |