Commit b75b1a35 authored by Eric Snow's avatar Eric Snow Committed by GitHub

bpo-33608: Revert "Factor out a private, per-interpreter _Py_AddPendingCall()." (gh-12806)

This reverts commit f13c5c8b (gh-12360).
parent f938d8be
...@@ -221,7 +221,7 @@ PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc); ...@@ -221,7 +221,7 @@ PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc);
#ifndef Py_LIMITED_API #ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *); PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *);
PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *); PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyInterpreterState *); PyAPI_FUNC(void) _PyEval_SignalAsyncExc(void);
#endif #endif
/* Masks and values used by FORMAT_VALUE opcode. */ /* Masks and values used by FORMAT_VALUE opcode. */
......
...@@ -11,11 +11,7 @@ extern "C" { ...@@ -11,11 +11,7 @@ extern "C" {
#include "pycore_atomic.h" #include "pycore_atomic.h"
#include "pythread.h" #include "pythread.h"
struct _is; // See PyInterpreterState in cpython/pystate.h. PyAPI_FUNC(void) _Py_FinishPendingCalls(void);
PyAPI_FUNC(int) _Py_AddPendingCall(struct _is*, unsigned long, int (*)(void *), void *);
PyAPI_FUNC(int) _Py_MakePendingCalls(struct _is*);
PyAPI_FUNC(void) _Py_FinishPendingCalls(struct _is*);
struct _pending_calls { struct _pending_calls {
int finishing; int finishing;
...@@ -28,7 +24,6 @@ struct _pending_calls { ...@@ -28,7 +24,6 @@ struct _pending_calls {
int async_exc; int async_exc;
#define NPENDINGCALLS 32 #define NPENDINGCALLS 32
struct { struct {
unsigned long thread_id;
int (*func)(void *); int (*func)(void *);
void *arg; void *arg;
} calls[NPENDINGCALLS]; } calls[NPENDINGCALLS];
...@@ -36,13 +31,6 @@ struct _pending_calls { ...@@ -36,13 +31,6 @@ struct _pending_calls {
int last; int last;
}; };
struct _ceval_interpreter_state {
/* This single variable consolidates all requests to break out of
the fast path in the eval loop. */
_Py_atomic_int eval_breaker;
struct _pending_calls pending;
};
#include "pycore_gil.h" #include "pycore_gil.h"
struct _ceval_runtime_state { struct _ceval_runtime_state {
...@@ -53,8 +41,12 @@ struct _ceval_runtime_state { ...@@ -53,8 +41,12 @@ struct _ceval_runtime_state {
c_tracefunc. This speeds up the if statement in c_tracefunc. This speeds up the if statement in
PyEval_EvalFrameEx() after fast_next_opcode. */ PyEval_EvalFrameEx() after fast_next_opcode. */
int tracing_possible; int tracing_possible;
/* This single variable consolidates all requests to break out of
the fast path in the eval loop. */
_Py_atomic_int eval_breaker;
/* Request for dropping the GIL */ /* Request for dropping the GIL */
_Py_atomic_int gil_drop_request; _Py_atomic_int gil_drop_request;
struct _pending_calls pending;
/* Request for checking signals. */ /* Request for checking signals. */
_Py_atomic_int signals_pending; _Py_atomic_int signals_pending;
struct _gil_runtime_state gil; struct _gil_runtime_state gil;
......
...@@ -12,7 +12,6 @@ extern "C" { ...@@ -12,7 +12,6 @@ extern "C" {
#include "pystate.h" #include "pystate.h"
#include "pythread.h" #include "pythread.h"
#include "pycore_atomic.h"
#include "pycore_ceval.h" #include "pycore_ceval.h"
#include "pycore_pathconfig.h" #include "pycore_pathconfig.h"
#include "pycore_pymem.h" #include "pycore_pymem.h"
...@@ -84,8 +83,6 @@ struct _is { ...@@ -84,8 +83,6 @@ struct _is {
PyObject *pyexitmodule; PyObject *pyexitmodule;
uint64_t tstate_next_unique_id; uint64_t tstate_next_unique_id;
struct _ceval_interpreter_state ceval;
}; };
PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T); PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T);
......
...@@ -373,7 +373,7 @@ class TestPendingCalls(unittest.TestCase): ...@@ -373,7 +373,7 @@ class TestPendingCalls(unittest.TestCase):
def test_pendingcalls_threaded(self): def test_pendingcalls_threaded(self):
#do every callback on a separate thread #do every callback on a separate thread
n = 32 #total callbacks (see NPENDINGCALLS in pycore_ceval.h) n = 32 #total callbacks
threads = [] threads = []
class foo(object):pass class foo(object):pass
context = foo() context = foo()
......
We added a new internal _Py_AddPendingCall() that operates relative to the
provided interpreter. This allows us to use the existing implementation to
ask another interpreter to do work that cannot be done in the current
interpreter, like decref an object the other interpreter owns. The existing
Py_AddPendingCall() only operates relative to the main interpreter.
...@@ -2445,7 +2445,6 @@ pending_threadfunc(PyObject *self, PyObject *arg) ...@@ -2445,7 +2445,6 @@ pending_threadfunc(PyObject *self, PyObject *arg)
Py_INCREF(callable); Py_INCREF(callable);
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
/* XXX Use the internal _Py_AddPendingCall(). */
r = Py_AddPendingCall(&_pending_callback, callable); r = Py_AddPendingCall(&_pending_callback, callable);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <process.h> #include <process.h>
#endif #endif
#endif #endif
#include "internal/pycore_pystate.h"
#ifdef HAVE_SIGNAL_H #ifdef HAVE_SIGNAL_H
#include <signal.h> #include <signal.h>
...@@ -296,9 +295,7 @@ trip_signal(int sig_num) ...@@ -296,9 +295,7 @@ trip_signal(int sig_num)
{ {
/* Py_AddPendingCall() isn't signal-safe, but we /* Py_AddPendingCall() isn't signal-safe, but we
still use it for this exceptional case. */ still use it for this exceptional case. */
_Py_AddPendingCall(_PyRuntime.interpreters.main, Py_AddPendingCall(report_wakeup_send_error,
main_thread,
report_wakeup_send_error,
(void *)(intptr_t) last_error); (void *)(intptr_t) last_error);
} }
} }
...@@ -316,9 +313,7 @@ trip_signal(int sig_num) ...@@ -316,9 +313,7 @@ trip_signal(int sig_num)
{ {
/* Py_AddPendingCall() isn't signal-safe, but we /* Py_AddPendingCall() isn't signal-safe, but we
still use it for this exceptional case. */ still use it for this exceptional case. */
_Py_AddPendingCall(_PyRuntime.interpreters.main, Py_AddPendingCall(report_wakeup_write_error,
main_thread,
report_wakeup_write_error,
(void *)(intptr_t)errno); (void *)(intptr_t)errno);
} }
} }
......
This diff is collapsed.
...@@ -176,7 +176,7 @@ static void drop_gil(PyThreadState *tstate) ...@@ -176,7 +176,7 @@ static void drop_gil(PyThreadState *tstate)
&_PyRuntime.ceval.gil.last_holder) &_PyRuntime.ceval.gil.last_holder)
) == tstate) ) == tstate)
{ {
RESET_GIL_DROP_REQUEST(tstate->interp); RESET_GIL_DROP_REQUEST();
/* NOTE: if COND_WAIT does not atomically start waiting when /* NOTE: if COND_WAIT does not atomically start waiting when
releasing the mutex, another thread can run through, take releasing the mutex, another thread can run through, take
the GIL and drop it again, and reset the condition the GIL and drop it again, and reset the condition
...@@ -213,7 +213,7 @@ static void take_gil(PyThreadState *tstate) ...@@ -213,7 +213,7 @@ static void take_gil(PyThreadState *tstate)
if (timed_out && if (timed_out &&
_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked) && _Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked) &&
_PyRuntime.ceval.gil.switch_number == saved_switchnum) { _PyRuntime.ceval.gil.switch_number == saved_switchnum) {
SET_GIL_DROP_REQUEST(tstate->interp); SET_GIL_DROP_REQUEST();
} }
} }
_ready: _ready:
...@@ -239,10 +239,10 @@ _ready: ...@@ -239,10 +239,10 @@ _ready:
MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex); MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex);
#endif #endif
if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request)) { if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request)) {
RESET_GIL_DROP_REQUEST(tstate->interp); RESET_GIL_DROP_REQUEST();
} }
if (tstate->async_exc != NULL) { if (tstate->async_exc != NULL) {
_PyEval_SignalAsyncExc(tstate->interp); _PyEval_SignalAsyncExc();
} }
MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex); MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex);
......
...@@ -1146,7 +1146,7 @@ Py_FinalizeEx(void) ...@@ -1146,7 +1146,7 @@ Py_FinalizeEx(void)
interp = tstate->interp; interp = tstate->interp;
// Make any remaining pending calls. // Make any remaining pending calls.
_Py_FinishPendingCalls(interp); _Py_FinishPendingCalls();
/* The interpreter is still entirely intact at this point, and the /* The interpreter is still entirely intact at this point, and the
* exit funcs may be relying on that. In particular, if some thread * exit funcs may be relying on that. In particular, if some thread
...@@ -1552,9 +1552,6 @@ Py_EndInterpreter(PyThreadState *tstate) ...@@ -1552,9 +1552,6 @@ Py_EndInterpreter(PyThreadState *tstate)
// Wrap up existing "threading"-module-created, non-daemon threads. // Wrap up existing "threading"-module-created, non-daemon threads.
wait_for_thread_shutdown(); wait_for_thread_shutdown();
// Make any remaining pending calls.
_Py_FinishPendingCalls(interp);
call_py_exitfuncs(interp); call_py_exitfuncs(interp);
if (tstate != interp->tstate_head || tstate->next != NULL) if (tstate != interp->tstate_head || tstate->next != NULL)
......
...@@ -173,14 +173,6 @@ PyInterpreterState_New(void) ...@@ -173,14 +173,6 @@ PyInterpreterState_New(void)
memset(interp, 0, sizeof(*interp)); memset(interp, 0, sizeof(*interp));
interp->id_refcount = -1; interp->id_refcount = -1;
interp->check_interval = 100; interp->check_interval = 100;
interp->ceval.pending.lock = PyThread_allocate_lock();
if (interp->ceval.pending.lock == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"failed to create interpreter ceval pending mutex");
return NULL;
}
interp->core_config = _PyCoreConfig_INIT; interp->core_config = _PyCoreConfig_INIT;
interp->eval_frame = _PyEval_EvalFrameDefault; interp->eval_frame = _PyEval_EvalFrameDefault;
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
...@@ -287,9 +279,6 @@ PyInterpreterState_Delete(PyInterpreterState *interp) ...@@ -287,9 +279,6 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
if (interp->id_mutex != NULL) { if (interp->id_mutex != NULL) {
PyThread_free_lock(interp->id_mutex); PyThread_free_lock(interp->id_mutex);
} }
if (interp->ceval.pending.lock != NULL) {
PyThread_free_lock(interp->ceval.pending.lock);
}
PyMem_RawFree(interp); PyMem_RawFree(interp);
} }
...@@ -939,7 +928,7 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) ...@@ -939,7 +928,7 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
p->async_exc = exc; p->async_exc = exc;
HEAD_UNLOCK(); HEAD_UNLOCK();
Py_XDECREF(old_exc); Py_XDECREF(old_exc);
_PyEval_SignalAsyncExc(interp); _PyEval_SignalAsyncExc();
return 1; return 1;
} }
} }
...@@ -1353,7 +1342,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) ...@@ -1353,7 +1342,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
return 0; return 0;
} }
static int static void
_release_xidata(void *arg) _release_xidata(void *arg)
{ {
_PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg; _PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg;
...@@ -1361,8 +1350,30 @@ _release_xidata(void *arg) ...@@ -1361,8 +1350,30 @@ _release_xidata(void *arg)
data->free(data->data); data->free(data->data);
} }
Py_XDECREF(data->obj); Py_XDECREF(data->obj);
PyMem_Free(data); }
return 0;
static void
_call_in_interpreter(PyInterpreterState *interp,
void (*func)(void *), void *arg)
{
/* We would use Py_AddPendingCall() if it weren't specific to the
* main interpreter (see bpo-33608). In the meantime we take a
* naive approach.
*/
PyThreadState *save_tstate = NULL;
if (interp != _PyInterpreterState_Get()) {
// XXX Using the "head" thread isn't strictly correct.
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
// XXX Possible GILState issues?
save_tstate = PyThreadState_Swap(tstate);
}
func(arg);
// Switch back.
if (save_tstate != NULL) {
PyThreadState_Swap(save_tstate);
}
} }
void void
...@@ -1373,7 +1384,7 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) ...@@ -1373,7 +1384,7 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
return; return;
} }
// Get the original interpreter. // Switch to the original interpreter.
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp); PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp);
if (interp == NULL) { if (interp == NULL) {
// The intepreter was already destroyed. // The intepreter was already destroyed.
...@@ -1382,24 +1393,9 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) ...@@ -1382,24 +1393,9 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
} }
return; return;
} }
// XXX There's an ever-so-slight race here...
if (interp->finalizing) {
// XXX Someone leaked some memory...
return;
}
// "Release" the data and/or the object. // "Release" the data and/or the object.
_PyCrossInterpreterData *copied = PyMem_Malloc(sizeof(_PyCrossInterpreterData)); _call_in_interpreter(interp, _release_xidata, data);
if (copied == NULL) {
PyErr_SetString(PyExc_MemoryError,
"Not enough memory to preserve cross-interpreter data");
PyErr_Print();
return;
}
memcpy(copied, data, sizeof(_PyCrossInterpreterData));
if (_Py_AddPendingCall(interp, 0, _release_xidata, copied) != 0) {
// XXX Queue full or couldn't get lock. Try again somehow?
}
} }
PyObject * PyObject *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment