PageRenderTime 65ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/fiber.h

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