From a4decc02b89f7f20923894cad4206187f4eaaeb9 Mon Sep 17 00:00:00 2001 From: neonene Date: Sun, 31 Mar 2024 11:16:08 +0900 Subject: [PATCH 1/7] capi per interp (module) --- Include/datetime.h | 11 +++- Include/internal/pycore_interp.h | 1 + Modules/_datetimemodule.c | 95 +++++++++++++++++--------------- Modules/_testcapi/datetime.c | 14 ----- 4 files changed, 59 insertions(+), 62 deletions(-) diff --git a/Include/datetime.h b/Include/datetime.h index b78cc0e8e2e5ac..787a67640e36ce 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -185,6 +185,8 @@ typedef struct { } PyDateTime_CAPI; +PyAPI_FUNC(PyDateTime_CAPI *) get_datetime_capi(void); + #define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" @@ -194,10 +196,13 @@ typedef struct { * */ #ifndef _PY_DATETIME_IMPL /* Define global variable for the C API and a macro for setting it. */ -static PyDateTime_CAPI *PyDateTimeAPI = NULL; +#define PyDateTimeAPI get_datetime_capi() -#define PyDateTime_IMPORT \ - PyDateTimeAPI = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0) +static inline void pydatetime_import(void) { + PyDateTime_CAPI *capi = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); + assert(capi == get_datetime_capi()); +} +#define PyDateTime_IMPORT pydatetime_import() /* Macro for access to the UTC singleton */ #define PyDateTime_TimeZone_UTC PyDateTimeAPI->TimeZone_UTC diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index b8d0fdcce11ba8..2713b44400cc34 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -217,6 +217,7 @@ struct _is { // more comments. struct _obmalloc_state *obmalloc; + void *datetime_module_state; PyObject *audit_hooks; PyType_WatchCallback type_watchers[TYPE_MAX_WATCHERS]; PyCode_WatchCallback code_watchers[CODE_MAX_WATCHERS]; diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index a626bda2ea9be9..8062bfa557f68a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -12,6 +12,7 @@ #endif #include "Python.h" +#include "pycore_interp.h" #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" // _PyObject_Init() #include "pycore_time.h" // _PyTime_ObjectToTime_t() @@ -43,6 +44,8 @@ #define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) typedef struct { + PyDateTime_CAPI capi; + /* Conversion factors. */ PyObject *us_per_ms; // 1_000 PyObject *us_per_second; // 1_000_000 @@ -61,7 +64,29 @@ typedef struct { static datetime_state _datetime_global_state; -#define STATIC_STATE() (&_datetime_global_state) +static inline datetime_state * +get_module_state(PyObject *module) +{ + return &_datetime_global_state; +} + +static inline datetime_state * +get_module_state_by_interp(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + void *state = interp->datetime_module_state; + assert(state != NULL); + return (datetime_state *)state; +} + +PyDateTime_CAPI * +get_datetime_capi(void) +{ + return &get_module_state_by_interp()->capi; +} + +#define STATIC_STATE() get_module_state_by_interp() + /* We require that C int be at least 32 bits, and use int virtually * everywhere. In just a few cases we use a temp long, where a Python @@ -6701,44 +6726,27 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; -/* Get a new C API by calling this function. - * Clients get at C API via PyDateTime_IMPORT, defined in datetime.h. - */ -static inline PyDateTime_CAPI * -get_datetime_capi(void) -{ - PyDateTime_CAPI *capi = PyMem_Malloc(sizeof(PyDateTime_CAPI)); - if (capi == NULL) { - PyErr_NoMemory(); - return NULL; - } - capi->DateType = &PyDateTime_DateType; - capi->DateTimeType = &PyDateTime_DateTimeType; - capi->TimeType = &PyDateTime_TimeType; - capi->DeltaType = &PyDateTime_DeltaType; - capi->TZInfoType = &PyDateTime_TZInfoType; - capi->Date_FromDate = new_date_ex; - capi->DateTime_FromDateAndTime = new_datetime_ex; - capi->Time_FromTime = new_time_ex; - capi->Delta_FromDelta = new_delta_ex; - capi->TimeZone_FromTimeZone = new_timezone; - capi->DateTime_FromTimestamp = datetime_fromtimestamp; - capi->Date_FromTimestamp = datetime_date_fromtimestamp_capi; - capi->DateTime_FromDateAndTimeAndFold = new_datetime_ex2; - capi->Time_FromTimeAndFold = new_time_ex2; +static void +set_datetime_capi(datetime_state *st) +{ + st->capi.DateType = &PyDateTime_DateType; + st->capi.DateTimeType = &PyDateTime_DateTimeType; + st->capi.TimeType = &PyDateTime_TimeType; + st->capi.DeltaType = &PyDateTime_DeltaType; + st->capi.TZInfoType = &PyDateTime_TZInfoType; + st->capi.Date_FromDate = new_date_ex; + st->capi.DateTime_FromDateAndTime = new_datetime_ex; + st->capi.Time_FromTime = new_time_ex; + st->capi.Delta_FromDelta = new_delta_ex; + st->capi.TimeZone_FromTimeZone = new_timezone; + st->capi.DateTime_FromTimestamp = datetime_fromtimestamp; + st->capi.Date_FromTimestamp = datetime_date_fromtimestamp_capi; + st->capi.DateTime_FromDateAndTimeAndFold = new_datetime_ex2; + st->capi.Time_FromTimeAndFold = new_time_ex2; // Make sure this function is called after utc has // been initialized. - datetime_state *st = STATIC_STATE(); assert(st->utc != NULL); - capi->TimeZone_UTC = st->utc; // borrowed ref - return capi; -} - -static void -datetime_destructor(PyObject *op) -{ - void *ptr = PyCapsule_GetPointer(op, PyDateTime_CAPSULE_NAME); - PyMem_Free(ptr); + st->capi.TimeZone_UTC = st->utc; // borrowed ref } static int @@ -6823,6 +6831,10 @@ init_state(datetime_state *st) static int _datetime_exec(PyObject *module) { + datetime_state *st = get_module_state(module); + PyInterpreterState *interp = _PyInterpreterState_GET(); + interp->datetime_module_state = st; + // `&...` is not a constant expression according to a strict reading // of C standards. Fill tp_base at run-time rather than statically. // See https://bugs.python.org/issue40777 @@ -6889,7 +6901,6 @@ _datetime_exec(PyObject *module) 999999, Py_None, 0)); DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); - datetime_state *st = STATIC_STATE(); if (init_state(st) < 0) { goto error; } @@ -6924,18 +6935,12 @@ _datetime_exec(PyObject *module) } /* At last, set up and add the encapsulated C API */ - PyDateTime_CAPI *capi = get_datetime_capi(); - if (capi == NULL) { - goto error; - } - PyObject *capsule = PyCapsule_New(capi, PyDateTime_CAPSULE_NAME, - datetime_destructor); + set_datetime_capi(st); + PyObject *capsule = PyCapsule_New(&st->capi, PyDateTime_CAPSULE_NAME, NULL); if (capsule == NULL) { - PyMem_Free(capi); goto error; } if (PyModule_Add(module, "datetime_CAPI", capsule) < 0) { - PyMem_Free(capi); goto error; } diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index b1796039f0d83a..bc51d2b9a7e786 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -3,23 +3,9 @@ #include "datetime.h" // PyDateTimeAPI -static int test_run_counter = 0; - static PyObject * test_datetime_capi(PyObject *self, PyObject *args) { - if (PyDateTimeAPI) { - if (test_run_counter) { - /* Probably regrtest.py -R */ - Py_RETURN_NONE; - } - else { - PyErr_SetString(PyExc_AssertionError, - "PyDateTime_CAPI somehow initialized"); - return NULL; - } - } - test_run_counter++; PyDateTime_IMPORT; if (PyDateTimeAPI) { From 12ae163f935fe0a8aab75572d9e3b878aa23d6ff Mon Sep 17 00:00:00 2001 From: neonene Date: Sun, 31 Mar 2024 11:53:31 +0900 Subject: [PATCH 2/7] silence warning --- Include/datetime.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Include/datetime.h b/Include/datetime.h index 787a67640e36ce..689a39ddb2290a 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -200,6 +200,7 @@ PyAPI_FUNC(PyDateTime_CAPI *) get_datetime_capi(void); static inline void pydatetime_import(void) { PyDateTime_CAPI *capi = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); + (void)capi; assert(capi == get_datetime_capi()); } #define PyDateTime_IMPORT pydatetime_import() From 2dc90102bcff89fee83a382b849374a515418f3f Mon Sep 17 00:00:00 2001 From: neonene Date: Sun, 31 Mar 2024 21:30:03 +0900 Subject: [PATCH 3/7] use inline specifier --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8062bfa557f68a..285379443a3992 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6726,7 +6726,7 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; -static void +static inline void set_datetime_capi(datetime_state *st) { st->capi.DateType = &PyDateTime_DateType; From e3abfce31a6b33ac298bfb367f627afff60ff181 Mon Sep 17 00:00:00 2001 From: neonene Date: Sun, 31 Mar 2024 22:12:29 +0900 Subject: [PATCH 4/7] experiment: switch to stable api --- PC/python3dll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/PC/python3dll.c b/PC/python3dll.c index c6fdc0bd73b9fe..98576ff4270f7c 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -914,3 +914,4 @@ EXPORT_DATA(PyUnicode_Type) EXPORT_DATA(PyUnicodeIter_Type) EXPORT_DATA(PyWrapperDescr_Type) EXPORT_DATA(PyZip_Type) +EXPORT_DATA(get_datetime_capi) From 080bb8a414c3f922fe511c1d4e407e2b8a0bf2b2 Mon Sep 17 00:00:00 2001 From: neonene Date: Sun, 31 Mar 2024 22:17:30 +0900 Subject: [PATCH 5/7] revert experiment --- PC/python3dll.c | 1 - 1 file changed, 1 deletion(-) diff --git a/PC/python3dll.c b/PC/python3dll.c index 98576ff4270f7c..c6fdc0bd73b9fe 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -914,4 +914,3 @@ EXPORT_DATA(PyUnicode_Type) EXPORT_DATA(PyUnicodeIter_Type) EXPORT_DATA(PyWrapperDescr_Type) EXPORT_DATA(PyZip_Type) -EXPORT_DATA(get_datetime_capi) From 2e7a9a2985a6f6392163f98da8a25d2eb150b4a6 Mon Sep 17 00:00:00 2001 From: neonene Date: Sun, 31 Mar 2024 22:43:16 +0900 Subject: [PATCH 6/7] restore the capsule's mem management --- Modules/_datetimemodule.c | 58 +++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 285379443a3992..be2ab4cfc20845 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -44,7 +44,7 @@ #define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) typedef struct { - PyDateTime_CAPI capi; + PyDateTime_CAPI *capi; /* Conversion factors. */ PyObject *us_per_ms; // 1_000 @@ -82,7 +82,7 @@ get_module_state_by_interp(void) PyDateTime_CAPI * get_datetime_capi(void) { - return &get_module_state_by_interp()->capi; + return get_module_state_by_interp()->capi; } #define STATIC_STATE() get_module_state_by_interp() @@ -6726,27 +6726,40 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; -static inline void +static inline int set_datetime_capi(datetime_state *st) { - st->capi.DateType = &PyDateTime_DateType; - st->capi.DateTimeType = &PyDateTime_DateTimeType; - st->capi.TimeType = &PyDateTime_TimeType; - st->capi.DeltaType = &PyDateTime_DeltaType; - st->capi.TZInfoType = &PyDateTime_TZInfoType; - st->capi.Date_FromDate = new_date_ex; - st->capi.DateTime_FromDateAndTime = new_datetime_ex; - st->capi.Time_FromTime = new_time_ex; - st->capi.Delta_FromDelta = new_delta_ex; - st->capi.TimeZone_FromTimeZone = new_timezone; - st->capi.DateTime_FromTimestamp = datetime_fromtimestamp; - st->capi.Date_FromTimestamp = datetime_date_fromtimestamp_capi; - st->capi.DateTime_FromDateAndTimeAndFold = new_datetime_ex2; - st->capi.Time_FromTimeAndFold = new_time_ex2; + st->capi = PyMem_Malloc(sizeof(PyDateTime_CAPI)); + if (st->capi == NULL) { + PyErr_NoMemory(); + return -1; + } + st->capi->DateType = &PyDateTime_DateType; + st->capi->DateTimeType = &PyDateTime_DateTimeType; + st->capi->TimeType = &PyDateTime_TimeType; + st->capi->DeltaType = &PyDateTime_DeltaType; + st->capi->TZInfoType = &PyDateTime_TZInfoType; + st->capi->Date_FromDate = new_date_ex; + st->capi->DateTime_FromDateAndTime = new_datetime_ex; + st->capi->Time_FromTime = new_time_ex; + st->capi->Delta_FromDelta = new_delta_ex; + st->capi->TimeZone_FromTimeZone = new_timezone; + st->capi->DateTime_FromTimestamp = datetime_fromtimestamp; + st->capi->Date_FromTimestamp = datetime_date_fromtimestamp_capi; + st->capi->DateTime_FromDateAndTimeAndFold = new_datetime_ex2; + st->capi->Time_FromTimeAndFold = new_time_ex2; // Make sure this function is called after utc has // been initialized. assert(st->utc != NULL); - st->capi.TimeZone_UTC = st->utc; // borrowed ref + st->capi->TimeZone_UTC = st->utc; // borrowed ref + return 0; +} + +static void +datetime_destructor(PyObject *op) +{ + void *ptr = PyCapsule_GetPointer(op, PyDateTime_CAPSULE_NAME); + PyMem_Free(ptr); } static int @@ -6935,12 +6948,17 @@ _datetime_exec(PyObject *module) } /* At last, set up and add the encapsulated C API */ - set_datetime_capi(st); - PyObject *capsule = PyCapsule_New(&st->capi, PyDateTime_CAPSULE_NAME, NULL); + if (set_datetime_capi(st) < 0) { + goto error; + } + PyObject *capsule = PyCapsule_New(st->capi, PyDateTime_CAPSULE_NAME, + datetime_destructor); if (capsule == NULL) { + PyMem_Free(st->capi); goto error; } if (PyModule_Add(module, "datetime_CAPI", capsule) < 0) { + PyMem_Free(st->capi); goto error; } From aede7e72cf5bba005bcb94741ff2524c811685da Mon Sep 17 00:00:00 2001 From: neonene Date: Mon, 1 Apr 2024 01:31:38 +0900 Subject: [PATCH 7/7] back to 2dc9010 to keep description --- Modules/_datetimemodule.c | 58 ++++++++++++++------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index be2ab4cfc20845..285379443a3992 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -44,7 +44,7 @@ #define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) typedef struct { - PyDateTime_CAPI *capi; + PyDateTime_CAPI capi; /* Conversion factors. */ PyObject *us_per_ms; // 1_000 @@ -82,7 +82,7 @@ get_module_state_by_interp(void) PyDateTime_CAPI * get_datetime_capi(void) { - return get_module_state_by_interp()->capi; + return &get_module_state_by_interp()->capi; } #define STATIC_STATE() get_module_state_by_interp() @@ -6726,40 +6726,27 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; -static inline int +static inline void set_datetime_capi(datetime_state *st) { - st->capi = PyMem_Malloc(sizeof(PyDateTime_CAPI)); - if (st->capi == NULL) { - PyErr_NoMemory(); - return -1; - } - st->capi->DateType = &PyDateTime_DateType; - st->capi->DateTimeType = &PyDateTime_DateTimeType; - st->capi->TimeType = &PyDateTime_TimeType; - st->capi->DeltaType = &PyDateTime_DeltaType; - st->capi->TZInfoType = &PyDateTime_TZInfoType; - st->capi->Date_FromDate = new_date_ex; - st->capi->DateTime_FromDateAndTime = new_datetime_ex; - st->capi->Time_FromTime = new_time_ex; - st->capi->Delta_FromDelta = new_delta_ex; - st->capi->TimeZone_FromTimeZone = new_timezone; - st->capi->DateTime_FromTimestamp = datetime_fromtimestamp; - st->capi->Date_FromTimestamp = datetime_date_fromtimestamp_capi; - st->capi->DateTime_FromDateAndTimeAndFold = new_datetime_ex2; - st->capi->Time_FromTimeAndFold = new_time_ex2; + st->capi.DateType = &PyDateTime_DateType; + st->capi.DateTimeType = &PyDateTime_DateTimeType; + st->capi.TimeType = &PyDateTime_TimeType; + st->capi.DeltaType = &PyDateTime_DeltaType; + st->capi.TZInfoType = &PyDateTime_TZInfoType; + st->capi.Date_FromDate = new_date_ex; + st->capi.DateTime_FromDateAndTime = new_datetime_ex; + st->capi.Time_FromTime = new_time_ex; + st->capi.Delta_FromDelta = new_delta_ex; + st->capi.TimeZone_FromTimeZone = new_timezone; + st->capi.DateTime_FromTimestamp = datetime_fromtimestamp; + st->capi.Date_FromTimestamp = datetime_date_fromtimestamp_capi; + st->capi.DateTime_FromDateAndTimeAndFold = new_datetime_ex2; + st->capi.Time_FromTimeAndFold = new_time_ex2; // Make sure this function is called after utc has // been initialized. assert(st->utc != NULL); - st->capi->TimeZone_UTC = st->utc; // borrowed ref - return 0; -} - -static void -datetime_destructor(PyObject *op) -{ - void *ptr = PyCapsule_GetPointer(op, PyDateTime_CAPSULE_NAME); - PyMem_Free(ptr); + st->capi.TimeZone_UTC = st->utc; // borrowed ref } static int @@ -6948,17 +6935,12 @@ _datetime_exec(PyObject *module) } /* At last, set up and add the encapsulated C API */ - if (set_datetime_capi(st) < 0) { - goto error; - } - PyObject *capsule = PyCapsule_New(st->capi, PyDateTime_CAPSULE_NAME, - datetime_destructor); + set_datetime_capi(st); + PyObject *capsule = PyCapsule_New(&st->capi, PyDateTime_CAPSULE_NAME, NULL); if (capsule == NULL) { - PyMem_Free(st->capi); goto error; } if (PyModule_Add(module, "datetime_CAPI", capsule) < 0) { - PyMem_Free(st->capi); goto error; }