summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorRichard Hansen <rhansen@rhansen.org>2024-10-14 19:28:41 (GMT)
committerGitHub <noreply@github.com>2024-10-14 19:28:41 (GMT)
commit843d28f59d2616d052d9d45f31823976da07f0f3 (patch)
tree85ca3958261acfd6c320d1a306c1871b4993da6b /Python
parente99650b80ace3893c2a80b3f2a4aca99cb305191 (diff)
downloadcpython-843d28f59d2616d052d9d45f31823976da07f0f3.zip
cpython-843d28f59d2616d052d9d45f31823976da07f0f3.tar.gz
cpython-843d28f59d2616d052d9d45f31823976da07f0f3.tar.bz2
gh-124872: Replace enter/exit events with "switched" (#124776)
Users want to know when the current context switches to a different context object. Right now this happens when and only when a context is entered or exited, so the enter and exit events are synonymous with "switched". However, if the changes proposed for gh-99633 are implemented, the current context will also switch for reasons other than context enter or exit. Since users actually care about context switches and not enter or exit, replace the enter and exit events with a single switched event. The former exit event was emitted just before exiting the context. The new switched event is emitted after the context is exited to match the semantics users expect of an event with a past-tense name. If users need the ability to clean up before the switch takes effect, another event type can be added in the future. It is not added here because YAGNI. I skipped 0 in the enum as a matter of practice. Skipping 0 makes it easier to troubleshoot when code forgets to set zeroed memory, and it aligns with best practices for other tools (e.g., https://protobuf.dev/programming-guides/dos-donts/#unspecified-enum).
Diffstat (limited to 'Python')
-rw-r--r--Python/context.c31
1 files changed, 21 insertions, 10 deletions
diff --git a/Python/context.c b/Python/context.c
index 8bc487a..95aa822 100644
--- a/Python/context.c
+++ b/Python/context.c
@@ -102,10 +102,8 @@ PyContext_CopyCurrent(void)
static const char *
context_event_name(PyContextEvent event) {
switch (event) {
- case Py_CONTEXT_EVENT_ENTER:
- return "Py_CONTEXT_EVENT_ENTER";
- case Py_CONTEXT_EVENT_EXIT:
- return "Py_CONTEXT_EVENT_EXIT";
+ case Py_CONTEXT_SWITCHED:
+ return "Py_CONTEXT_SWITCHED";
default:
return "?";
}
@@ -115,6 +113,13 @@ context_event_name(PyContextEvent event) {
static void
notify_context_watchers(PyThreadState *ts, PyContextEvent event, PyObject *ctx)
{
+ if (ctx == NULL) {
+ // This will happen after exiting the last context in the stack, which
+ // can occur if context_get was never called before entering a context
+ // (e.g., called `contextvars.Context().run()` on a fresh thread, as
+ // PyContext_Enter doesn't call context_get).
+ ctx = Py_None;
+ }
assert(Py_REFCNT(ctx) > 0);
PyInterpreterState *interp = ts->interp;
assert(interp->_initialized);
@@ -175,6 +180,16 @@ PyContext_ClearWatcher(int watcher_id)
}
+static inline void
+context_switched(PyThreadState *ts)
+{
+ ts->context_ver++;
+ // ts->context is used instead of context_get() because context_get() might
+ // throw if ts->context is NULL.
+ notify_context_watchers(ts, Py_CONTEXT_SWITCHED, ts->context);
+}
+
+
static int
_PyContext_Enter(PyThreadState *ts, PyObject *octx)
{
@@ -191,9 +206,7 @@ _PyContext_Enter(PyThreadState *ts, PyObject *octx)
ctx->ctx_entered = 1;
ts->context = Py_NewRef(ctx);
- ts->context_ver++;
-
- notify_context_watchers(ts, Py_CONTEXT_EVENT_ENTER, octx);
+ context_switched(ts);
return 0;
}
@@ -227,13 +240,11 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx)
return -1;
}
- notify_context_watchers(ts, Py_CONTEXT_EVENT_EXIT, octx);
Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev);
- ts->context_ver++;
ctx->ctx_prev = NULL;
ctx->ctx_entered = 0;
-
+ context_switched(ts);
return 0;
}