17db96d56Sopenharmony_ci/*
27db96d56Sopenharmony_ci * Implementation of the Global Interpreter Lock (GIL).
37db96d56Sopenharmony_ci */
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ci#include <stdlib.h>
67db96d56Sopenharmony_ci#include <errno.h>
77db96d56Sopenharmony_ci
87db96d56Sopenharmony_ci#include "pycore_atomic.h"
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci/*
127db96d56Sopenharmony_ci   Notes about the implementation:
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci   - The GIL is just a boolean variable (locked) whose access is protected
157db96d56Sopenharmony_ci     by a mutex (gil_mutex), and whose changes are signalled by a condition
167db96d56Sopenharmony_ci     variable (gil_cond). gil_mutex is taken for short periods of time,
177db96d56Sopenharmony_ci     and therefore mostly uncontended.
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ci   - In the GIL-holding thread, the main loop (PyEval_EvalFrameEx) must be
207db96d56Sopenharmony_ci     able to release the GIL on demand by another thread. A volatile boolean
217db96d56Sopenharmony_ci     variable (gil_drop_request) is used for that purpose, which is checked
227db96d56Sopenharmony_ci     at every turn of the eval loop. That variable is set after a wait of
237db96d56Sopenharmony_ci     `interval` microseconds on `gil_cond` has timed out.
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ci      [Actually, another volatile boolean variable (eval_breaker) is used
267db96d56Sopenharmony_ci       which ORs several conditions into one. Volatile booleans are
277db96d56Sopenharmony_ci       sufficient as inter-thread signalling means since Python is run
287db96d56Sopenharmony_ci       on cache-coherent architectures only.]
297db96d56Sopenharmony_ci
307db96d56Sopenharmony_ci   - A thread wanting to take the GIL will first let pass a given amount of
317db96d56Sopenharmony_ci     time (`interval` microseconds) before setting gil_drop_request. This
327db96d56Sopenharmony_ci     encourages a defined switching period, but doesn't enforce it since
337db96d56Sopenharmony_ci     opcodes can take an arbitrary time to execute.
347db96d56Sopenharmony_ci
357db96d56Sopenharmony_ci     The `interval` value is available for the user to read and modify
367db96d56Sopenharmony_ci     using the Python API `sys.{get,set}switchinterval()`.
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_ci   - When a thread releases the GIL and gil_drop_request is set, that thread
397db96d56Sopenharmony_ci     ensures that another GIL-awaiting thread gets scheduled.
407db96d56Sopenharmony_ci     It does so by waiting on a condition variable (switch_cond) until
417db96d56Sopenharmony_ci     the value of last_holder is changed to something else than its
427db96d56Sopenharmony_ci     own thread state pointer, indicating that another thread was able to
437db96d56Sopenharmony_ci     take the GIL.
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ci     This is meant to prohibit the latency-adverse behaviour on multi-core
467db96d56Sopenharmony_ci     machines where one thread would speculatively release the GIL, but still
477db96d56Sopenharmony_ci     run and end up being the first to re-acquire it, making the "timeslices"
487db96d56Sopenharmony_ci     much longer than expected.
497db96d56Sopenharmony_ci     (Note: this mechanism is enabled with FORCE_SWITCHING above)
507db96d56Sopenharmony_ci*/
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci#include "condvar.h"
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_ci#define MUTEX_INIT(mut) \
557db96d56Sopenharmony_ci    if (PyMUTEX_INIT(&(mut))) { \
567db96d56Sopenharmony_ci        Py_FatalError("PyMUTEX_INIT(" #mut ") failed"); };
577db96d56Sopenharmony_ci#define MUTEX_FINI(mut) \
587db96d56Sopenharmony_ci    if (PyMUTEX_FINI(&(mut))) { \
597db96d56Sopenharmony_ci        Py_FatalError("PyMUTEX_FINI(" #mut ") failed"); };
607db96d56Sopenharmony_ci#define MUTEX_LOCK(mut) \
617db96d56Sopenharmony_ci    if (PyMUTEX_LOCK(&(mut))) { \
627db96d56Sopenharmony_ci        Py_FatalError("PyMUTEX_LOCK(" #mut ") failed"); };
637db96d56Sopenharmony_ci#define MUTEX_UNLOCK(mut) \
647db96d56Sopenharmony_ci    if (PyMUTEX_UNLOCK(&(mut))) { \
657db96d56Sopenharmony_ci        Py_FatalError("PyMUTEX_UNLOCK(" #mut ") failed"); };
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci#define COND_INIT(cond) \
687db96d56Sopenharmony_ci    if (PyCOND_INIT(&(cond))) { \
697db96d56Sopenharmony_ci        Py_FatalError("PyCOND_INIT(" #cond ") failed"); };
707db96d56Sopenharmony_ci#define COND_FINI(cond) \
717db96d56Sopenharmony_ci    if (PyCOND_FINI(&(cond))) { \
727db96d56Sopenharmony_ci        Py_FatalError("PyCOND_FINI(" #cond ") failed"); };
737db96d56Sopenharmony_ci#define COND_SIGNAL(cond) \
747db96d56Sopenharmony_ci    if (PyCOND_SIGNAL(&(cond))) { \
757db96d56Sopenharmony_ci        Py_FatalError("PyCOND_SIGNAL(" #cond ") failed"); };
767db96d56Sopenharmony_ci#define COND_WAIT(cond, mut) \
777db96d56Sopenharmony_ci    if (PyCOND_WAIT(&(cond), &(mut))) { \
787db96d56Sopenharmony_ci        Py_FatalError("PyCOND_WAIT(" #cond ") failed"); };
797db96d56Sopenharmony_ci#define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
807db96d56Sopenharmony_ci    { \
817db96d56Sopenharmony_ci        int r = PyCOND_TIMEDWAIT(&(cond), &(mut), (microseconds)); \
827db96d56Sopenharmony_ci        if (r < 0) \
837db96d56Sopenharmony_ci            Py_FatalError("PyCOND_WAIT(" #cond ") failed"); \
847db96d56Sopenharmony_ci        if (r) /* 1 == timeout, 2 == impl. can't say, so assume timeout */ \
857db96d56Sopenharmony_ci            timeout_result = 1; \
867db96d56Sopenharmony_ci        else \
877db96d56Sopenharmony_ci            timeout_result = 0; \
887db96d56Sopenharmony_ci    } \
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci
917db96d56Sopenharmony_ci#define DEFAULT_INTERVAL 5000
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_cistatic void _gil_initialize(struct _gil_runtime_state *gil)
947db96d56Sopenharmony_ci{
957db96d56Sopenharmony_ci    _Py_atomic_int uninitialized = {-1};
967db96d56Sopenharmony_ci    gil->locked = uninitialized;
977db96d56Sopenharmony_ci    gil->interval = DEFAULT_INTERVAL;
987db96d56Sopenharmony_ci}
997db96d56Sopenharmony_ci
1007db96d56Sopenharmony_cistatic int gil_created(struct _gil_runtime_state *gil)
1017db96d56Sopenharmony_ci{
1027db96d56Sopenharmony_ci    return (_Py_atomic_load_explicit(&gil->locked, _Py_memory_order_acquire) >= 0);
1037db96d56Sopenharmony_ci}
1047db96d56Sopenharmony_ci
1057db96d56Sopenharmony_cistatic void create_gil(struct _gil_runtime_state *gil)
1067db96d56Sopenharmony_ci{
1077db96d56Sopenharmony_ci    MUTEX_INIT(gil->mutex);
1087db96d56Sopenharmony_ci#ifdef FORCE_SWITCHING
1097db96d56Sopenharmony_ci    MUTEX_INIT(gil->switch_mutex);
1107db96d56Sopenharmony_ci#endif
1117db96d56Sopenharmony_ci    COND_INIT(gil->cond);
1127db96d56Sopenharmony_ci#ifdef FORCE_SWITCHING
1137db96d56Sopenharmony_ci    COND_INIT(gil->switch_cond);
1147db96d56Sopenharmony_ci#endif
1157db96d56Sopenharmony_ci    _Py_atomic_store_relaxed(&gil->last_holder, 0);
1167db96d56Sopenharmony_ci    _Py_ANNOTATE_RWLOCK_CREATE(&gil->locked);
1177db96d56Sopenharmony_ci    _Py_atomic_store_explicit(&gil->locked, 0, _Py_memory_order_release);
1187db96d56Sopenharmony_ci}
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_cistatic void destroy_gil(struct _gil_runtime_state *gil)
1217db96d56Sopenharmony_ci{
1227db96d56Sopenharmony_ci    /* some pthread-like implementations tie the mutex to the cond
1237db96d56Sopenharmony_ci     * and must have the cond destroyed first.
1247db96d56Sopenharmony_ci     */
1257db96d56Sopenharmony_ci    COND_FINI(gil->cond);
1267db96d56Sopenharmony_ci    MUTEX_FINI(gil->mutex);
1277db96d56Sopenharmony_ci#ifdef FORCE_SWITCHING
1287db96d56Sopenharmony_ci    COND_FINI(gil->switch_cond);
1297db96d56Sopenharmony_ci    MUTEX_FINI(gil->switch_mutex);
1307db96d56Sopenharmony_ci#endif
1317db96d56Sopenharmony_ci    _Py_atomic_store_explicit(&gil->locked, -1,
1327db96d56Sopenharmony_ci                              _Py_memory_order_release);
1337db96d56Sopenharmony_ci    _Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked);
1347db96d56Sopenharmony_ci}
1357db96d56Sopenharmony_ci
1367db96d56Sopenharmony_ci#ifdef HAVE_FORK
1377db96d56Sopenharmony_cistatic void recreate_gil(struct _gil_runtime_state *gil)
1387db96d56Sopenharmony_ci{
1397db96d56Sopenharmony_ci    _Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked);
1407db96d56Sopenharmony_ci    /* XXX should we destroy the old OS resources here? */
1417db96d56Sopenharmony_ci    create_gil(gil);
1427db96d56Sopenharmony_ci}
1437db96d56Sopenharmony_ci#endif
1447db96d56Sopenharmony_ci
1457db96d56Sopenharmony_cistatic void
1467db96d56Sopenharmony_cidrop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
1477db96d56Sopenharmony_ci         PyThreadState *tstate)
1487db96d56Sopenharmony_ci{
1497db96d56Sopenharmony_ci    struct _gil_runtime_state *gil = &ceval->gil;
1507db96d56Sopenharmony_ci    if (!_Py_atomic_load_relaxed(&gil->locked)) {
1517db96d56Sopenharmony_ci        Py_FatalError("drop_gil: GIL is not locked");
1527db96d56Sopenharmony_ci    }
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci    /* tstate is allowed to be NULL (early interpreter init) */
1557db96d56Sopenharmony_ci    if (tstate != NULL) {
1567db96d56Sopenharmony_ci        /* Sub-interpreter support: threads might have been switched
1577db96d56Sopenharmony_ci           under our feet using PyThreadState_Swap(). Fix the GIL last
1587db96d56Sopenharmony_ci           holder variable so that our heuristics work. */
1597db96d56Sopenharmony_ci        _Py_atomic_store_relaxed(&gil->last_holder, (uintptr_t)tstate);
1607db96d56Sopenharmony_ci    }
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    MUTEX_LOCK(gil->mutex);
1637db96d56Sopenharmony_ci    _Py_ANNOTATE_RWLOCK_RELEASED(&gil->locked, /*is_write=*/1);
1647db96d56Sopenharmony_ci    _Py_atomic_store_relaxed(&gil->locked, 0);
1657db96d56Sopenharmony_ci    COND_SIGNAL(gil->cond);
1667db96d56Sopenharmony_ci    MUTEX_UNLOCK(gil->mutex);
1677db96d56Sopenharmony_ci
1687db96d56Sopenharmony_ci#ifdef FORCE_SWITCHING
1697db96d56Sopenharmony_ci    if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request) && tstate != NULL) {
1707db96d56Sopenharmony_ci        MUTEX_LOCK(gil->switch_mutex);
1717db96d56Sopenharmony_ci        /* Not switched yet => wait */
1727db96d56Sopenharmony_ci        if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
1737db96d56Sopenharmony_ci        {
1747db96d56Sopenharmony_ci            assert(is_tstate_valid(tstate));
1757db96d56Sopenharmony_ci            RESET_GIL_DROP_REQUEST(tstate->interp);
1767db96d56Sopenharmony_ci            /* NOTE: if COND_WAIT does not atomically start waiting when
1777db96d56Sopenharmony_ci               releasing the mutex, another thread can run through, take
1787db96d56Sopenharmony_ci               the GIL and drop it again, and reset the condition
1797db96d56Sopenharmony_ci               before we even had a chance to wait for it. */
1807db96d56Sopenharmony_ci            COND_WAIT(gil->switch_cond, gil->switch_mutex);
1817db96d56Sopenharmony_ci        }
1827db96d56Sopenharmony_ci        MUTEX_UNLOCK(gil->switch_mutex);
1837db96d56Sopenharmony_ci    }
1847db96d56Sopenharmony_ci#endif
1857db96d56Sopenharmony_ci}
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ci/* Check if a Python thread must exit immediately, rather than taking the GIL
1897db96d56Sopenharmony_ci   if Py_Finalize() has been called.
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_ci   When this function is called by a daemon thread after Py_Finalize() has been
1927db96d56Sopenharmony_ci   called, the GIL does no longer exist.
1937db96d56Sopenharmony_ci
1947db96d56Sopenharmony_ci   tstate must be non-NULL. */
1957db96d56Sopenharmony_cistatic inline int
1967db96d56Sopenharmony_citstate_must_exit(PyThreadState *tstate)
1977db96d56Sopenharmony_ci{
1987db96d56Sopenharmony_ci    /* bpo-39877: Access _PyRuntime directly rather than using
1997db96d56Sopenharmony_ci       tstate->interp->runtime to support calls from Python daemon threads.
2007db96d56Sopenharmony_ci       After Py_Finalize() has been called, tstate can be a dangling pointer:
2017db96d56Sopenharmony_ci       point to PyThreadState freed memory. */
2027db96d56Sopenharmony_ci    PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
2037db96d56Sopenharmony_ci    return (finalizing != NULL && finalizing != tstate);
2047db96d56Sopenharmony_ci}
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ci
2077db96d56Sopenharmony_ci/* Take the GIL.
2087db96d56Sopenharmony_ci
2097db96d56Sopenharmony_ci   The function saves errno at entry and restores its value at exit.
2107db96d56Sopenharmony_ci
2117db96d56Sopenharmony_ci   tstate must be non-NULL. */
2127db96d56Sopenharmony_cistatic void
2137db96d56Sopenharmony_citake_gil(PyThreadState *tstate)
2147db96d56Sopenharmony_ci{
2157db96d56Sopenharmony_ci    int err = errno;
2167db96d56Sopenharmony_ci
2177db96d56Sopenharmony_ci    assert(tstate != NULL);
2187db96d56Sopenharmony_ci
2197db96d56Sopenharmony_ci    if (tstate_must_exit(tstate)) {
2207db96d56Sopenharmony_ci        /* bpo-39877: If Py_Finalize() has been called and tstate is not the
2217db96d56Sopenharmony_ci           thread which called Py_Finalize(), exit immediately the thread.
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci           This code path can be reached by a daemon thread after Py_Finalize()
2247db96d56Sopenharmony_ci           completes. In this case, tstate is a dangling pointer: points to
2257db96d56Sopenharmony_ci           PyThreadState freed memory. */
2267db96d56Sopenharmony_ci        PyThread_exit_thread();
2277db96d56Sopenharmony_ci    }
2287db96d56Sopenharmony_ci
2297db96d56Sopenharmony_ci    assert(is_tstate_valid(tstate));
2307db96d56Sopenharmony_ci    PyInterpreterState *interp = tstate->interp;
2317db96d56Sopenharmony_ci    struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
2327db96d56Sopenharmony_ci    struct _ceval_state *ceval2 = &interp->ceval;
2337db96d56Sopenharmony_ci    struct _gil_runtime_state *gil = &ceval->gil;
2347db96d56Sopenharmony_ci
2357db96d56Sopenharmony_ci    /* Check that _PyEval_InitThreads() was called to create the lock */
2367db96d56Sopenharmony_ci    assert(gil_created(gil));
2377db96d56Sopenharmony_ci
2387db96d56Sopenharmony_ci    MUTEX_LOCK(gil->mutex);
2397db96d56Sopenharmony_ci
2407db96d56Sopenharmony_ci    if (!_Py_atomic_load_relaxed(&gil->locked)) {
2417db96d56Sopenharmony_ci        goto _ready;
2427db96d56Sopenharmony_ci    }
2437db96d56Sopenharmony_ci
2447db96d56Sopenharmony_ci    int drop_requested = 0;
2457db96d56Sopenharmony_ci    while (_Py_atomic_load_relaxed(&gil->locked)) {
2467db96d56Sopenharmony_ci        unsigned long saved_switchnum = gil->switch_number;
2477db96d56Sopenharmony_ci
2487db96d56Sopenharmony_ci        unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
2497db96d56Sopenharmony_ci        int timed_out = 0;
2507db96d56Sopenharmony_ci        COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
2517db96d56Sopenharmony_ci
2527db96d56Sopenharmony_ci        /* If we timed out and no switch occurred in the meantime, it is time
2537db96d56Sopenharmony_ci           to ask the GIL-holding thread to drop it. */
2547db96d56Sopenharmony_ci        if (timed_out &&
2557db96d56Sopenharmony_ci            _Py_atomic_load_relaxed(&gil->locked) &&
2567db96d56Sopenharmony_ci            gil->switch_number == saved_switchnum)
2577db96d56Sopenharmony_ci        {
2587db96d56Sopenharmony_ci            if (tstate_must_exit(tstate)) {
2597db96d56Sopenharmony_ci                MUTEX_UNLOCK(gil->mutex);
2607db96d56Sopenharmony_ci                // gh-96387: If the loop requested a drop request in a previous
2617db96d56Sopenharmony_ci                // iteration, reset the request. Otherwise, drop_gil() can
2627db96d56Sopenharmony_ci                // block forever waiting for the thread which exited. Drop
2637db96d56Sopenharmony_ci                // requests made by other threads are also reset: these threads
2647db96d56Sopenharmony_ci                // may have to request again a drop request (iterate one more
2657db96d56Sopenharmony_ci                // time).
2667db96d56Sopenharmony_ci                if (drop_requested) {
2677db96d56Sopenharmony_ci                    RESET_GIL_DROP_REQUEST(interp);
2687db96d56Sopenharmony_ci                }
2697db96d56Sopenharmony_ci                PyThread_exit_thread();
2707db96d56Sopenharmony_ci            }
2717db96d56Sopenharmony_ci            assert(is_tstate_valid(tstate));
2727db96d56Sopenharmony_ci
2737db96d56Sopenharmony_ci            SET_GIL_DROP_REQUEST(interp);
2747db96d56Sopenharmony_ci            drop_requested = 1;
2757db96d56Sopenharmony_ci        }
2767db96d56Sopenharmony_ci    }
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_ci_ready:
2797db96d56Sopenharmony_ci#ifdef FORCE_SWITCHING
2807db96d56Sopenharmony_ci    /* This mutex must be taken before modifying gil->last_holder:
2817db96d56Sopenharmony_ci       see drop_gil(). */
2827db96d56Sopenharmony_ci    MUTEX_LOCK(gil->switch_mutex);
2837db96d56Sopenharmony_ci#endif
2847db96d56Sopenharmony_ci    /* We now hold the GIL */
2857db96d56Sopenharmony_ci    _Py_atomic_store_relaxed(&gil->locked, 1);
2867db96d56Sopenharmony_ci    _Py_ANNOTATE_RWLOCK_ACQUIRED(&gil->locked, /*is_write=*/1);
2877db96d56Sopenharmony_ci
2887db96d56Sopenharmony_ci    if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) {
2897db96d56Sopenharmony_ci        _Py_atomic_store_relaxed(&gil->last_holder, (uintptr_t)tstate);
2907db96d56Sopenharmony_ci        ++gil->switch_number;
2917db96d56Sopenharmony_ci    }
2927db96d56Sopenharmony_ci
2937db96d56Sopenharmony_ci#ifdef FORCE_SWITCHING
2947db96d56Sopenharmony_ci    COND_SIGNAL(gil->switch_cond);
2957db96d56Sopenharmony_ci    MUTEX_UNLOCK(gil->switch_mutex);
2967db96d56Sopenharmony_ci#endif
2977db96d56Sopenharmony_ci
2987db96d56Sopenharmony_ci    if (tstate_must_exit(tstate)) {
2997db96d56Sopenharmony_ci        /* bpo-36475: If Py_Finalize() has been called and tstate is not
3007db96d56Sopenharmony_ci           the thread which called Py_Finalize(), exit immediately the
3017db96d56Sopenharmony_ci           thread.
3027db96d56Sopenharmony_ci
3037db96d56Sopenharmony_ci           This code path can be reached by a daemon thread which was waiting
3047db96d56Sopenharmony_ci           in take_gil() while the main thread called
3057db96d56Sopenharmony_ci           wait_for_thread_shutdown() from Py_Finalize(). */
3067db96d56Sopenharmony_ci        MUTEX_UNLOCK(gil->mutex);
3077db96d56Sopenharmony_ci        drop_gil(ceval, ceval2, tstate);
3087db96d56Sopenharmony_ci        PyThread_exit_thread();
3097db96d56Sopenharmony_ci    }
3107db96d56Sopenharmony_ci    assert(is_tstate_valid(tstate));
3117db96d56Sopenharmony_ci
3127db96d56Sopenharmony_ci    if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request)) {
3137db96d56Sopenharmony_ci        RESET_GIL_DROP_REQUEST(interp);
3147db96d56Sopenharmony_ci    }
3157db96d56Sopenharmony_ci    else {
3167db96d56Sopenharmony_ci        /* bpo-40010: eval_breaker should be recomputed to be set to 1 if there
3177db96d56Sopenharmony_ci           is a pending signal: signal received by another thread which cannot
3187db96d56Sopenharmony_ci           handle signals.
3197db96d56Sopenharmony_ci
3207db96d56Sopenharmony_ci           Note: RESET_GIL_DROP_REQUEST() calls COMPUTE_EVAL_BREAKER(). */
3217db96d56Sopenharmony_ci        COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
3227db96d56Sopenharmony_ci    }
3237db96d56Sopenharmony_ci
3247db96d56Sopenharmony_ci    /* Don't access tstate if the thread must exit */
3257db96d56Sopenharmony_ci    if (tstate->async_exc != NULL) {
3267db96d56Sopenharmony_ci        _PyEval_SignalAsyncExc(tstate->interp);
3277db96d56Sopenharmony_ci    }
3287db96d56Sopenharmony_ci
3297db96d56Sopenharmony_ci    MUTEX_UNLOCK(gil->mutex);
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci    errno = err;
3327db96d56Sopenharmony_ci}
3337db96d56Sopenharmony_ci
3347db96d56Sopenharmony_civoid _PyEval_SetSwitchInterval(unsigned long microseconds)
3357db96d56Sopenharmony_ci{
3367db96d56Sopenharmony_ci    struct _gil_runtime_state *gil = &_PyRuntime.ceval.gil;
3377db96d56Sopenharmony_ci    gil->interval = microseconds;
3387db96d56Sopenharmony_ci}
3397db96d56Sopenharmony_ci
3407db96d56Sopenharmony_ciunsigned long _PyEval_GetSwitchInterval()
3417db96d56Sopenharmony_ci{
3427db96d56Sopenharmony_ci    struct _gil_runtime_state *gil = &_PyRuntime.ceval.gil;
3437db96d56Sopenharmony_ci    return gil->interval;
3447db96d56Sopenharmony_ci}
345