PageRenderTime 35ms CodeModel.GetById 25ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llcoros.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 166 lines | 37 code | 10 blank | 119 comment | 0 complexity | 0bc52c798fef9b5488c16c42801f64b1 MD5 | raw file
  1/**
  2 * @file   llcoros.h
  3 * @author Nat Goodspeed
  4 * @date   2009-06-02
  5 * @brief  Manage running boost::coroutine instances
  6 * 
  7 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#if ! defined(LL_LLCOROS_H)
 30#define LL_LLCOROS_H
 31
 32#include <boost/coroutine/coroutine.hpp>
 33#include "llsingleton.h"
 34#include <boost/ptr_container/ptr_map.hpp>
 35#include <string>
 36#include <boost/preprocessor/repetition/enum_params.hpp>
 37#include <boost/preprocessor/repetition/enum_binary_params.hpp>
 38#include <boost/preprocessor/iteration/local.hpp>
 39#include <stdexcept>
 40
 41/**
 42 * Registry of named Boost.Coroutine instances
 43 *
 44 * The Boost.Coroutine library supports the general case of a coroutine
 45 * accepting arbitrary parameters and yielding multiple (sets of) results. For
 46 * such use cases, it's natural for the invoking code to retain the coroutine
 47 * instance: the consumer repeatedly calls into the coroutine, perhaps passing
 48 * new parameter values, prompting it to yield its next result.
 49 *
 50 * Our typical coroutine usage is different, though. For us, coroutines
 51 * provide an alternative to the @c Responder pattern. Our typical coroutine
 52 * has @c void return, invoked in fire-and-forget mode: the handler for some
 53 * user gesture launches the coroutine and promptly returns to the main loop.
 54 * The coroutine initiates some action that will take multiple frames (e.g. a
 55 * capability request), waits for its result, processes it and silently steals
 56 * away.
 57 *
 58 * This usage poses two (related) problems:
 59 *
 60 * # Who should own the coroutine instance? If it's simply local to the
 61 *   handler code that launches it, return from the handler will destroy the
 62 *   coroutine object, terminating the coroutine.
 63 * # Once the coroutine terminates, in whatever way, who's responsible for
 64 *   cleaning up the coroutine object?
 65 *
 66 * LLCoros is a Singleton collection of currently-active coroutine instances.
 67 * Each has a name. You ask LLCoros to launch a new coroutine with a suggested
 68 * name prefix; from your prefix it generates a distinct name, registers the
 69 * new coroutine and returns the actual name.
 70 *
 71 * The name can be used to kill off the coroutine prematurely, if needed. It
 72 * can also provide diagnostic info: we can look up the name of the
 73 * currently-running coroutine.
 74 *
 75 * Finally, the next frame ("mainloop" event) after the coroutine terminates,
 76 * LLCoros will notice its demise and destroy it.
 77 */
 78class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
 79{
 80public:
 81    /// Canonical boost::coroutines::coroutine signature we use
 82    typedef boost::coroutines::coroutine<void()> coro;
 83    /// Canonical 'self' type
 84    typedef coro::self self;
 85
 86    /**
 87     * Create and start running a new coroutine with specified name. The name
 88     * string you pass is a suggestion; it will be tweaked for uniqueness. The
 89     * actual name is returned to you.
 90     *
 91     * Usage looks like this, for (e.g.) two coroutine parameters:
 92     * @code
 93     * class MyClass
 94     * {
 95     * public:
 96     *     ...
 97     *     // Do NOT NOT NOT accept reference params other than 'self'!
 98     *     // Pass by value only!
 99     *     void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
100     *     ...
101     * };
102     * ...
103     * std::string name = LLCoros::instance().launch(
104     *    "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
105     *                          "somestring", LLSD(17));
106     * @endcode
107     *
108     * Your function/method must accept LLCoros::self& as its first parameter.
109     * It can accept any other parameters you want -- but ONLY BY VALUE!
110     * Other reference parameters are a BAD IDEA! You Have Been Warned. See
111     * DEV-32777 comments for an explanation.
112     *
113     * Pass a callable that accepts the single LLCoros::self& parameter. It
114     * may work to pass a free function whose only parameter is 'self'; for
115     * all other cases use boost::bind(). Of course, for a non-static class
116     * method, the first parameter must be the class instance. Use the
117     * placeholder _1 for the 'self' parameter. Any other parameters should be
118     * passed via the bind() expression.
119     *
120     * launch() tweaks the suggested name so it won't collide with any
121     * existing coroutine instance, creates the coroutine instance, registers
122     * it with the tweaked name and runs it until its first wait. At that
123     * point it returns the tweaked name.
124     */
125    template <typename CALLABLE>
126    std::string launch(const std::string& prefix, const CALLABLE& callable)
127    {
128        return launchImpl(prefix, new coro(callable));
129    }
130
131    /**
132     * Abort a running coroutine by name. Normally, when a coroutine either
133     * runs to completion or terminates with an exception, LLCoros quietly
134     * cleans it up. This is for use only when you must explicitly interrupt
135     * one prematurely. Returns @c true if the specified name was found and
136     * still running at the time.
137     */
138    bool kill(const std::string& name);
139
140    /**
141     * From within a coroutine, pass its @c self object to look up the
142     * (tweaked) name string by which this coroutine is registered. Returns
143     * the empty string if not found (e.g. if the coroutine was launched by
144     * hand rather than using LLCoros::launch()).
145     */
146    template <typename COROUTINE_SELF>
147    std::string getName(const COROUTINE_SELF& self) const
148    {
149        return getNameByID(self.get_id());
150    }
151
152    /// getName() by self.get_id()
153    std::string getNameByID(const void* self_id) const;
154
155private:
156    friend class LLSingleton<LLCoros>;
157    LLCoros();
158    std::string launchImpl(const std::string& prefix, coro* newCoro);
159    std::string generateDistinctName(const std::string& prefix) const;
160    bool cleanup(const LLSD&);
161
162    typedef boost::ptr_map<std::string, coro> CoroMap;
163    CoroMap mCoros;
164};
165
166#endif /* ! defined(LL_LLCOROS_H) */