Skip to content

Commit ce9bd0b

Browse files
Add _PyThreadState_SwapNoGIL().
1 parent f4ea30d commit ce9bd0b

File tree

4 files changed

+53
-21
lines changed

4 files changed

+53
-21
lines changed

Include/internal/pycore_ceval.h

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ extern void _PyEval_FiniGIL(PyInterpreterState *interp);
102102

103103
extern void _PyEval_AcquireLock(PyThreadState *tstate);
104104
extern void _PyEval_ReleaseLock(PyThreadState *tstate);
105+
extern PyThreadState * _PyThreadState_SwapNoGIL(PyThreadState *);
105106

106107
extern void _PyEval_DeactivateOpCache(void);
107108

Python/ceval_gil.c

+6-8
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ PyEval_AcquireThread(PyThreadState *tstate)
631631

632632
take_gil(tstate);
633633

634-
if (_PyThreadState_Swap(tstate->interp->runtime, tstate) != NULL) {
634+
if (_PyThreadState_SwapNoGIL(tstate) != NULL) {
635635
Py_FatalError("non-NULL old thread state");
636636
}
637637
}
@@ -641,8 +641,7 @@ PyEval_ReleaseThread(PyThreadState *tstate)
641641
{
642642
assert(is_tstate_valid(tstate));
643643

644-
_PyRuntimeState *runtime = tstate->interp->runtime;
645-
PyThreadState *new_tstate = _PyThreadState_Swap(runtime, NULL);
644+
PyThreadState *new_tstate = _PyThreadState_SwapNoGIL(NULL);
646645
if (new_tstate != tstate) {
647646
Py_FatalError("wrong thread state");
648647
}
@@ -690,8 +689,7 @@ _PyEval_SignalAsyncExc(PyInterpreterState *interp)
690689
PyThreadState *
691690
PyEval_SaveThread(void)
692691
{
693-
_PyRuntimeState *runtime = &_PyRuntime;
694-
PyThreadState *tstate = _PyThreadState_Swap(runtime, NULL);
692+
PyThreadState *tstate = _PyThreadState_SwapNoGIL(NULL);
695693
_Py_EnsureTstateNotNULL(tstate);
696694

697695
struct _ceval_state *ceval = &tstate->interp->ceval;
@@ -707,7 +705,7 @@ PyEval_RestoreThread(PyThreadState *tstate)
707705

708706
take_gil(tstate);
709707

710-
_PyThreadState_Swap(tstate->interp->runtime, tstate);
708+
_PyThreadState_SwapNoGIL(tstate);
711709
}
712710

713711

@@ -1011,7 +1009,7 @@ _Py_HandlePending(PyThreadState *tstate)
10111009
/* GIL drop request */
10121010
if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->gil_drop_request)) {
10131011
/* Give another thread a chance */
1014-
if (_PyThreadState_Swap(runtime, NULL) != tstate) {
1012+
if (_PyThreadState_SwapNoGIL(NULL) != tstate) {
10151013
Py_FatalError("tstate mix-up");
10161014
}
10171015
drop_gil(interp_ceval_state, tstate);
@@ -1020,7 +1018,7 @@ _Py_HandlePending(PyThreadState *tstate)
10201018

10211019
take_gil(tstate);
10221020

1023-
if (_PyThreadState_Swap(runtime, tstate) != NULL) {
1021+
if (_PyThreadState_SwapNoGIL(tstate) != NULL) {
10241022
Py_FatalError("orphan tstate");
10251023
}
10261024
}

Python/pylifecycle.c

+14-3
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ init_interp_create_gil(PyThreadState *tstate, int own_gil)
591591

592592
/* finalize_interp_delete() comment explains why _PyEval_FiniGIL() is
593593
only called here. */
594+
// XXX This is broken with a per-interpreter GIL.
594595
_PyEval_FiniGIL(tstate->interp);
595596

596597
/* Auto-thread-state API */
@@ -645,7 +646,8 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
645646
return _PyStatus_ERR("can't make first thread");
646647
}
647648
_PyThreadState_Bind(tstate);
648-
(void) PyThreadState_Swap(tstate);
649+
// XXX For now we do this before the GIL is created.
650+
(void) _PyThreadState_SwapNoGIL(tstate);
649651

650652
status = init_interp_create_gil(tstate, config.own_gil);
651653
if (_PyStatus_EXCEPTION(status)) {
@@ -2025,11 +2027,14 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config)
20252027
}
20262028
_PyThreadState_Bind(tstate);
20272029

