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