Repository navigation
The datetime C API is not thread-safe #141563
Copy link
Copy link
Closed
Labels
3.15bugs and security fixesbugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirtopic-C-APItopic-free-threadingtopic-subinterpreterstype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or errorextension-modulesC modules in the Modules dirC modules in the Modules dir
on Nov 14, 2025 - added3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes
on Nov 14, 2025 Has anyone found any examples of this on GitHub? I imagine that this might happen because it seems like it would be common to use dateline to measure the time of something executing but at the same time why use datetime itself? There has to be better libs or inbuilt functions for most use cases
Yes.
I think we can make it thread safe with atomics, basically create a exchange loop and only initialize it if it is not initialized already.
Something like this should work:
diff --git a/Include/datetime.h b/Include/datetime.h index ed36e6e48c8..17c85b19e11 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -197,7 +197,15 @@ typedef struct { static PyDateTime_CAPI *PyDateTimeAPI = NULL; #define PyDateTime_IMPORT \ - PyDateTimeAPI = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0) + do + { + PyDateTime_CAPI *capi = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); \ + if (capi && _Py_atomic_load_ptr(&PyDateTimeAPI) == NULL) { \ + PyDateTime_CAPI *expected_api = NULL; \ + _Py_atomic_compare_exchange_ptr(&PyDateTimeAPI, &expected_api, capi); \ + } + } while (0) + /* Macro for access to the UTC singleton */ #define PyDateTime_TimeZone_UTC PyDateTimeAPI->TimeZone_UTC
I think that will probably break code that relies on
PyDateTimeAPIfor checking ifPyDateTime_IMPORTfailed, so that's probably too much for a patch release. Maybe it'll work for 3.15 though?26 remaining items
- added 5 commits that reference this issue
on Feb 28, 2026 - added 6 commits that reference this issue
on Apr 25, 2026
Metadata
Metadata
Assignees
Labels
3.15bugs and security fixesbugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirtopic-C-APItopic-free-threadingtopic-subinterpreterstype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Projects
- StatusShow more project fieldsDone
- StatusShow more project fieldsDone
Bug report
Bug description:
datetimehas a C API that we expose through a global variable. This does not work well under multiple threads. For example, ifPyDateTime_IMPORTfails in one thread while another thread is invoking the datetime C API, then that will setPyDateTimeAPItoNULL, leading toNULLpointer dereferences in the original thread.This global variable also forces us to do some nasty hacks with subinterpreters because it exposes static types. I helped fix this a few months ago by making
_datetimestatic (see this thread), but there are probably other issues that I haven't found yet.I think a bandaid fix would be to make
PyDateTimeAPIa thread-local variable, but I suspect that would break a lot of things, nor would that fix this for subinterpreters. More generally, we could deprecatePyDateTime_IMPORTandPyDateTimeAPIin favor of letting users manage the pointer on their own, but I don't know if we can do this without too many code changes (see #141543 and #83785 for the docs side).cc @pganssle
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
PyDateTime_IMPORTthread-safe #144210