summaryrefslogtreecommitdiffstats
path: root/Python/pystate.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pystate.c')
-rw-r--r--Python/pystate.c204
1 files changed, 183 insertions, 21 deletions
diff --git a/Python/pystate.c b/Python/pystate.c
index b17efdb..394b12d 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -565,6 +565,124 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
#endif
+//---------------
+// global objects
+//---------------
+
+/* The global objects thread state is meant to be used in a very limited
+ way and should not be used to actually run any Python code. */
+
+static PyThreadState *
+bind_global_objects_state(_PyRuntimeState *runtime)
+{
+ PyThreadState *main_tstate = &runtime->cached_objects.main_tstate;
+
+ bind_tstate(main_tstate);
+ /* Unlike _PyThreadState_Bind(), we do not modify gilstate TSS. */
+
+ return main_tstate;
+}
+
+static void
+unbind_global_objects_state(_PyRuntimeState *runtime)
+{
+ PyThreadState *main_tstate = &runtime->cached_objects.main_tstate;
+ assert(tstate_is_alive(main_tstate));
+ assert(!main_tstate->_status.active);
+ assert(gilstate_tss_get(runtime) != main_tstate);
+
+ unbind_tstate(main_tstate);
+
+ /* This thread state may be bound/unbound repeatedly,
+ so we must erase evidence that it was ever bound (or unbound). */
+ main_tstate->_status.bound = 0;
+ main_tstate->_status.unbound = 0;
+
+ /* We must fully unlink the thread state from any OS thread,
+ to allow it to be bound more than once. */
+ main_tstate->thread_id = 0;
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+ main_tstate->native_thread_id = 0;
+#endif
+}
+
+static inline void
+acquire_global_objects_lock(_PyRuntimeState *runtime)
+{
+ /* For now we can rely on the GIL, so we don't actually
+ acquire a global lock here. */
+ assert(current_fast_get(runtime) != NULL);
+}
+
+static inline void
+release_global_objects_lock(_PyRuntimeState *runtime)
+{
+ /* For now we can rely on the GIL, so we don't actually
+ release a global lock here. */
+ assert(current_fast_get(runtime) != NULL);
+}
+
+PyObject *
+_Py_AddToGlobalDict(PyObject *dict, PyObject *key, PyObject *value)
+{
+ assert(dict != NULL);
+ assert(PyDict_CheckExact(dict));
+
+ /* All global objects are stored in _PyRuntime
+ and owned by the main interpreter. */
+ _PyRuntimeState *runtime = &_PyRuntime;
+ PyThreadState *curts = current_fast_get(runtime);
+ PyInterpreterState *interp = curts->interp;
+ assert(interp != NULL); // The GIL must be held.
+
+ /* Due to interpreter isolation we must hold a global lock,
+ starting at this point and ending before we return.
+ Note that the operations in this function are very fucused
+ and we should not expect any reentrancy. */
+ acquire_global_objects_lock(runtime);
+
+ /* Swap to the main interpreter, if necessary. */
+ PyThreadState *oldts = NULL;
+ if (!_Py_IsMainInterpreter(interp)) {
+ PyThreadState *main_tstate = bind_global_objects_state(runtime);
+
+ oldts = _PyThreadState_Swap(runtime, main_tstate);
+ assert(oldts != NULL);
+ assert(!_Py_IsMainInterpreter(oldts->interp));
+
+ /* The limitations of the global objects thread state apply
+ from this point to the point we swap back to oldts. */
+ }
+
+ /* This might trigger a resize, which is why we must "acquire"
+ the global object state. Also note that PyDict_SetDefault()
+ must be compatible with our reentrancy and global objects state
+ constraints. */
+ PyObject *actual = PyDict_SetDefault(dict, key, value);
+ if (actual == NULL) {
+ /* Raising an exception from one interpreter in another
+ is problematic, so we clear it and let the caller deal
+ with the returned NULL. */
+ assert(PyErr_ExceptionMatches(PyExc_MemoryError));
+ PyErr_Clear();
+ }
+
+ /* Swap back, it it wasn't in the main interpreter already. */
+ if (oldts != NULL) {
+ // The returned tstate should be _PyRuntime.cached_objects.main_tstate.
+ _PyThreadState_Swap(runtime, oldts);
+
+ unbind_global_objects_state(runtime);
+ }
+
+ release_global_objects_lock(runtime);
+
+ // XXX Immortalize the key and value.
+
+ return actual;
+}
+
+
/*************************************/
/* the per-interpreter runtime state */
/*************************************/
@@ -1217,8 +1335,7 @@ free_threadstate(PyThreadState *tstate)
static void
init_threadstate(PyThreadState *tstate,
- PyInterpreterState *interp, uint64_t id,
- PyThreadState *next)
+ PyInterpreterState *interp, uint64_t id)
{
if (tstate->_status.initialized) {
Py_FatalError("thread state already initialized");
@@ -1227,18 +1344,13 @@ init_threadstate(PyThreadState *tstate,
assert(interp != NULL);
tstate->interp = interp;
+ // next/prev are set in add_threadstate().
+ assert(tstate->next == NULL);
+ assert(tstate->prev == NULL);
+
assert(id > 0);
tstate->id = id;
- assert(interp->threads.head == tstate);
- assert((next != NULL && id != 1) || (next == NULL && id == 1));
- if (next != NULL) {
- assert(next->prev == NULL || next->prev == tstate);
- next->prev = tstate;
- }
- tstate->next = next;
- assert(tstate->prev == NULL);
-
// thread_id and native_thread_id are set in bind_tstate().
tstate->py_recursion_limit = interp->ceval.recursion_limit,
@@ -1259,6 +1371,22 @@ init_threadstate(PyThreadState *tstate,
tstate->_status.initialized = 1;
}
+static void
+add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
+ PyThreadState *next)
+{
+ assert(interp->threads.head != tstate);
+ assert((next != NULL && tstate->id != 1) ||
+ (next == NULL && tstate->id == 1));
+ if (next != NULL) {
+ assert(next->prev == NULL || next->prev == tstate);
+ next->prev = tstate;
+ }
+ tstate->next = next;
+ assert(tstate->prev == NULL);
+ interp->threads.head = tstate;
+}
+
static PyThreadState *
new_threadstate(PyInterpreterState *interp)
{
@@ -1298,9 +1426,9 @@ new_threadstate(PyInterpreterState *interp)
&initial._main_interpreter._initial_thread,
sizeof(*tstate));
}
- interp->threads.head = tstate;
- init_threadstate(tstate, interp, id, old_head);
+ init_threadstate(tstate, interp, id);
+ add_threadstate(interp, tstate, old_head);
HEAD_UNLOCK(runtime);
if (!used_newtstate) {
@@ -1348,6 +1476,33 @@ _PyThreadState_Init(PyThreadState *tstate)
}
void
+_PyThreadState_InitDetached(PyThreadState *tstate, PyInterpreterState *interp)
+{
+ _PyRuntimeState *runtime = interp->runtime;
+
+ HEAD_LOCK(runtime);
+ interp->threads.next_unique_id += 1;
+ uint64_t id = interp->threads.next_unique_id;
+ HEAD_UNLOCK(runtime);
+
+ init_threadstate(tstate, interp, id);
+ // We do not call add_threadstate().
+}
+
+
+static void
+clear_datastack(PyThreadState *tstate)
+{
+ _PyStackChunk *chunk = tstate->datastack_chunk;
+ tstate->datastack_chunk = NULL;
+ while (chunk != NULL) {
+ _PyStackChunk *prev = chunk->previous;
+ _PyObject_VirtualFree(chunk, chunk->size);
+ chunk = prev;
+ }
+}
+
+void
PyThreadState_Clear(PyThreadState *tstate)
{
assert(tstate->_status.initialized && !tstate->_status.cleared);
@@ -1421,7 +1576,6 @@ PyThreadState_Clear(PyThreadState *tstate)
// XXX Do it as early in the function as possible.
}
-
/* Common code for PyThreadState_Delete() and PyThreadState_DeleteCurrent() */
static void
tstate_delete_common(PyThreadState *tstate)
@@ -1454,17 +1608,25 @@ tstate_delete_common(PyThreadState *tstate)
unbind_tstate(tstate);
// XXX Move to PyThreadState_Clear()?
- _PyStackChunk *chunk = tstate->datastack_chunk;
- tstate->datastack_chunk = NULL;
- while (chunk != NULL) {
- _PyStackChunk *prev = chunk->previous;
- _PyObject_VirtualFree(chunk, chunk->size);
- chunk = prev;
- }
+ clear_datastack(tstate);
tstate->_status.finalized = 1;
}
+void
+_PyThreadState_ClearDetached(PyThreadState *tstate)
+{
+ assert(!tstate->_status.bound);
+ assert(!tstate->_status.bound_gilstate);
+ assert(tstate->datastack_chunk == NULL);
+ assert(tstate->thread_id == 0);
+ assert(tstate->native_thread_id == 0);
+ assert(tstate->next == NULL);
+ assert(tstate->prev == NULL);
+
+ PyThreadState_Clear(tstate);
+ clear_datastack(tstate);
+}
static void
zapthreads(PyInterpreterState *interp)