You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

260 lines
9.1 KiB

  1. /*
  2. * Implementation of the Global Interpreter Lock (GIL).
  3. */
  4. #include <stdlib.h>
  5. #include <errno.h>
  6. #include "pycore_atomic.h"
  7. /* First some general settings */
  8. #define INTERVAL (_PyRuntime.ceval.gil.interval >= 1 ? _PyRuntime.ceval.gil.interval : 1)
  9. /*
  10. Notes about the implementation:
  11. - The GIL is just a boolean variable (locked) whose access is protected
  12. by a mutex (gil_mutex), and whose changes are signalled by a condition
  13. variable (gil_cond). gil_mutex is taken for short periods of time,
  14. and therefore mostly uncontended.
  15. - In the GIL-holding thread, the main loop (PyEval_EvalFrameEx) must be
  16. able to release the GIL on demand by another thread. A volatile boolean
  17. variable (gil_drop_request) is used for that purpose, which is checked
  18. at every turn of the eval loop. That variable is set after a wait of
  19. `interval` microseconds on `gil_cond` has timed out.
  20. [Actually, another volatile boolean variable (eval_breaker) is used
  21. which ORs several conditions into one. Volatile booleans are
  22. sufficient as inter-thread signalling means since Python is run
  23. on cache-coherent architectures only.]
  24. - A thread wanting to take the GIL will first let pass a given amount of
  25. time (`interval` microseconds) before setting gil_drop_request. This
  26. encourages a defined switching period, but doesn't enforce it since
  27. opcodes can take an arbitrary time to execute.
  28. The `interval` value is available for the user to read and modify
  29. using the Python API `sys.{get,set}switchinterval()`.
  30. - When a thread releases the GIL and gil_drop_request is set, that thread
  31. ensures that another GIL-awaiting thread gets scheduled.
  32. It does so by waiting on a condition variable (switch_cond) until
  33. the value of last_holder is changed to something else than its
  34. own thread state pointer, indicating that another thread was able to
  35. take the GIL.
  36. This is meant to prohibit the latency-adverse behaviour on multi-core
  37. machines where one thread would speculatively release the GIL, but still
  38. run and end up being the first to re-acquire it, making the "timeslices"
  39. much longer than expected.
  40. (Note: this mechanism is enabled with FORCE_SWITCHING above)
  41. */
  42. #include "condvar.h"
  43. #define MUTEX_INIT(mut) \
  44. if (PyMUTEX_INIT(&(mut))) { \
  45. Py_FatalError("PyMUTEX_INIT(" #mut ") failed"); };
  46. #define MUTEX_FINI(mut) \
  47. if (PyMUTEX_FINI(&(mut))) { \
  48. Py_FatalError("PyMUTEX_FINI(" #mut ") failed"); };
  49. #define MUTEX_LOCK(mut) \
  50. if (PyMUTEX_LOCK(&(mut))) { \
  51. Py_FatalError("PyMUTEX_LOCK(" #mut ") failed"); };
  52. #define MUTEX_UNLOCK(mut) \
  53. if (PyMUTEX_UNLOCK(&(mut))) { \
  54. Py_FatalError("PyMUTEX_UNLOCK(" #mut ") failed"); };
  55. #define COND_INIT(cond) \
  56. if (PyCOND_INIT(&(cond))) { \
  57. Py_FatalError("PyCOND_INIT(" #cond ") failed"); };
  58. #define COND_FINI(cond) \
  59. if (PyCOND_FINI(&(cond))) { \
  60. Py_FatalError("PyCOND_FINI(" #cond ") failed"); };
  61. #define COND_SIGNAL(cond) \
  62. if (PyCOND_SIGNAL(&(cond))) { \
  63. Py_FatalError("PyCOND_SIGNAL(" #cond ") failed"); };
  64. #define COND_WAIT(cond, mut) \
  65. if (PyCOND_WAIT(&(cond), &(mut))) { \
  66. Py_FatalError("PyCOND_WAIT(" #cond ") failed"); };
  67. #define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
  68. { \
  69. int r = PyCOND_TIMEDWAIT(&(cond), &(mut), (microseconds)); \
  70. if (r < 0) \
  71. Py_FatalError("PyCOND_WAIT(" #cond ") failed"); \
  72. if (r) /* 1 == timeout, 2 == impl. can't say, so assume timeout */ \
  73. timeout_result = 1; \
  74. else \
  75. timeout_result = 0; \
  76. } \
  77. #define DEFAULT_INTERVAL 5000
  78. static void _gil_initialize(struct _gil_runtime_state *state)
  79. {
  80. _Py_atomic_int uninitialized = {-1};
  81. state->locked = uninitialized;
  82. state->interval = DEFAULT_INTERVAL;
  83. }
  84. static int gil_created(void)
  85. {
  86. return (_Py_atomic_load_explicit(&_PyRuntime.ceval.gil.locked,
  87. _Py_memory_order_acquire)
  88. ) >= 0;
  89. }
  90. static void create_gil(void)
  91. {
  92. MUTEX_INIT(_PyRuntime.ceval.gil.mutex);
  93. #ifdef FORCE_SWITCHING
  94. MUTEX_INIT(_PyRuntime.ceval.gil.switch_mutex);
  95. #endif
  96. COND_INIT(_PyRuntime.ceval.gil.cond);
  97. #ifdef FORCE_SWITCHING
  98. COND_INIT(_PyRuntime.ceval.gil.switch_cond);
  99. #endif
  100. _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder, 0);
  101. _Py_ANNOTATE_RWLOCK_CREATE(&_PyRuntime.ceval.gil.locked);
  102. _Py_atomic_store_explicit(&_PyRuntime.ceval.gil.locked, 0,
  103. _Py_memory_order_release);
  104. }
  105. static void destroy_gil(void)
  106. {
  107. /* some pthread-like implementations tie the mutex to the cond
  108. * and must have the cond destroyed first.
  109. */
  110. COND_FINI(_PyRuntime.ceval.gil.cond);
  111. MUTEX_FINI(_PyRuntime.ceval.gil.mutex);
  112. #ifdef FORCE_SWITCHING
  113. COND_FINI(_PyRuntime.ceval.gil.switch_cond);
  114. MUTEX_FINI(_PyRuntime.ceval.gil.switch_mutex);
  115. #endif
  116. _Py_atomic_store_explicit(&_PyRuntime.ceval.gil.locked, -1,
  117. _Py_memory_order_release);
  118. _Py_ANNOTATE_RWLOCK_DESTROY(&_PyRuntime.ceval.gil.locked);
  119. }
  120. static void recreate_gil(void)
  121. {
  122. _Py_ANNOTATE_RWLOCK_DESTROY(&_PyRuntime.ceval.gil.locked);
  123. /* XXX should we destroy the old OS resources here? */
  124. create_gil();
  125. }
  126. static void drop_gil(PyThreadState *tstate)
  127. {
  128. if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked))
  129. Py_FatalError("drop_gil: GIL is not locked");
  130. /* tstate is allowed to be NULL (early interpreter init) */
  131. if (tstate != NULL) {
  132. /* Sub-interpreter support: threads might have been switched
  133. under our feet using PyThreadState_Swap(). Fix the GIL last
  134. holder variable so that our heuristics work. */
  135. _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder,
  136. (uintptr_t)tstate);
  137. }
  138. MUTEX_LOCK(_PyRuntime.ceval.gil.mutex);
  139. _Py_ANNOTATE_RWLOCK_RELEASED(&_PyRuntime.ceval.gil.locked, /*is_write=*/1);
  140. _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.locked, 0);
  141. COND_SIGNAL(_PyRuntime.ceval.gil.cond);
  142. MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex);
  143. #ifdef FORCE_SWITCHING
  144. if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request) &&
  145. tstate != NULL)
  146. {
  147. MUTEX_LOCK(_PyRuntime.ceval.gil.switch_mutex);
  148. /* Not switched yet => wait */
  149. if (((PyThreadState*)_Py_atomic_load_relaxed(
  150. &_PyRuntime.ceval.gil.last_holder)
  151. ) == tstate)
  152. {
  153. RESET_GIL_DROP_REQUEST();
  154. /* NOTE: if COND_WAIT does not atomically start waiting when
  155. releasing the mutex, another thread can run through, take
  156. the GIL and drop it again, and reset the condition
  157. before we even had a chance to wait for it. */
  158. COND_WAIT(_PyRuntime.ceval.gil.switch_cond,
  159. _PyRuntime.ceval.gil.switch_mutex);
  160. }
  161. MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex);
  162. }
  163. #endif
  164. }
  165. static void take_gil(PyThreadState *tstate)
  166. {
  167. int err;
  168. if (tstate == NULL)
  169. Py_FatalError("take_gil: NULL tstate");
  170. err = errno;
  171. MUTEX_LOCK(_PyRuntime.ceval.gil.mutex);
  172. if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked))
  173. goto _ready;
  174. while (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked)) {
  175. int timed_out = 0;
  176. unsigned long saved_switchnum;
  177. saved_switchnum = _PyRuntime.ceval.gil.switch_number;
  178. COND_TIMED_WAIT(_PyRuntime.ceval.gil.cond, _PyRuntime.ceval.gil.mutex,
  179. INTERVAL, timed_out);
  180. /* If we timed out and no switch occurred in the meantime, it is time
  181. to ask the GIL-holding thread to drop it. */
  182. if (timed_out &&
  183. _Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked) &&
  184. _PyRuntime.ceval.gil.switch_number == saved_switchnum) {
  185. SET_GIL_DROP_REQUEST();
  186. }
  187. }
  188. _ready:
  189. #ifdef FORCE_SWITCHING
  190. /* This mutex must be taken before modifying
  191. _PyRuntime.ceval.gil.last_holder (see drop_gil()). */
  192. MUTEX_LOCK(_PyRuntime.ceval.gil.switch_mutex);
  193. #endif
  194. /* We now hold the GIL */
  195. _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.locked, 1);
  196. _Py_ANNOTATE_RWLOCK_ACQUIRED(&_PyRuntime.ceval.gil.locked, /*is_write=*/1);
  197. if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(
  198. &_PyRuntime.ceval.gil.last_holder))
  199. {
  200. _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder,
  201. (uintptr_t)tstate);
  202. ++_PyRuntime.ceval.gil.switch_number;
  203. }
  204. #ifdef FORCE_SWITCHING
  205. COND_SIGNAL(_PyRuntime.ceval.gil.switch_cond);
  206. MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex);
  207. #endif
  208. if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request)) {
  209. RESET_GIL_DROP_REQUEST();
  210. }
  211. if (tstate->async_exc != NULL) {
  212. _PyEval_SignalAsyncExc();
  213. }
  214. MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex);
  215. errno = err;
  216. }
  217. void _PyEval_SetSwitchInterval(unsigned long microseconds)
  218. {
  219. _PyRuntime.ceval.gil.interval = microseconds;
  220. }
  221. unsigned long _PyEval_GetSwitchInterval()
  222. {
  223. return _PyRuntime.ceval.gil.interval;
  224. }