10000 bpo-33608: Factor out a private, per-interpreter _Py_AddPendingCall(). by ericsnowcurrently · Pull Request #12360 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-33608: Factor out a private, per-interpreter _Py_AddPendingCall(). #12360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Optionally lock a pending call to a specific thread.
  • Loading branch information
ericsnowcurrently committed Apr 5, 2019
commit d71613160298471fb1e64c19cb60e1c4953f1a5d
3 changes: 2 additions & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extern "C" {

struct _is; // See PyInterpreterState in cpython/pystate.h.

PyAPI_FUNC(int) _Py_AddPendingCall(struct _is*, int (*)(void *), 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*);

Expand All @@ -28,6 +28,7 @@ struct _pending_calls {
int async_exc;
#define NPENDINGCALLS 32
struct {
unsigned long thread_id;
int (*func)(void *);
void *arg;
} calls[NPENDINGCALLS];
Expand Down
2 changes: 2 additions & 0 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ trip_signal(int sig_num)
/* Py_AddPendingCall() isn't signal-safe, but we
still use it for this exceptional case. */
_Py_AddPendingCall(_PyRuntime.interpreters.main,
main_thread,
report_wakeup_send_error,
(void *)(intptr_t) last_error);
}
Expand All @@ -316,6 +317,7 @@ trip_signal(int sig_num)
/* Py_AddPendingCall() isn't signal-safe, but we
still use it for this exceptional case. */
_Py_AddPendingCall(_PyRuntime.interpreters.main,
main_thread,
report_wakeup_write_error,
(void *)(intptr_t)errno);
}
Expand Down
23 changes: 17 additions & 6 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,15 @@ _PyEval_SignalReceived(void)

/* Push one item onto the queue while holding the lock. */
static int
_push_pending_call(struct _pending_calls *pending,
_push_pending_call(struct _pending_calls *pending, unsigned long thread_id,
int (*func)(void *), void *arg)
{
int i = pending->last;
int j = (i + 1) % NPENDINGCALLS;
if (j == pending->first) {
return -1; /* Queue full */
}
pending->calls[i].thread_id = thread_id;
pending->calls[i].func = func;
pending->calls[i].arg = arg;
pending->last = j;
Expand All @@ -354,7 +355,7 @@ _push_pending_call(struct _pending_calls *pending,

/* Pop one item off the queue while holding the lock. */
static void
_pop_pending_call(struct _pending_calls *pending,
_pop_pending_call(struct _pending_calls *pending, unsigned long *thread_id,
int (**func)(void *), void **arg)
{
int i = pending->first;
Expand All @@ -364,6 +365,7 @@ _pop_pending_call(struct _pending_calls *pending,

*func = pending->calls[i].func;
*arg = pending->calls[i].arg;
*thread_id = pending->calls[i].thread_id;
pending->first = (i + 1) % NPENDINGCALLS;
}

Expand All @@ -373,7 +375,8 @@ _pop_pending_call(struct _pending_calls *pending,
*/

int
_Py_AddPendingCall(PyInterpreterState *interp, int (*func)(void *), void *arg)
_Py_AddPendingCall(PyInterpreterState *interp, unsigned long thread_id,
int (*func)(void *), void *arg)
{
struct _pending_calls *pending = &interp->ceval.pending;

Expand All @@ -390,7 +393,7 @@ _Py_AddPendingCall(PyInterpreterState *interp, int (*func)(void *), void *arg)
PyErr_Restore(exc, val, tb);
return -1;
}
int result = _push_pending_call(pending, func, arg);
int result = _push_pending_call(pending, thread_id, func, arg);
/* signal main loop */
SIGNAL_PENDING_CALLS(interp);
PyThread_release_lock(pending->lock);
Expand All @@ -404,7 +407,7 @@ int
Py_AddPendingCall(int (*func)(void *), void *arg)
{
PyInterpreterState *interp = _PyRuntime.interpreters.main;
return _Py_AddPendingCall(interp, func, arg);
return _Py_AddPendingCall(interp, _PyRuntime.main_thread, func, arg);
}

static int
Expand Down Expand Up @@ -449,15 +452,23 @@ make_pending_calls(PyInterpreterState *interp)
int res = 0;

/* perform a bounded number of calls, in case of recursion */
unsigned long thread_id = 0;
for (int i=0; i<NPENDINGCALLS; i++) {
int (*func)(void *) = NULL;
void *arg = NULL;

/* pop one item off the queue while holding the lock */
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
_pop_pending_call(pending, &func, &arg);
_pop_pending_call(pending, &thread_id, &func, &arg);
PyThread_release_lock(pending->lock);

if (thread_id && PyThread_get_thread_ident() != thread_id) {
// Thread mismatch, so move it to the end of the list
// and start over.
_Py_AddPendingCall(interp, thread_id, func, arg);
return 0;
}

/* having released the lock, perform the callback */
if (func == NULL) {
break;
Expand Down
0