TThreadStateDestroy.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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_compiler_compat.hpp"
  17. #include "TGreenletGlobals.cpp"
  18. #include "TThreadState.hpp"
  19. #include "TThreadStateCreator.hpp"
  20. namespace greenlet {
  21. extern "C" {
  22. struct ThreadState_DestroyNoGIL
  23. {
  24. /**
  25. This function uses the same lock that the PendingCallback does
  26. */
  27. static void
  28. MarkGreenletDeadAndQueueCleanup(ThreadState* const state)
  29. {
  30. #if GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK
  31. // One rare platform.
  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) {
  64. return false;
  65. }
  66. LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
  67. if (state->has_main_greenlet()) {
  68. // mark the thread as dead ASAP.
  69. // this is racy! If we try to throw or switch to a
  70. // greenlet from this thread from some other thread before
  71. // we clear the state pointer, it won't realize the state
  72. // is dead which can crash the process.
  73. PyGreenlet* p(state->borrow_main_greenlet().borrow());
  74. assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr);
  75. dynamic_cast<MainGreenlet*>(p->pimpl)->thread_state(nullptr);
  76. return true;
  77. }
  78. return false;
  79. }
  80. static void
  81. AddToCleanupQueue(ThreadState* const state)
  82. {
  83. assert(state && state->has_main_greenlet());
  84. // NOTE: Because we're not holding the GIL here, some other
  85. // Python thread could run and call ``os.fork()``, which would
  86. // be bad if that happened while we are holding the cleanup
  87. // lock (it wouldn't function in the child process).
  88. // Make a best effort to try to keep the duration we hold the
  89. // lock short.
  90. // TODO: On platforms that support it, use ``pthread_atfork`` to
  91. // drop this lock.
  92. LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
  93. mod_globs->queue_to_destroy(state);
  94. if (mod_globs->thread_states_to_destroy.size() == 1) {
  95. // We added the first item to the queue. We need to schedule
  96. // the cleanup.
  97. // A size greater than 1 means that we have already added the pending call,
  98. // and in fact, it may be executing now.
  99. // If it is executing, our lock makes sure that it will see the item we just added
  100. // to the queue on its next iteration (after we release the lock)
  101. //
  102. // A size of 1 means there is no pending call, OR the pending call is
  103. // currently executing, has dropped the lock, and is deleting the last item
  104. // from the queue; its next iteration will go ahead and delete the item we just added.
  105. // And the pending call we schedule here will have no work to do.
  106. int result = AddPendingCall(
  107. PendingCallback_DestroyQueue,
  108. nullptr);
  109. if (result < 0) {
  110. // Hmm, what can we do here?
  111. fprintf(stderr,
  112. "greenlet: WARNING: failed in call to Py_AddPendingCall; "
  113. "expect a memory leak.\n");
  114. }
  115. }
  116. }
  117. static int
  118. PendingCallback_DestroyQueue(void* UNUSED(arg))
  119. {
  120. // We're may or may not be holding the GIL here (depending on
  121. // Py_GIL_DISABLED), so calls to ``os.fork()`` may or may not
  122. // be possible.
  123. while (1) {
  124. ThreadState* to_destroy;
  125. {
  126. LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
  127. if (mod_globs->thread_states_to_destroy.empty()) {
  128. break;
  129. }
  130. to_destroy = mod_globs->take_next_to_destroy();
  131. }
  132. assert(to_destroy);
  133. assert(to_destroy->has_main_greenlet());
  134. // Drop the lock while we do the actual deletion.
  135. // This allows other calls to MarkGreenletDeadAndQueueCleanup
  136. // to enter and add to our queue.
  137. DestroyOne(to_destroy);
  138. }
  139. return 0;
  140. }
  141. static void
  142. DestroyOne(const ThreadState* const state)
  143. {
  144. // May or may not be holding the GIL (depending on Py_GIL_DISABLED).
  145. // Passed a non-shared pointer to the actual thread state.
  146. // state -> main greenlet
  147. assert(state->has_main_greenlet());
  148. PyGreenlet* main(state->borrow_main_greenlet());
  149. // When we need to do cross-thread operations, we check this.
  150. // A NULL value means the thread died some time ago.
  151. // We do this here, rather than in a Python dealloc function
  152. // for the greenlet, in case there's still a reference out
  153. // there.
  154. dynamic_cast<MainGreenlet*>(main->pimpl)->thread_state(nullptr);
  155. delete state; // Deleting this runs the destructor, DECREFs the main greenlet.
  156. }
  157. static int AddPendingCall(int (*func)(void*), void* arg)
  158. {
  159. // If the interpreter is in the middle of finalizing, we can't add a
  160. // pending call. Trying to do so will end up in a SIGSEGV, as
  161. // Py_AddPendingCall will not be able to get the interpreter and will
  162. // try to dereference a NULL pointer. It's possible this can still
  163. // segfault if we happen to get context switched, and maybe we should
  164. // just always implement our own AddPendingCall, but I'd like to see if
  165. // this works first
  166. #if GREENLET_PY313
  167. if (Py_IsFinalizing()) {
  168. #else
  169. if (_Py_IsFinalizing()) {
  170. #endif
  171. #ifdef GREENLET_DEBUG
  172. // No need to log in the general case. Yes, we'll leak,
  173. // but we're shutting down so it should be ok.
  174. fprintf(stderr,
  175. "greenlet: WARNING: Interpreter is finalizing. Ignoring "
  176. "call to Py_AddPendingCall; \n");
  177. #endif
  178. return 0;
  179. }
  180. return Py_AddPendingCall(func, arg);
  181. }
  182. };
  183. };
  184. }; // namespace greenlet
  185. // The intent when GET_THREAD_STATE() is needed multiple times in a
  186. // function is to take a reference to its return value in a local
  187. // variable, to avoid the thread-local indirection. On some platforms
  188. // (macOS), accessing a thread-local involves a function call (plus an
  189. // initial function call in each function that uses a thread local);
  190. // in contrast, static volatile variables are at some pre-computed
  191. // offset.
  192. typedef greenlet::ThreadStateCreator<greenlet::ThreadState_DestroyNoGIL::MarkGreenletDeadAndQueueCleanup> ThreadStateCreator;
  193. static thread_local ThreadStateCreator g_thread_state_global;
  194. #define GET_THREAD_STATE() g_thread_state_global
  195. #endif //T_THREADSTATE_DESTROY