diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 0b1f58f48ebcd06b737a192beffdc95b3a320349..f2fb7f1e708945ca2d2ef3f4ee5798b18fe8a445 100755
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -30,6 +30,7 @@
 #define LL_LLCOROS_H
 
 #include <boost/dcoroutine/coroutine.hpp>
+#include <boost/dcoroutine/future.hpp>
 #include "llsingleton.h"
 #include <boost/ptr_container/ptr_map.hpp>
 #include <boost/function.hpp>
@@ -162,6 +163,15 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
     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 <typename T>
+    class Future;
+
 private:
     LLCoros();
     friend class LLSingleton<LLCoros>;
@@ -233,4 +243,42 @@ class Suspending
 
 } // namespace llcoro
 
+template <typename T>
+class LLCoros::Future
+{
+    typedef boost::dcoroutines::future<T> dfuture;
+
+public:
+    Future():
+        mFuture(get_self())
+    {}
+
+    typedef typename boost::dcoroutines::make_callback_result<dfuture>::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 9b7e8fb65fdf126fc07652c6feef44c2782cc649..578a2b62c856152a3e58ec7e99a46c79df32e32f 100755
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -34,12 +34,11 @@
 #include <map>
 // std headers
 // external library headers
-#include <boost/dcoroutine/coroutine.hpp>
-#include <boost/dcoroutine/future.hpp>
 // 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<LLSD>::make_callback(), the callback has a signature
+/// like void callback(LLSD), which isn't a valid LLEventPump listener: such
+/// listeners must return bool.
+template <typename LISTENER>
+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<LLSD_consumed> future(LLCoros::get_self());
+    LLCoros::Future<LLSD> 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<FutureListener>(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, bool*> 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 <typename LISTENER>
-class WaitForEventOnHelper
+template <typename LISTENER, typename DISCRIM>
+class FutureListener2: public FutureListener<LISTENER>
 {
+    typedef FutureListener<LISTENER> 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 <typename LISTENER>
-WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
-{
-    return WaitForEventOnHelper<LISTENER>(listener, discriminator);
-}
-
 } // anonymous
 
 namespace llcoro
@@ -266,16 +284,18 @@ LLEventWithID postAndSuspend2(const LLSD& event,
                            const LLSD& replyPump1NamePath)
 {
     // declare the future
-    boost::dcoroutines::future<LLEventWithID_consumed> future(LLCoros::get_self());
+    LLCoros::Future<LLEventWithID> 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<FutureListener2>(future.make_callback(), 0)));
     LLTempBoundListener connection1(
-        replyPump1.getPump().listen(name + "b",
-                                    wfeoh(boost::dcoroutines::make_callback(future), 1)));
+        replyPump1.getPump().listen(
+            name + "b",
+            llmake<FutureListener2>(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 acf2ad24a4063e2ebd74683f3eb9d759991471bf..2105faf8615f899164352525f546aa1799559257 100755
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -76,36 +76,6 @@ class LLEventPumpOrPumpName
 namespace llcoro
 {
 
-typedef std::pair<LLSD, bool*> 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 <typename LISTENER>
-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 <typename LISTENER>
-VoidListener<LISTENER> voidlistener(const LISTENER& listener)
-{
-    return VoidListener<LISTENER>(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 0000000000000000000000000000000000000000..9a662a06408a3af5a9805bb409c8ac6b3f593e78
--- /dev/null
+++ b/indra/llcommon/llmake.h
@@ -0,0 +1,63 @@
+/**
+ * @file   llmake.h
+ * @author Nat Goodspeed
+ * @date   2015-12-18
+ * @brief  Generic llmake<Template>(arg) function to instantiate
+ *         Template<decltype(arg)>(arg).
+ *
+ *         Many of our class templates have an accompanying helper function to
+ *         make an instance with arguments of arbitrary type. llmake()
+ *         eliminates the need to declare a new helper function for every such
+ *         class template.
+ * 
+ *         also relevant:
+ *
+ *         Template parameter deduction for constructors
+ *         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html
+ *
+ *         https://github.com/viboes/std-make
+ *
+ *         but obviously we're not there yet.
+ *
+ * $LicenseInfo:firstyear=2015&license=viewerlgpl$
+ * Copyright (c) 2015, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLMAKE_H)
+#define LL_LLMAKE_H
+
+/*==========================================================================*|
+// When we allow ourselves to compile with C++11 features enabled, this form
+// should generically handle an arbitrary number of arguments.
+
+template <template<typename...> class CLASS_TEMPLATE, typename... ARGS>
+CLASS_TEMPLATE<ARGS...> llmake(ARGS && ... args)
+{
+    return CLASS_TEMPLATE<ARGS...>(std::forward<ARGS>(args)...);
+}
+|*==========================================================================*/
+
+// As of 2015-12-18, this is what we'll use instead. Add explicit overloads
+// for different numbers of template parameters as use cases arise.
+
+/**
+ * Usage: llmake<SomeTemplate>(arg)
+ *
+ * Deduces the type T of 'arg' and returns an instance of SomeTemplate<T>
+ * initialized with 'arg'. Assumes a constructor accepting T (by value,
+ * reference or whatever).
+ */
+template <template<typename> class CLASS_TEMPLATE, typename ARG1>
+CLASS_TEMPLATE<ARG1> llmake(const ARG1& arg1)
+{
+    return CLASS_TEMPLATE<ARG1>(arg1);
+}
+
+template <template<typename, typename> class CLASS_TEMPLATE, typename ARG1, typename ARG2>
+CLASS_TEMPLATE<ARG1, ARG2> llmake(const ARG1& arg1, const ARG2& arg2)
+{
+    return CLASS_TEMPLATE<ARG1, ARG2>(arg1, arg2);
+}
+
+#endif /* ! defined(LL_LLMAKE_H) */
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index da1439418f601b31ca67c799090db18cdfdb84c4..a459d17fb8b58b2692bf731910eb204116ab8495 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()