PageRenderTime 104ms CodeModel.GetById 60ms app.highlight 14ms RepoModel.GetById 26ms app.codeStats 1ms

/mordor/fiber.h

http://github.com/mozy/mordor
C Header | 262 lines | 158 code | 43 blank | 61 comment | 2 complexity | 7c853728132fefd98bd375327ffb9260 MD5 | raw file
  1#ifndef __MORDOR_FIBER_H__
  2#define __MORDOR_FIBER_H__
  3// Copyright (c) 2009 - Mozy, Inc.
  4
  5#include <list>
  6
  7#include <boost/enable_shared_from_this.hpp>
  8#include <boost/function.hpp>
  9#include <boost/shared_ptr.hpp>
 10#include <boost/thread/mutex.hpp>
 11
 12#include "exception.h"
 13#include "thread_local_storage.h"
 14#include "version.h"
 15
 16// Fiber impl selection
 17
 18#ifdef X86_64
 19#   ifdef WINDOWS
 20#       define NATIVE_WINDOWS_FIBERS
 21#   elif defined(OSX)
 22#       define SETJMP_FIBERS
 23#   elif defined(POSIX)
 24#       define UCONTEXT_FIBERS
 25#   endif
 26#elif defined(X86)
 27#   ifdef WINDOWS
 28#       define NATIVE_WINDOWS_FIBERS
 29#   elif defined(OSX)
 30#       define SETJMP_FIBERS
 31#   elif defined(POSIX)
 32#       define UCONTEXT_FIBERS
 33#   endif
 34#elif defined(PPC)
 35#   define UCONTEXT_FIBERS
 36#elif defined(ARM)
 37#   define UCONTEXT_FIBERS
 38#else
 39#   error Platform not supported
 40#endif
 41
 42#ifdef UCONTEXT_FIBERS
 43#   ifdef __APPLE__
 44#       include <sys/ucontext.h>
 45#   else
 46#       include <ucontext.h>
 47#   endif
 48#endif
 49#ifdef SETJMP_FIBERS
 50#include <setjmp.h>
 51#endif
 52
 53#include "cxa_exception.h"
 54
 55namespace Mordor {
 56
 57/// Cooperative Thread
 58class Fiber : public boost::enable_shared_from_this<Fiber>
 59{
 60    template <class T> friend class FiberLocalStorageBase;
 61public:
 62    typedef boost::shared_ptr<Fiber> ptr;
 63    typedef boost::weak_ptr<Fiber> weak_ptr;
 64
 65    /// The current execution state of a Fiber
 66    enum State
 67    {
 68        /// Initialized, but not run
 69        INIT,
 70        /// Currently suspended
 71        HOLD,
 72        /// Currently executing
 73        EXEC,
 74        /// Terminated because of an exception
 75        EXCEPT,
 76        /// Terminated
 77        TERM
 78    };
 79
 80private:
 81    /// @brief Create a Fiber for the currently executing thread
 82    /// @pre No other Fiber object represents the currently executing thread
 83    /// @post state() == EXEC
 84    Fiber();
 85
 86public:
 87    /// @brief Create a new Fiber
 88    /// @param dg The initial function
 89    /// @param stacksize An explicit size for the stack.  This is initial size in virtual
 90    /// memory; physical/paging memory is not allocated until the actual pages
 91    /// are touched by the Fiber executing
 92    /// @post state() == INIT
 93    Fiber(boost::function<void ()> dg, size_t stacksize = 0);
 94    ~Fiber();
 95
 96    /// @brief Reset a Fiber to be used again, with a different initial function
 97    /// @param dg The new initial function
 98    /// @pre state() == INIT || state() == TERM || state() == EXCEPT
 99    /// @post state() == INIT
100    void reset(boost::function<void ()> dg);
101
102    /// @return The currently executing Fiber
103    static ptr getThis();
104
105    /// Call a Fiber
106
107    /// The Fiber is executed as a "child" Fiber of the currently executing
108    /// Fiber.  The currently executing Fiber is left in the EXEC state,
109    /// and this Fiber also transitions to the EXEC state by either calling
110    /// the initial function, or returning from yield() or yieldTo().
111    ///
112    /// call() does not return until the Fiber calls yield(), returns,
113    /// or throws an exception.
114    /// @pre state() == INIT || state() == HOLD
115    void call();
116
117    /// Inject an exception into a Fiber
118
119    /// The Fiber is executed, but instead of returning from yield() or
120    /// yieldTo(), exception is rethrown in the Fiber
121    /// @param exception The exception to be rethrown in the Fiber
122    /// @pre state() == INIT || state() == HOLD
123    void inject(boost::exception_ptr exception);
124
125    /// Yield execution to a specific Fiber
126
127    /// The Fiber is executed by replacing the currently executing Fiber.
128    /// The currently executing Fiber transitions to the HOLD state, and this
129    /// Fiber transitions to the EXEC state, by either calling the initial
130    /// function, or returning from yield() or yieldTo().
131    ///
132    /// yieldTo() does not return until another Fiber calls yieldTo() on the
133    /// currently executing Fiber, or yieldToCallerOnTerminate is true and
134    /// this fiber returns or throws an exception
135    /// @param yieldToCallerOnTerminate Whether to keep a weak reference back
136    /// to the currently executing Fiber in order to yield back to it when this
137    /// Fiber terminates
138    /// @return The Fiber that yielded back
139    /// (not necessarily the Fiber that was yielded to)
140    /// @pre state() == INIT || state() == HOLD
141    Fiber::ptr yieldTo(bool yieldToCallerOnTerminate = true);
142
143    /// Yield to the calling Fiber
144
145    /// yield() returns when the Fiber has been called or yielded to again
146    /// @pre This Fiber was executed by call()
147    static void yield();
148
149    /// The current execution state of the Fiber
150    State state();
151
152    /// Get the backtrace of a fiber
153
154    /// The fiber must not be currently executing.  If it's in a state other
155    /// than HOLD, the backtrace will be empty.
156    /// @pre state() != EXEC
157    std::vector<void *> backtrace();
158
159private:
160    Fiber::ptr yieldTo(bool yieldToCallerOnTerminate, State targetState);
161    static void setThis(Fiber *f);
162    static void entryPoint();
163    static void exitPoint(Fiber::ptr &cur, State targetState);
164
165    void allocStack();
166    void freeStack();
167    void initStack();
168
169    void switchContext(Fiber *toFiber);
170
171private:
172    boost::function<void ()> m_dg;
173    void *m_stack, *m_sp;
174    size_t m_stacksize;
175#ifdef UCONTEXT_FIBERS
176    ucontext_t m_ctx;
177#ifdef OSX
178    char m_mctx[sizeof(*(mcontext_t)0)];
179#endif
180#elif defined(SETJMP_FIBERS)
181    jmp_buf m_env;
182#endif
183#if defined(VALGRIND) && (defined(LINUX) || defined(OSX))
184    int m_valgrindStackId;
185#endif
186#ifdef CXXABIV1_EXCEPTION
187    ExceptionStack m_eh;
188#endif
189    State m_state, m_yielderNextState;
190    ptr m_outer, m_yielder;
191    weak_ptr m_terminateOuter;
192    boost::exception_ptr m_exception;
193
194    static ThreadLocalStorage<Fiber *> t_fiber;
195
196    // FLS Support
197    static size_t flsAlloc();
198    static void flsFree(size_t key);
199    static void flsSet(size_t key, intptr_t value);
200    static intptr_t flsGet(size_t key);
201
202    std::vector<intptr_t> m_fls;
203};
204
205std::ostream &operator<<(std::ostream &os, Fiber::State state);
206
207template <class T>
208class FiberLocalStorageBase : boost::noncopyable
209{
210public:
211    FiberLocalStorageBase()
212    {
213        m_key = Fiber::flsAlloc();
214    }
215
216    ~FiberLocalStorageBase()
217    {
218        Fiber::flsFree(m_key);
219    }
220
221    typename boost::enable_if_c<sizeof(T) <= sizeof(intptr_t)>::type set(const T &t)
222    {
223        Fiber::flsSet(m_key, (intptr_t)t);
224    }
225
226    T get() const
227    {
228#ifdef WINDOWS
229#pragma warning(push)
230#pragma warning(disable: 4800)
231#endif
232        return (T)Fiber::flsGet(m_key);
233#ifdef WINDOWS
234#pragma warning(pop)
235#endif
236    }
237
238    operator T() const { return get(); }
239
240private:
241    size_t m_key;
242};
243
244template <class T>
245class FiberLocalStorage : public FiberLocalStorageBase<T>
246{
247public:
248    T operator =(T t) { FiberLocalStorageBase<T>::set(t); return t; }
249};
250
251template <class T>
252class FiberLocalStorage<T *> : public FiberLocalStorageBase<T *>
253{
254public:
255    T * operator =(T *const t) { FiberLocalStorageBase<T *>::set(t); return t; }
256    T & operator*() { return *FiberLocalStorageBase<T *>::get(); }
257    T * operator->() { return FiberLocalStorageBase<T *>::get(); }
258};
259
260}
261
262#endif // __FIBER_H__