TThreadStateDestroy.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
  2. /**
  3. * Implementation of the ThreadState destructors.
  4. *
  5. * Format with:
  6. * clang-format -i --style=file src/greenlet/greenlet.c
  7. *
  8. *
  9. * Fix missing braces with:
  10. * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
  11. */
  12. #ifndef T_THREADSTATE_DESTROY
  13. #define T_THREADSTATE_DESTROY
  14. #include "TGreenlet.hpp"
  15. #include "greenlet_thread_support.hpp"
  16. #include "greenlet_cpython_add_pending.hpp"
  17. #include "greenlet_compiler_compat.hpp"
  18. #include "TGreenletGlobals.cpp"
  19. #include "TThreadState.hpp"
  20. #include "TThreadStateCreator.hpp"
  21. namespace greenlet {
  22. extern "C" {
  23. struct ThreadState_DestroyNoGIL
  24. {
  25. /**
  26. This function uses the same lock that the PendingCallback does
  27. */
  28. static void
  29. MarkGreenletDeadAndQueueCleanup(ThreadState* const state)
  30. {
  31. #if GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK
  32. return;
  33. #endif
  34. // We are *NOT* holding the GIL. Our thread is in the middle
  35. // of its death throes and the Python thread state is already
  36. // gone so we can't use most Python APIs. One that is safe is
  37. // ``Py_AddPendingCall``, unless the interpreter itself has
  38. // been torn down. There is a limited number of calls that can
  39. // be queued: 32 (NPENDINGCALLS) in CPython 3.10, so we
  40. // coalesce these calls using our own queue.
  41. if (!MarkGreenletDeadIfNeeded(state)) {
  42. // No state, or no greenlet
  43. return;
  44. }
  45. // XXX: Because we don't have the GIL, this is a race condition.
  46. if (!PyInterpreterState_Head()) {
  47. // We have to leak the thread state, if the
  48. // interpreter has shut down when we're getting
  49. // deallocated, we can't run the cleanup code that
  50. // deleting it would imply.
  51. return;
  52. }
  53. AddToCleanupQueue(state);
  54. }
  55. private:
  56. // If the state has an allocated main greenlet:
  57. // - mark the greenlet as dead by disassociating it from the state;
  58. // - return 1
  59. // Otherwise, return 0.
  60. static bool
  61. MarkGreenletDeadIfNeeded(ThreadState* const state)
  62. {
  63. if (state && state->has_main_greenlet()) {
  64. // mark the thread as dead ASAP.
  65. // this is racy! If we try to throw or switch to a
  66. // greenlet from this thread from some other thread before
  67. // we clear the state pointer, it won't realize the state
  68. // is dead which can crash the process.
  69. PyGreenlet* p(state->borrow_main_greenlet().borrow());
  70. assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr);
  71. dynamic_cast<MainGreenlet*>(p->pimpl)->thread_state(nullptr);
  72. return true;
  73. }
  74. return false;
  75. }
  76. static void
  77. AddToCleanupQueue(ThreadState* const state)
  78. {
  79. assert(state && state->has_main_greenlet());
  80. // NOTE: Because we're not holding the GIL here, some other
  81. // Python thread could run and call ``os.fork()``, which would
  82. // be bad if that happened while we are holding the cleanup
  83. // lock (it wouldn't function in the child process).
  84. // Make a best effort to try to keep the duration we hold the
  85. // lock short.
  86. // TODO: On platforms that support it, use ``pthread_atfork`` to
  87. // drop this lock.
  88. LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
  89. mod_globs->queue_to_destroy(state);
  90. if (mod_globs->thread_states_to_destroy.size() == 1) {
  91. // We added the first item to the queue. We need to schedule
  92. // the cleanup.
  93. // A size greater than 1 means that we have already added the pending call,
  94. // and in fact, it may be executing now.
  95. // If it is executing, our lock makes sure that it will see the item we just added
  96. // to the queue on its next iteration (after we release the lock)
  97. //
  98. // A size of 1 means there is no pending call, OR the pending call is
  99. // currently executing, has dropped the lock, and is deleting the last item
  100. // from the queue; its next iteration will go ahead and delete the item we just added.
  101. // And the pending call we schedule here will have no work to do.
  102. int result = AddPendingCall(
  103. PendingCallback_DestroyQueueWithGIL,
  104. nullptr);
  105. if (result < 0) {
  106. // Hmm, what can we do here?
  107. fprintf(stderr,
  108. "greenlet: WARNING: failed in call to Py_AddPendingCall; "
  109. "expect a memory leak.\n");
  110. }
  111. }
  112. }
  113. static int
  114. PendingCallback_DestroyQueueWithGIL(void* UNUSED(arg))
  115. {
  116. // We're holding the GIL here, so no Python code should be able to
  117. // run to call ``os.fork()``.
  118. while (1) {
  119. ThreadState* to_destroy;
  120. {
  121. LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
  122. if (mod_globs->thread_states_to_destroy.empty()) {
  123. break;
  124. }
  125. to_destroy = mod_globs->take_next_to_destroy();
  126. }
  127. assert(to_destroy);
  128. assert(to_destroy->has_main_greenlet());
  129. // Drop the lock while we do the actual deletion.
  130. // This allows other calls to MarkGreenletDeadAndQueueCleanup
  131. // to enter and add to our queue.
  132. DestroyOneWithGIL(to_destroy);
  133. }
  134. return 0;
  135. }
  136. static void
  137. DestroyOneWithGIL(const ThreadState* const state)
  138. {
  139. // Holding the GIL.
  140. // Passed a non-shared pointer to the actual thread state.
  141. // state -> main greenlet
  142. assert(state->has_main_greenlet());
  143. PyGreenlet* main(state->borrow_main_greenlet());
  144. // When we need to do cross-thread operations, we check this.
  145. // A NULL value means the thread died some time ago.
  146. // We do this here, rather than in a Python dealloc function
  147. // for the greenlet, in case there's still a reference out
  148. // there.
  149. dynamic_cast<MainGreenlet*>(main->pimpl)->thread_state(nullptr);
  150. delete state; // Deleting this runs the destructor, DECREFs the main greenlet.
  151. }
  152. // ensure this is actually defined.
  153. static_assert(GREENLET_BROKEN_PY_ADD_PENDING == 1 || GREENLET_BROKEN_PY_ADD_PENDING == 0,
  154. "GREENLET_BROKEN_PY_ADD_PENDING not defined correctly.");
  155. #if GREENLET_BROKEN_PY_ADD_PENDING
  156. static int _push_pending_call(struct _pending_calls *pending,
  157. int (*func)(void *), void *arg)
  158. {
  159. int i = pending->last;
  160. int j = (i + 1) % NPENDINGCALLS;
  161. if (j == pending->first) {
  162. return -1; /* Queue full */
  163. }
  164. pending->calls[i].func = func;
  165. pending->calls[i].arg = arg;
  166. pending->last = j;
  167. return 0;
  168. }
  169. static int AddPendingCall(int (*func)(void *), void *arg)
  170. {
  171. _PyRuntimeState *runtime = &_PyRuntime;
  172. if (!runtime) {
  173. // obviously impossible
  174. return 0;
  175. }
  176. struct _pending_calls *pending = &runtime->ceval.pending;
  177. if (!pending->lock) {
  178. return 0;
  179. }
  180. int result = 0;
  181. PyThread_acquire_lock(pending->lock, WAIT_LOCK);
  182. if (!pending->finishing) {
  183. result = _push_pending_call(pending, func, arg);
  184. }
  185. PyThread_release_lock(pending->lock);
  186. SIGNAL_PENDING_CALLS(&runtime->ceval);
  187. return result;
  188. }
  189. #else
  190. // Python < 3.8 or >= 3.9
  191. static int AddPendingCall(int (*func)(void*), void* arg)
  192. {
  193. // If the interpreter is in the middle of finalizing, we can't add a
  194. // pending call. Trying to do so will end up in a SIGSEGV, as
  195. // Py_AddPendingCall will not be able to get the interpreter and will
  196. // try to dereference a NULL pointer. It's possible this can still
  197. // segfault if we happen to get context switched, and maybe we should
  198. // just always implement our own AddPendingCall, but I'd like to see if
  199. // this works first
  200. #if GREENLET_PY313
  201. if (Py_IsFinalizing()) {
  202. #else
  203. if (_Py_IsFinalizing()) {
  204. #endif
  205. #ifdef GREENLET_DEBUG
  206. // No need to log in the general case. Yes, we'll leak,
  207. // but we're shutting down so it should be ok.
  208. fprintf(stderr,
  209. "greenlet: WARNING: Interpreter is finalizing. Ignoring "
  210. "call to Py_AddPendingCall; \n");
  211. #endif
  212. return 0;
  213. }
  214. return Py_AddPendingCall(func, arg);
  215. }
  216. #endif
  217. };
  218. };
  219. }; // namespace greenlet
  220. // The intent when GET_THREAD_STATE() is needed multiple times in a
  221. // function is to take a reference to its return value in a local
  222. // variable, to avoid the thread-local indirection. On some platforms
  223. // (macOS), accessing a thread-local involves a function call (plus an
  224. // initial function call in each function that uses a thread local);
  225. // in contrast, static volatile variables are at some pre-computed
  226. // offset.
  227. typedef greenlet::ThreadStateCreator<greenlet::ThreadState_DestroyNoGIL::MarkGreenletDeadAndQueueCleanup> ThreadStateCreator;
  228. static thread_local ThreadStateCreator g_thread_state_global;
  229. #define GET_THREAD_STATE() g_thread_state_global
  230. #endif //T_THREADSTATE_DESTROY