2028-
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
2030+
// XXX For now we do this before the GIL is created.
2031+
PyThreadState *save_tstate = _PyThreadState_SwapNoGIL(tstate);
2032+
int has_gil = 0;
20292033

20302034
/* Copy the current interpreter config into the new interpreter */
20312035
const PyConfig *src_config;
20322036
if (save_tstate != NULL) {
2037+
_PyEval_ReleaseLock(save_tstate);
20332038
src_config = _PyInterpreterState_GetConfig(save_tstate->interp);
20342039
}
20352040
else
@@ -2053,6 +2058,7 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config)
20532058
if (_PyStatus_EXCEPTION(status)) {
20542059
goto error;
20552060
}
2061+
has_gil = 1;
20562062

20572063
status = pycore_interp_init(tstate);
20582064
if (_PyStatus_EXCEPTION(status)) {
@@ -2072,7 +2078,12 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config)
20722078

20732079
/* Oops, it didn't work. Undo it all. */
20742080
PyErr_PrintEx(0);
2075-
PyThreadState_Swap(save_tstate);
2081+
if (has_gil) {
2082+
PyThreadState_Swap(save_tstate);
2083+
}
2084+
else {
2085+
_PyThreadState_SwapNoGIL(save_tstate);
2086+
}
20762087
PyThreadState_Clear(tstate);
20772088
PyThreadState_Delete(tstate);
20782089
PyInterpreterState_Delete(interp);

Python/pystate.c

+32-10
Original file line numberDiff line numberDiff line change
@@ -1863,17 +1863,11 @@ PyThreadState_Get(void)
18631863
}
18641864

18651865

1866-
PyThreadState *
1867-
_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts)
1866+
static void
1867+
_swap_thread_states(_PyRuntimeState *runtime,
1868+
PyThreadState *oldts, PyThreadState *newts)
18681869
{
1869-
#if defined(Py_DEBUG)
1870-
/* This can be called from PyEval_RestoreThread(). Similar
1871-
to it, we need to ensure errno doesn't change.
1872-
*/
1873-
int err = errno;
1874-
#endif
1875-
PyThreadState *oldts = current_fast_get(runtime);
1876-
1870+
// XXX Do this only if oldts != NULL?
18771871
current_fast_clear(runtime);
18781872

18791873
if (oldts != NULL) {
@@ -1887,13 +1881,41 @@ _PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts)
18871881
current_fast_set(runtime, newts);
18881882
tstate_activate(newts);
18891883
}
1884+
}
1885+
1886+
PyThreadState *
1887+
_PyThreadState_SwapNoGIL(PyThreadState *newts)
1888+
{
1889+
#if defined(Py_DEBUG)
1890+
/* This can be called from PyEval_RestoreThread(). Similar
1891+
to it, we need to ensure errno doesn't change.
1892+
*/
1893+
int err = errno;
1894+
#endif
1895+
1896+
PyThreadState *oldts = current_fast_get(&_PyRuntime);
1897+
_swap_thread_states(&_PyRuntime, oldts, newts);
18901898

18911899
#if defined(Py_DEBUG)
18921900
errno = err;
18931901
#endif
18941902
return oldts;
18951903
}
18961904

1905+
PyThreadState *
1906+
_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts)
1907+
{
1908+
PyThreadState *oldts = current_fast_get(runtime);
1909+
if (oldts != NULL) {
1910+
_PyEval_ReleaseLock(oldts);
1911+
}
1912+
_swap_thread_states(runtime, oldts, newts);
1913+
if (newts != NULL) {
1914+
_PyEval_AcquireLock(newts);
1915+
}
1916+
return oldts;
1917+
}
1918+
18971919
PyThreadState *
18981920
PyThreadState_Swap(PyThreadState *newts)
18991921
{

0 commit comments

Comments
 (0)