greenlet_cpython_add_pending.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #ifndef GREENLET_CPYTHON_ADD_PENDING_HPP
  2. #define GREENLET_CPYTHON_ADD_PENDING_HPP
  3. #if (PY_VERSION_HEX >= 0x30800A0 && PY_VERSION_HEX < 0x3090000) && !(defined(_WIN32) || defined(WIN32))
  4. // XXX: From Python 3.8a3 [1] up until Python 3.9a6 [2][3],
  5. // ``Py_AddPendingCall`` would try to produce a Python exception if
  6. // the interpreter was in the beginning of shutting down when this
  7. // function is called. However, ``Py_AddPendingCall`` doesn't require
  8. // the GIL, and we are absolutely not holding it when we make that
  9. // call. That means that trying to create the Python exception is
  10. // using the C API in an undefined state; here the C API detects this
  11. // and aborts the process with an error ("Fatal Python error: Python
  12. // memory allocator called without holding the GIL": Add ->
  13. // PyErr_SetString -> PyUnicode_New -> PyObject_Malloc). This arises
  14. // (obviously) in multi-threaded programs and happens if one thread is
  15. // exiting and cleaning up its thread-local data while the other
  16. // thread is trying to shut down the interpreter. A crash on shutdown
  17. // is still a crash and could result in data loss (e.g., daemon
  18. // threads are still running, pending signal handlers may be present,
  19. // buffers may not be flushed, there may be __del__ that need run,
  20. // etc), so we have to work around it.
  21. //
  22. // Of course, we can (and do) check for whether the interpreter is
  23. // shutting down before calling ``Py_AddPendingCall``, but that's a
  24. // race condition since we don't hold the GIL, and so we may not
  25. // actually get the right answer. Plus, ``Py_FinalizeEx`` actually
  26. // calls ``_Py_FinishPendingCalls`` (which sets the pending->finishing
  27. // flag, which is used to gate creating the exceptioen) *before*
  28. // publishing any other data that would let us detect the shutdown
  29. // (such as runtime->finalizing). So that point is moot.
  30. //
  31. // Our solution for those versions is to inline the same code, without
  32. // the problematic bit that sets the exception. Unfortunately, all of
  33. // the structure definitions are private/opaque, *and* we can't
  34. // actually count on being able to include their definitions from
  35. // ``internal/pycore_*``, because on some platforms those header files
  36. // are incomplete (i.e., on macOS with macports 3.8, the includes are
  37. // fine, but on Ubuntu jammy with 3.8 from ppa:deadsnakes or GitHub
  38. // Actions 3.8 (I think it's Ubuntu 18.04), they con't be used; at
  39. // least, I couldn't get them to work). So we need to define the
  40. // structures and _PyRuntime data member ourself. Yet more
  41. // unfortunately, _PyRuntime won't link on Windows, so we can only do
  42. // this on other platforms.
  43. //
  44. // [1] https://github.com/python/cpython/commit/842a2f07f2f08a935ef470bfdaeef40f87490cfc
  45. // [2] https://github.com/python/cpython/commit/cfc3c2f8b34d3864717ab584c5b6c260014ba55a
  46. // [3] https://github.com/python/cpython/issues/81308
  47. # define GREENLET_BROKEN_PY_ADD_PENDING 1
  48. // When defining these structures, the important thing is to get
  49. // binary compatibility, i.e., structure layout. For that, we only
  50. // need to define fields up to the ones we use; after that they're
  51. // irrelevant UNLESS the structure is included in another structure
  52. // *before* the structure we're interested in --- in that case, it
  53. // must be complete. Ellipsis indicate elided trailing members.
  54. // Pointer types are changed to void* to keep from having to define
  55. // more structures.
  56. // From "internal/pycore_atomic.h"
  57. // There are several different definitions of this, including the
  58. // plain ``int`` version, a ``volatile int`` and an ``_Atomic int``
  59. // I don't think any of those change the size/layout.
  60. typedef struct _Py_atomic_int {
  61. volatile int _value;
  62. } _Py_atomic_int;
  63. // This needs too much infrastructure, so we just do a regular store.
  64. #define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \
  65. (ATOMIC_VAL)->_value = NEW_VAL
  66. // From "internal/pycore_pymem.h"
  67. #define NUM_GENERATIONS 3
  68. struct gc_generation {
  69. PyGC_Head head; // We already have this defined.
  70. int threshold;
  71. int count;
  72. };
  73. struct gc_generation_stats {
  74. Py_ssize_t collections;
  75. Py_ssize_t collected;
  76. Py_ssize_t uncollectable;
  77. };
  78. struct _gc_runtime_state {
  79. void *trash_delete_later;
  80. int trash_delete_nesting;
  81. int enabled;
  82. int debug;
  83. struct gc_generation generations[NUM_GENERATIONS];
  84. void *generation0;
  85. struct gc_generation permanent_generation;
  86. struct gc_generation_stats generation_stats[NUM_GENERATIONS];
  87. int collecting;
  88. void *garbage;
  89. void *callbacks;
  90. Py_ssize_t long_lived_total;
  91. Py_ssize_t long_lived_pending;
  92. };
  93. // From "internal/pycore_pystate.h"
  94. struct _pending_calls {
  95. int finishing;
  96. PyThread_type_lock lock;
  97. _Py_atomic_int calls_to_do;
  98. int async_exc;
  99. #define NPENDINGCALLS 32
  100. struct {
  101. int (*func)(void *);
  102. void *arg;
  103. } calls[NPENDINGCALLS];
  104. int first;
  105. int last;
  106. };
  107. struct _ceval_runtime_state {
  108. int recursion_limit;
  109. int tracing_possible;
  110. _Py_atomic_int eval_breaker;
  111. _Py_atomic_int gil_drop_request;
  112. struct _pending_calls pending;
  113. // ...
  114. };
  115. typedef struct pyruntimestate {
  116. int preinitializing;
  117. int preinitialized;
  118. int core_initialized;
  119. int initialized;
  120. void *finalizing;
  121. struct pyinterpreters {
  122. PyThread_type_lock mutex;
  123. void *head;
  124. void *main;
  125. int64_t next_id;
  126. } interpreters;
  127. // XXX Remove this field once we have a tp_* slot.
  128. struct _xidregistry {
  129. PyThread_type_lock mutex;
  130. void *head;
  131. } xidregistry;
  132. unsigned long main_thread;
  133. #define NEXITFUNCS 32
  134. void (*exitfuncs[NEXITFUNCS])(void);
  135. int nexitfuncs;
  136. struct _gc_runtime_state gc;
  137. struct _ceval_runtime_state ceval;
  138. // ...
  139. } _PyRuntimeState;
  140. #define SIGNAL_PENDING_CALLS(ceval) \
  141. do { \
  142. _Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 1); \
  143. _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \
  144. } while (0)
  145. extern _PyRuntimeState _PyRuntime;
  146. #else
  147. # define GREENLET_BROKEN_PY_ADD_PENDING 0
  148. #endif
  149. #endif