greenlet.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
  2. /* Format with:
  3. * clang-format -i --style=file src/greenlet/greenlet.c
  4. *
  5. *
  6. * Fix missing braces with:
  7. * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
  8. */
  9. #include <cstdlib>
  10. #include <string>
  11. #include <algorithm>
  12. #include <exception>
  13. #define PY_SSIZE_T_CLEAN
  14. #include <Python.h>
  15. #include "structmember.h" // PyMemberDef
  16. #include "greenlet_internal.hpp"
  17. // Code after this point can assume access to things declared in stdint.h,
  18. // including the fixed-width types. This goes for the platform-specific switch functions
  19. // as well.
  20. #include "greenlet_refs.hpp"
  21. #include "greenlet_slp_switch.hpp"
  22. #include "greenlet_thread_support.hpp"
  23. #include "TGreenlet.hpp"
  24. #include "TGreenletGlobals.cpp"
  25. #include "TGreenlet.cpp"
  26. #include "TMainGreenlet.cpp"
  27. #include "TUserGreenlet.cpp"
  28. #include "TBrokenGreenlet.cpp"
  29. #include "TExceptionState.cpp"
  30. #include "TPythonState.cpp"
  31. #include "TStackState.cpp"
  32. #include "TThreadState.hpp"
  33. #include "TThreadStateCreator.hpp"
  34. #include "TThreadStateDestroy.cpp"
  35. #include "PyGreenlet.cpp"
  36. #include "PyGreenletUnswitchable.cpp"
  37. #include "CObjects.cpp"
  38. using greenlet::LockGuard;
  39. using greenlet::LockInitError;
  40. using greenlet::PyErrOccurred;
  41. using greenlet::Require;
  42. using greenlet::g_handle_exit;
  43. using greenlet::single_result;
  44. using greenlet::Greenlet;
  45. using greenlet::UserGreenlet;
  46. using greenlet::MainGreenlet;
  47. using greenlet::BrokenGreenlet;
  48. using greenlet::ThreadState;
  49. using greenlet::PythonState;
  50. // ******* Implementation of things from included files
  51. template<typename T, greenlet::refs::TypeChecker TC>
  52. greenlet::refs::_BorrowedGreenlet<T, TC>& greenlet::refs::_BorrowedGreenlet<T, TC>::operator=(const greenlet::refs::BorrowedObject& other)
  53. {
  54. this->_set_raw_pointer(static_cast<PyObject*>(other));
  55. return *this;
  56. }
  57. template <typename T, greenlet::refs::TypeChecker TC>
  58. inline greenlet::refs::_BorrowedGreenlet<T, TC>::operator Greenlet*() const noexcept
  59. {
  60. if (!this->p) {
  61. return nullptr;
  62. }
  63. return reinterpret_cast<PyGreenlet*>(this->p)->pimpl;
  64. }
  65. template<typename T, greenlet::refs::TypeChecker TC>
  66. greenlet::refs::_BorrowedGreenlet<T, TC>::_BorrowedGreenlet(const BorrowedObject& p)
  67. : BorrowedReference<T, TC>(nullptr)
  68. {
  69. this->_set_raw_pointer(p.borrow());
  70. }
  71. template <typename T, greenlet::refs::TypeChecker TC>
  72. inline greenlet::refs::_OwnedGreenlet<T, TC>::operator Greenlet*() const noexcept
  73. {
  74. if (!this->p) {
  75. return nullptr;
  76. }
  77. return reinterpret_cast<PyGreenlet*>(this->p)->pimpl;
  78. }
  79. #ifdef __clang__
  80. # pragma clang diagnostic push
  81. # pragma clang diagnostic ignored "-Wmissing-field-initializers"
  82. # pragma clang diagnostic ignored "-Wwritable-strings"
  83. #elif defined(__GNUC__)
  84. # pragma GCC diagnostic push
  85. // warning: ISO C++ forbids converting a string constant to ‘char*’
  86. // (The python APIs aren't const correct and accept writable char*)
  87. # pragma GCC diagnostic ignored "-Wwrite-strings"
  88. #endif
  89. /***********************************************************
  90. A PyGreenlet is a range of C stack addresses that must be
  91. saved and restored in such a way that the full range of the
  92. stack contains valid data when we switch to it.
  93. Stack layout for a greenlet:
  94. | ^^^ |
  95. | older data |
  96. | |
  97. stack_stop . |_______________|
  98. . | |
  99. . | greenlet data |
  100. . | in stack |
  101. . * |_______________| . . _____________ stack_copy + stack_saved
  102. . | | | |
  103. . | data | |greenlet data|
  104. . | unrelated | | saved |
  105. . | to | | in heap |
  106. stack_start . | this | . . |_____________| stack_copy
  107. | greenlet |
  108. | |
  109. | newer data |
  110. | vvv |
  111. Note that a greenlet's stack data is typically partly at its correct
  112. place in the stack, and partly saved away in the heap, but always in
  113. the above configuration: two blocks, the more recent one in the heap
  114. and the older one still in the stack (either block may be empty).
  115. Greenlets are chained: each points to the previous greenlet, which is
  116. the one that owns the data currently in the C stack above my
  117. stack_stop. The currently running greenlet is the first element of
  118. this chain. The main (initial) greenlet is the last one. Greenlets
  119. whose stack is entirely in the heap can be skipped from the chain.
  120. The chain is not related to execution order, but only to the order
  121. in which bits of C stack happen to belong to greenlets at a particular
  122. point in time.
  123. The main greenlet doesn't have a stack_stop: it is responsible for the
  124. complete rest of the C stack, and we don't know where it begins. We
  125. use (char*) -1, the largest possible address.
  126. States:
  127. stack_stop == NULL && stack_start == NULL: did not start yet
  128. stack_stop != NULL && stack_start == NULL: already finished
  129. stack_stop != NULL && stack_start != NULL: active
  130. The running greenlet's stack_start is undefined but not NULL.
  131. ***********************************************************/
  132. /***********************************************************/
  133. /* Some functions must not be inlined:
  134. * slp_restore_state, when inlined into slp_switch might cause
  135. it to restore stack over its own local variables
  136. * slp_save_state, when inlined would add its own local
  137. variables to the saved stack, wasting space
  138. * slp_switch, cannot be inlined for obvious reasons
  139. * g_initialstub, when inlined would receive a pointer into its
  140. own stack frame, leading to incomplete stack save/restore
  141. g_initialstub is a member function and declared virtual so that the
  142. compiler always calls it through a vtable.
  143. slp_save_state and slp_restore_state are also member functions. They
  144. are called from trampoline functions that themselves are declared as
  145. not eligible for inlining.
  146. */
  147. extern "C" {
  148. static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref)
  149. {
  150. return switching_thread_state->slp_save_state(stackref);
  151. }
  152. static void GREENLET_NOINLINE(slp_restore_state_trampoline)()
  153. {
  154. switching_thread_state->slp_restore_state();
  155. }
  156. }
  157. /***********************************************************/
  158. #include "PyModule.cpp"
  159. static PyObject*
  160. greenlet_internal_mod_init() noexcept
  161. {
  162. static void* _PyGreenlet_API[PyGreenlet_API_pointers];
  163. try {
  164. CreatedModule m(greenlet_module_def);
  165. Require(PyType_Ready(&PyGreenlet_Type));
  166. Require(PyType_Ready(&PyGreenletUnswitchable_Type));
  167. mod_globs = new greenlet::GreenletGlobals;
  168. ThreadState::init();
  169. m.PyAddObject("greenlet", PyGreenlet_Type);
  170. m.PyAddObject("UnswitchableGreenlet", PyGreenletUnswitchable_Type);
  171. m.PyAddObject("error", mod_globs->PyExc_GreenletError);
  172. m.PyAddObject("GreenletExit", mod_globs->PyExc_GreenletExit);
  173. m.PyAddObject("GREENLET_USE_GC", 1);
  174. m.PyAddObject("GREENLET_USE_TRACING", 1);
  175. m.PyAddObject("GREENLET_USE_CONTEXT_VARS", 1L);
  176. m.PyAddObject("GREENLET_USE_STANDARD_THREADING", 1L);
  177. OwnedObject clocks_per_sec = OwnedObject::consuming(PyLong_FromSsize_t(CLOCKS_PER_SEC));
  178. m.PyAddObject("CLOCKS_PER_SEC", clocks_per_sec);
  179. /* also publish module-level data as attributes of the greentype. */
  180. // XXX: This is weird, and enables a strange pattern of
  181. // confusing the class greenlet with the module greenlet; with
  182. // the exception of (possibly) ``getcurrent()``, this
  183. // shouldn't be encouraged so don't add new items here.
  184. for (const char* const* p = copy_on_greentype; *p; p++) {
  185. OwnedObject o = m.PyRequireAttr(*p);
  186. PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o.borrow());
  187. }
  188. /*
  189. * Expose C API
  190. */
  191. /* types */
  192. _PyGreenlet_API[PyGreenlet_Type_NUM] = (void*)&PyGreenlet_Type;
  193. /* exceptions */
  194. _PyGreenlet_API[PyExc_GreenletError_NUM] = (void*)mod_globs->PyExc_GreenletError;
  195. _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void*)mod_globs->PyExc_GreenletExit;
  196. /* methods */
  197. _PyGreenlet_API[PyGreenlet_New_NUM] = (void*)PyGreenlet_New;
  198. _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void*)PyGreenlet_GetCurrent;
  199. _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void*)PyGreenlet_Throw;
  200. _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void*)PyGreenlet_Switch;
  201. _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void*)PyGreenlet_SetParent;
  202. /* Previously macros, but now need to be functions externally. */
  203. _PyGreenlet_API[PyGreenlet_MAIN_NUM] = (void*)Extern_PyGreenlet_MAIN;
  204. _PyGreenlet_API[PyGreenlet_STARTED_NUM] = (void*)Extern_PyGreenlet_STARTED;
  205. _PyGreenlet_API[PyGreenlet_ACTIVE_NUM] = (void*)Extern_PyGreenlet_ACTIVE;
  206. _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM] = (void*)Extern_PyGreenlet_GET_PARENT;
  207. /* XXX: Note that our module name is ``greenlet._greenlet``, but for
  208. backwards compatibility with existing C code, we need the _C_API to
  209. be directly in greenlet.
  210. */
  211. const NewReference c_api_object(Require(
  212. PyCapsule_New(
  213. (void*)_PyGreenlet_API,
  214. "greenlet._C_API",
  215. NULL)));
  216. m.PyAddObject("_C_API", c_api_object);
  217. assert(c_api_object.REFCNT() == 2);
  218. // cerr << "Sizes:"
  219. // << "\n\tGreenlet : " << sizeof(Greenlet)
  220. // << "\n\tUserGreenlet : " << sizeof(UserGreenlet)
  221. // << "\n\tMainGreenlet : " << sizeof(MainGreenlet)
  222. // << "\n\tExceptionState : " << sizeof(greenlet::ExceptionState)
  223. // << "\n\tPythonState : " << sizeof(greenlet::PythonState)
  224. // << "\n\tStackState : " << sizeof(greenlet::StackState)
  225. // << "\n\tSwitchingArgs : " << sizeof(greenlet::SwitchingArgs)
  226. // << "\n\tOwnedObject : " << sizeof(greenlet::refs::OwnedObject)
  227. // << "\n\tBorrowedObject : " << sizeof(greenlet::refs::BorrowedObject)
  228. // << "\n\tPyGreenlet : " << sizeof(PyGreenlet)
  229. // << endl;
  230. return m.borrow(); // But really it's the main reference.
  231. }
  232. catch (const LockInitError& e) {
  233. PyErr_SetString(PyExc_MemoryError, e.what());
  234. return NULL;
  235. }
  236. catch (const PyErrOccurred&) {
  237. return NULL;
  238. }
  239. }
  240. extern "C" {
  241. PyMODINIT_FUNC
  242. PyInit__greenlet(void)
  243. {
  244. return greenlet_internal_mod_init();
  245. }
  246. }; // extern C
  247. #ifdef __clang__
  248. # pragma clang diagnostic pop
  249. #elif defined(__GNUC__)
  250. # pragma GCC diagnostic pop
  251. #endif