From 8463ae8025e9c476abf4d23208c3d7ba991dcc78 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 May 2024 16:37:32 -0600 Subject: [PATCH 01/38] Use the static types directly. --- Modules/_datetimemodule.c | 98 +++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 466382b5148509..bae80555b11c0d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -25,17 +25,18 @@ # include /* struct timeval */ #endif + +/* forward declarations */ +static PyTypeObject PyDateTime_DateType; +static PyTypeObject PyDateTime_DateTimeType; +static PyTypeObject PyDateTime_TimeType; +static PyTypeObject PyDateTime_DeltaType; +static PyTypeObject PyDateTime_TZInfoType; +static PyTypeObject PyDateTime_TimeZoneType; + + typedef struct { - /* Static types exposed by the datetime C-API. */ - PyTypeObject *date_type; - PyTypeObject *datetime_type; - PyTypeObject *delta_type; - PyTypeObject *time_type; - PyTypeObject *tzinfo_type; - /* Exposed indirectly via TimeZone_UTC. */ - PyTypeObject *timezone_type; - - /* Other module classes. */ + /* Module heap types. */ PyTypeObject *isocalendar_date_type; /* Conversion factors. */ @@ -64,22 +65,22 @@ static inline datetime_state* get_datetime_state(void) return &_datetime_global_state; } -#define PyDate_Check(op) PyObject_TypeCheck(op, get_datetime_state()->date_type) -#define PyDate_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->date_type) +#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType) +#define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType) -#define PyDateTime_Check(op) PyObject_TypeCheck(op, get_datetime_state()->datetime_type) -#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->datetime_type) +#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType) +#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateTimeType) -#define PyTime_Check(op) PyObject_TypeCheck(op, get_datetime_state()->time_type) -#define PyTime_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->time_type) +#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType) +#define PyTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TimeType) -#define PyDelta_Check(op) PyObject_TypeCheck(op, get_datetime_state()->delta_type) -#define PyDelta_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->delta_type) +#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType) +#define PyDelta_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DeltaType) -#define PyTZInfo_Check(op) PyObject_TypeCheck(op, get_datetime_state()->tzinfo_type) -#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->tzinfo_type) +#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType) +#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TZInfoType) -#define PyTimezone_Check(op) PyObject_TypeCheck(op, get_datetime_state()->timezone_type) +#define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) /* 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 @@ -988,7 +989,7 @@ new_date_ex(int year, int month, int day, PyTypeObject *type) } #define new_date(year, month, day) \ - new_date_ex(year, month, day, get_datetime_state()->date_type) + new_date_ex(year, month, day, &PyDateTime_DateType) // Forward declaration static PyObject * @@ -998,13 +999,12 @@ new_datetime_ex(int, int, int, int, int, int, int, PyObject *, PyTypeObject *); static PyObject * new_date_subclass_ex(int year, int month, int day, PyObject *cls) { - datetime_state *st = get_datetime_state(); PyObject *result; // We have "fast path" constructors for two subclasses: date and datetime - if ((PyTypeObject *)cls == st->date_type) { + if ((PyTypeObject *)cls == &PyDateTime_DateType) { result = new_date_ex(year, month, day, (PyTypeObject *)cls); } - else if ((PyTypeObject *)cls == st->datetime_type) { + else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) { result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None, (PyTypeObject *)cls); } @@ -1059,7 +1059,7 @@ new_datetime_ex(int year, int month, int day, int hour, int minute, #define new_datetime(y, m, d, hh, mm, ss, us, tzinfo, fold) \ new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, \ - get_datetime_state()->datetime_type) + &PyDateTime_DateTimeType) static PyObject * call_subclass_fold(PyObject *cls, int fold, const char *format, ...) @@ -1100,9 +1100,8 @@ new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute int second, int usecond, PyObject *tzinfo, int fold, PyObject *cls) { - datetime_state *st = get_datetime_state(); PyObject* dt; - if ((PyTypeObject*)cls == st->datetime_type) { + if ((PyTypeObject*)cls == &PyDateTime_DateTimeType) { // Use the fast path constructor dt = new_datetime(year, month, day, hour, minute, second, usecond, tzinfo, fold); @@ -1163,16 +1162,15 @@ new_time_ex(int hour, int minute, int second, int usecond, return new_time_ex2(hour, minute, second, usecond, tzinfo, 0, type); } -#define new_time(hh, mm, ss, us, tzinfo, fold) \ - new_time_ex2(hh, mm, ss, us, tzinfo, fold, get_datetime_state()->time_type) +#define new_time(hh, mm, ss, us, tzinfo, fold) \ + new_time_ex2(hh, mm, ss, us, tzinfo, fold, &PyDateTime_TimeType) static PyObject * new_time_subclass_fold_ex(int hour, int minute, int second, int usecond, PyObject *tzinfo, int fold, PyObject *cls) { PyObject *t; - datetime_state *st = get_datetime_state(); - if ((PyTypeObject*)cls == st->time_type) { + if ((PyTypeObject*)cls == &PyDateTime_TimeType) { // Use the fast path constructor t = new_time(hour, minute, second, usecond, tzinfo, fold); } @@ -1224,7 +1222,7 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize, } #define new_delta(d, s, us, normalize) \ - new_delta_ex(d, s, us, normalize, get_datetime_state()->delta_type) + new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType) typedef struct @@ -1244,8 +1242,7 @@ static PyObject * create_timezone(PyObject *offset, PyObject *name) { PyDateTime_TimeZone *self; - datetime_state *st = get_datetime_state(); - PyTypeObject *type = st->timezone_type; + PyTypeObject *type = &PyDateTime_TimeZoneType; assert(offset != NULL); assert(PyDelta_Check(offset)); @@ -2049,7 +2046,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) } #define microseconds_to_delta(pymicros) \ - microseconds_to_delta_ex(pymicros, get_datetime_state()->delta_type) + microseconds_to_delta_ex(pymicros, &PyDateTime_DeltaType) static PyObject * multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) @@ -4018,9 +4015,8 @@ timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *offset; PyObject *name = NULL; - datetime_state *st = get_datetime_state(); if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws, - st->delta_type, &offset, &name)) + &PyDateTime_DeltaType, &offset, &name)) return new_timezone(offset, name); return NULL; @@ -4777,8 +4773,7 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } PyObject *t; - datetime_state *st = get_datetime_state(); - if ( (PyTypeObject *)cls == st->time_type) { + if ( (PyTypeObject *)cls == &PyDateTime_TimeType) { t = new_time(hour, minute, second, microsecond, tzinfo, 0); } else { t = PyObject_CallFunction(cls, "iiiiO", @@ -5376,10 +5371,9 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) PyObject *tzinfo = NULL; PyObject *result = NULL; - datetime_state *st = get_datetime_state(); if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords, - st->date_type, &date, - st->time_type, &time, &tzinfo)) { + &PyDateTime_DateType, &date, + &PyDateTime_TimeType, &time, &tzinfo)) { if (tzinfo == NULL) { if (HASTZINFO(time)) tzinfo = ((PyDateTime_Time *)time)->tzinfo; @@ -6877,14 +6871,6 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) return 0; } - /* Static types exposed by the C-API. */ - st->date_type = &PyDateTime_DateType; - st->datetime_type = &PyDateTime_DateTimeType; - st->delta_type = &PyDateTime_DeltaType; - st->time_type = &PyDateTime_TimeType; - st->tzinfo_type = &PyDateTime_TZInfoType; - st->timezone_type = &PyDateTime_TimeZoneType; - /* Per-module heap types. */ st->isocalendar_date_type = PyDateTime_IsoCalendarDateType; @@ -6997,26 +6983,26 @@ _datetime_exec(PyObject *module) } while(0) /* timedelta values */ - PyObject *d = st->delta_type->tp_dict; + PyObject *d = PyDateTime_DeltaType.tp_dict; DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0)); DATETIME_ADD_MACRO(d, "max", new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); /* date values */ - d = st->date_type->tp_dict; + d = PyDateTime_DateType.tp_dict; DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1)); DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31)); DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0)); /* time values */ - d = st->time_type->tp_dict; + d = PyDateTime_TimeType.tp_dict; DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0)); DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0)); DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* datetime values */ - d = st->datetime_type->tp_dict; + d = PyDateTime_DateTimeType.tp_dict; DATETIME_ADD_MACRO(d, "min", new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None, 0)); DATETIME_ADD_MACRO(d, "max", new_datetime(MAXYEAR, 12, 31, 23, 59, 59, @@ -7024,7 +7010,7 @@ _datetime_exec(PyObject *module) DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* timezone values */ - d = st->timezone_type->tp_dict; + d = PyDateTime_TimeZoneType.tp_dict; if (PyDict_SetItemString(d, "utc", st->utc) < 0) { goto error; } From ead00838b1b4f6bdcc95bc46c1b570eff0bd068e Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 May 2024 16:37:56 -0600 Subject: [PATCH 02/38] Use the UTC singleton directly. --- Modules/_datetimemodule.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index bae80555b11c0d..3a2f956ea98b67 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -48,9 +48,6 @@ typedef struct { PyObject *us_per_week; // 1e6 * 3600 * 24 * 7 as Python int PyObject *seconds_per_day; // 3600 * 24 as Python int - /* The interned UTC timezone instance */ - PyObject *utc; - /* The interned Unix epoch datetime instance */ PyObject *epoch; @@ -1264,6 +1261,7 @@ create_timezone(PyObject *offset, PyObject *name) } static int delta_bool(PyDateTime_Delta *self); +static PyDateTime_TimeZone utc_timezone; static PyObject * new_timezone(PyObject *offset, PyObject *name) @@ -1273,8 +1271,7 @@ new_timezone(PyObject *offset, PyObject *name) assert(name == NULL || PyUnicode_Check(name)); if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) { - datetime_state *st = get_datetime_state(); - return Py_NewRef(st->utc); + return Py_NewRef(&utc_timezone); } if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && @@ -1487,8 +1484,7 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) if (rv == 1) { // Create a timezone from offset in seconds (0 returns UTC) if (tzoffset == 0) { - datetime_state *st = get_datetime_state(); - return Py_NewRef(st->utc); + return Py_NewRef(&utc_timezone); } PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1); @@ -4069,8 +4065,7 @@ timezone_repr(PyDateTime_TimeZone *self) to use Py_TYPE(self)->tp_name here. */ const char *type_name = Py_TYPE(self)->tp_name; - datetime_state *st = get_datetime_state(); - if (((PyObject *)self) == st->utc) { + if (self == &utc_timezone) { return PyUnicode_FromFormat("%s.utc", type_name); } @@ -4092,8 +4087,7 @@ timezone_str(PyDateTime_TimeZone *self) if (self->name != NULL) { return Py_NewRef(self->name); } - datetime_state *st = get_datetime_state(); - if ((PyObject *)self == st->utc || + if (self == &utc_timezone || (GET_TD_DAYS(self->offset) == 0 && GET_TD_SECONDS(self->offset) == 0 && GET_TD_MICROSECONDS(self->offset) == 0)) @@ -6372,7 +6366,6 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) if (result == NULL) return NULL; - datetime_state *st = get_datetime_state(); /* Make sure result is aware and UTC. */ if (!HASTZINFO(result)) { temp = (PyObject *)result; @@ -6384,7 +6377,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) DATE_GET_MINUTE(result), DATE_GET_SECOND(result), DATE_GET_MICROSECOND(result), - st->utc, + (PyObject *)&utc_timezone, DATE_GET_FOLD(result), Py_TYPE(result)); Py_DECREF(temp); @@ -6393,7 +6386,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) } else { /* Result is already aware - just replace tzinfo. */ - Py_SETREF(result->tzinfo, Py_NewRef(st->utc)); + Py_SETREF(result->tzinfo, Py_NewRef(&utc_timezone)); } /* Attach new tzinfo and let fromutc() do the rest. */ @@ -6845,7 +6838,6 @@ datetime_clear(PyObject *module) Py_CLEAR(st->us_per_day); Py_CLEAR(st->us_per_week); Py_CLEAR(st->seconds_per_day); - Py_CLEAR(st->utc); Py_CLEAR(st->epoch); return 0; } @@ -6907,14 +6899,9 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) return -1; } - /* Init UTC timezone */ - st->utc = create_timezone_from_delta(0, 0, 0, 0); - if (st->utc == NULL) { - return -1; - } - /* Init Unix epoch */ - st->epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, st->utc, 0); + st->epoch = new_datetime( + 1970, 1, 1, 0, 0, 0, 0, (PyObject *)&utc_timezone, 0); if (st->epoch == NULL) { return -1; } @@ -7011,7 +6998,7 @@ _datetime_exec(PyObject *module) /* timezone values */ d = PyDateTime_TimeZoneType.tp_dict; - if (PyDict_SetItemString(d, "utc", st->utc) < 0) { + if (PyDict_SetItemString(d, "utc", (PyObject *)&utc_timezone) < 0) { goto error; } @@ -7034,7 +7021,7 @@ _datetime_exec(PyObject *module) if (PyModule_AddIntMacro(module, MAXYEAR) < 0) { goto error; } - if (PyModule_AddObjectRef(module, "UTC", st->utc) < 0) { + if (PyModule_AddObjectRef(module, "UTC", (PyObject *)&utc_timezone) < 0) { goto error; } From 4c52d5d70ddb6cae9faa0a7a4a60e70dd1b3861d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 27 May 2024 17:27:56 -0600 Subject: [PATCH 03/38] Add module state. --- Modules/_datetimemodule.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 3a2f956ea98b67..155db98cd75882 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -62,6 +62,14 @@ static inline datetime_state* get_datetime_state(void) return &_datetime_global_state; } +static datetime_state * +get_module_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (datetime_state *)state; +} + #define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType) #define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType) @@ -6864,7 +6872,8 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) } /* Per-module heap types. */ - st->isocalendar_date_type = PyDateTime_IsoCalendarDateType; + st->isocalendar_date_type = + (PyTypeObject *)Py_NewRef(PyDateTime_IsoCalendarDateType); st->us_per_ms = PyLong_FromLong(1000); if (st->us_per_ms == NULL) { @@ -6914,6 +6923,9 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) static int _datetime_exec(PyObject *module) { + int rc = -1; + PyTypeObject *PyDateTime_IsoCalendarDateType = NULL; + // `&...` 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 @@ -6944,14 +6956,15 @@ _datetime_exec(PyObject *module) } \ } while (0) - PyTypeObject *PyDateTime_IsoCalendarDateType = NULL; - datetime_state *st = get_datetime_state(); + CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type); +#undef CREATE_TYPE - if (!st->initialized) { - CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type); + datetime_state *st_global = get_datetime_state(); + if (init_state(st_global, PyDateTime_IsoCalendarDateType) < 0) { + goto error; } -#undef CREATE_TYPE + datetime_state *st = get_module_state(module); if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) { goto error; } @@ -7054,11 +7067,15 @@ _datetime_exec(PyObject *module) static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y"); assert(DI100Y == days_before_year(100+1)); - return 0; + rc = 0; + goto finally; error: datetime_clear(module); - return -1; + +finally: + Py_XDECREF(PyDateTime_IsoCalendarDateType); + return rc; } #undef DATETIME_ADD_MACRO @@ -7073,7 +7090,7 @@ static PyModuleDef datetimemodule = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_datetime", .m_doc = "Fast implementation of the datetime type.", - .m_size = 0, + .m_size = sizeof(datetime_state), .m_methods = module_methods, .m_slots = module_slots, }; From 6b291c8614a1098dff44598499232042eed14e87 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 May 2024 14:39:30 -0600 Subject: [PATCH 04/38] Clear the module state when destroying the module. --- Modules/_datetimemodule.c | 94 ++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 20 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 155db98cd75882..0ccd57aa93df1d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -55,7 +55,7 @@ typedef struct { int initialized; } datetime_state; -static datetime_state _datetime_global_state; +static datetime_state _datetime_global_state = {0}; static inline datetime_state* get_datetime_state(void) { @@ -6834,22 +6834,6 @@ get_datetime_capi(void) return &capi; } -static int -datetime_clear(PyObject *module) -{ - datetime_state *st = get_datetime_state(); - - Py_CLEAR(st->us_per_ms); - Py_CLEAR(st->us_per_second); - Py_CLEAR(st->us_per_minute); - Py_CLEAR(st->us_per_hour); - Py_CLEAR(st->us_per_day); - Py_CLEAR(st->us_per_week); - Py_CLEAR(st->seconds_per_day); - Py_CLEAR(st->epoch); - return 0; -} - static PyObject * create_timezone_from_delta(int days, int sec, int ms, int normalize) { @@ -6868,6 +6852,7 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) // While datetime uses global module "state", we unly initialize it once. // The PyLong objects created here (once per process) are not decref'd. if (st->initialized) { + st->initialized += 1; return 0; } @@ -6920,11 +6905,52 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) return 0; } +static int +traverse_state(datetime_state *st, visitproc visit, void *arg) +{ + if (!st->initialized) { + return 0; + } + if (st->initialized > 1) { + return 0; + } + /* heap types */ + Py_VISIT(st->isocalendar_date_type); + + return 0; +} + +static int +clear_state(datetime_state *st) +{ + assert(st->initialized >= 0); + if (!st->initialized) { + return 0; + } + st->initialized -= 1; + if (st->initialized) { + return 0; + } + + Py_CLEAR(st->isocalendar_date_type); + Py_CLEAR(st->us_per_ms); + Py_CLEAR(st->us_per_second); + Py_CLEAR(st->us_per_minute); + Py_CLEAR(st->us_per_hour); + Py_CLEAR(st->us_per_day); + Py_CLEAR(st->us_per_week); + Py_CLEAR(st->seconds_per_day); + Py_CLEAR(st->epoch); + return 0; +} + static int _datetime_exec(PyObject *module) { int rc = -1; PyTypeObject *PyDateTime_IsoCalendarDateType = NULL; + datetime_state *st = get_module_state(module); + datetime_state *st_global = get_datetime_state(); // `&...` is not a constant expression according to a strict reading // of C standards. Fill tp_base at run-time rather than statically. @@ -6959,12 +6985,10 @@ _datetime_exec(PyObject *module) CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type); #undef CREATE_TYPE - datetime_state *st_global = get_datetime_state(); if (init_state(st_global, PyDateTime_IsoCalendarDateType) < 0) { goto error; } - datetime_state *st = get_module_state(module); if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) { goto error; } @@ -7071,7 +7095,8 @@ _datetime_exec(PyObject *module) goto finally; error: - datetime_clear(module); + clear_state(st); + clear_state(st_global); finally: Py_XDECREF(PyDateTime_IsoCalendarDateType); @@ -7086,6 +7111,32 @@ static PyModuleDef_Slot module_slots[] = { {0, NULL}, }; +static int +module_traverse(PyObject *mod, visitproc visit, void *arg) +{ + datetime_state *st = get_module_state(mod); + traverse_state(st, visit, arg); + traverse_state(get_datetime_state(), visit, arg); + return 0; +} + +static int +module_clear(PyObject *mod) +{ + datetime_state *st = get_module_state(mod); + clear_state(st); + clear_state(get_datetime_state()); + return 0; +} + +static void +module_free(void *mod) +{ + datetime_state *st = get_module_state(mod); + clear_state(st); + clear_state(get_datetime_state()); +} + static PyModuleDef datetimemodule = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_datetime", @@ -7093,6 +7144,9 @@ static PyModuleDef datetimemodule = { .m_size = sizeof(datetime_state), .m_methods = module_methods, .m_slots = module_slots, + .m_traverse = module_traverse, + .m_clear = module_clear, + .m_free = module_free, }; PyMODINIT_FUNC From 69c7e1f9fae06780a10e34dfe801d52eae1e60de Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 12:43:39 -0600 Subject: [PATCH 05/38] Create PyDateTime_IsoCalendarDateType in init_state(). --- Modules/_datetimemodule.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0ccd57aa93df1d..6aba7884442351 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6847,7 +6847,7 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize) } static int -init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) +init_state(datetime_state *st, PyObject *module) { // While datetime uses global module "state", we unly initialize it once. // The PyLong objects created here (once per process) are not decref'd. @@ -6857,8 +6857,18 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) } /* Per-module heap types. */ - st->isocalendar_date_type = - (PyTypeObject *)Py_NewRef(PyDateTime_IsoCalendarDateType); +#define ADD_TYPE(FIELD, SPEC, BASE) \ + do { \ + PyObject *cls = PyType_FromModuleAndSpec( \ + module, SPEC, (PyObject *)BASE); \ + if (cls == NULL) { \ + return -1; \ + } \ + st->FIELD = (PyTypeObject *)cls; \ + } while (0) + + ADD_TYPE(isocalendar_date_type, &isocal_spec, &PyTuple_Type); +#undef ADD_TYPE st->us_per_ms = PyLong_FromLong(1000); if (st->us_per_ms == NULL) { @@ -6948,7 +6958,6 @@ static int _datetime_exec(PyObject *module) { int rc = -1; - PyTypeObject *PyDateTime_IsoCalendarDateType = NULL; datetime_state *st = get_module_state(module); datetime_state *st_global = get_datetime_state(); @@ -6973,23 +6982,11 @@ _datetime_exec(PyObject *module) } } -#define CREATE_TYPE(VAR, SPEC, BASE) \ - do { \ - VAR = (PyTypeObject *)PyType_FromModuleAndSpec( \ - module, SPEC, (PyObject *)BASE); \ - if (VAR == NULL) { \ - goto error; \ - } \ - } while (0) - - CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type); -#undef CREATE_TYPE - - if (init_state(st_global, PyDateTime_IsoCalendarDateType) < 0) { + if (init_state(st_global, module) < 0) { goto error; } - if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) { + if (init_state(st, module) < 0) { goto error; } @@ -7099,7 +7096,6 @@ _datetime_exec(PyObject *module) clear_state(st_global); finally: - Py_XDECREF(PyDateTime_IsoCalendarDateType); return rc; } #undef DATETIME_ADD_MACRO From 36dbeeaa43b223b445fed08ad3dea2df7471ac24 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 10:11:55 -0600 Subject: [PATCH 06/38] Track the "current" module in the internal per-interpreter dict. --- Modules/_datetimemodule.c | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 6aba7884442351..1561d292af7976 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -70,6 +70,32 @@ get_module_state(PyObject *module) return (datetime_state *)state; } +#define INTERP_KEY "cached-datetime-module" + +static PyObject * +get_current_module(PyInterpreterState *interp) +{ + PyObject *dict = PyInterpreterState_GetDict(interp); + if (dict == NULL) { + return NULL; + } + PyObject *mod = NULL; + if (PyDict_GetItemStringRef(dict, INTERP_KEY, &mod) < 0) { + return NULL; + } + return mod; +} + +static int +set_current_module(PyInterpreterState *interp, PyObject *mod) +{ + PyObject *dict = PyInterpreterState_GetDict(interp); + if (dict == NULL) { + return -1; + } + return PyDict_SetItemString(dict, INTERP_KEY, mod); +} + #define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType) #define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType) @@ -87,6 +113,7 @@ get_module_state(PyObject *module) #define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) + /* 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 * API returns a C long. In such cases, we have to ensure that the @@ -6961,6 +6988,14 @@ _datetime_exec(PyObject *module) datetime_state *st = get_module_state(module); datetime_state *st_global = get_datetime_state(); + PyInterpreterState *interp = PyInterpreterState_Get(); + PyObject *old_module = get_current_module(interp); + if (PyErr_Occurred()) { + assert(old_module == NULL); + goto error; + } + /* We actually set the "current" module right before a successful return. */ + // `&...` 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 @@ -7088,6 +7123,10 @@ _datetime_exec(PyObject *module) static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y"); assert(DI100Y == days_before_year(100+1)); + if (set_current_module(interp, module) < 0) { + goto error; + } + rc = 0; goto finally; @@ -7096,6 +7135,7 @@ _datetime_exec(PyObject *module) clear_state(st_global); finally: + Py_XDECREF(old_module); return rc; } #undef DATETIME_ADD_MACRO From d0c0b2d35537aec2353205e9af751b5e609fb0ba Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 12:31:31 -0600 Subject: [PATCH 07/38] Copy the "current" module state, if available. --- Modules/_datetimemodule.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 1561d292af7976..b25da8efe4742e 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6873,12 +6873,31 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize) return tz; } +static int +copy_state(datetime_state *st, datetime_state *from) +{ + *st = (datetime_state){ + .isocalendar_date_type + = (PyTypeObject *)Py_NewRef(from->isocalendar_date_type), + .us_per_ms = Py_NewRef(from->us_per_ms), + .us_per_second = Py_NewRef(from->us_per_second), + .us_per_minute = Py_NewRef(from->us_per_minute), + .us_per_hour = Py_NewRef(from->us_per_hour), + .us_per_day = Py_NewRef(from->us_per_day), + .us_per_week = Py_NewRef(from->us_per_week), + .seconds_per_day = Py_NewRef(from->seconds_per_day), + .epoch = Py_NewRef(from->epoch), + }; + return 0; +} + static int init_state(datetime_state *st, PyObject *module) { // While datetime uses global module "state", we unly initialize it once. // The PyLong objects created here (once per process) are not decref'd. if (st->initialized) { + assert(st->initialized > 0); st->initialized += 1; return 0; } @@ -7021,8 +7040,16 @@ _datetime_exec(PyObject *module) goto error; } - if (init_state(st, module) < 0) { - goto error; + if (old_module != NULL) { + datetime_state *st_old = get_module_state(old_module); + if (copy_state(st, st_old) < 0) { + goto error; + } + } + else { + if (init_state(st, module) < 0) { + goto error; + } } #define DATETIME_ADD_MACRO(dict, c, value_expr) \ From 0599dd6a908a739db9be4d8dcbb235e357b88e81 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 15:00:09 -0600 Subject: [PATCH 08/38] Make state usage explicit. --- Modules/_datetimemodule.c | 358 +++++++++++++++++++++++--------------- 1 file changed, 215 insertions(+), 143 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index b25da8efe4742e..9a4a7f25b0503c 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -55,6 +55,43 @@ typedef struct { int initialized; } datetime_state; +#define GLOBAL_STATE NULL; (void)st + +#define DATE_TYPE(st) &PyDateTime_DateType +#define DATETIME_TYPE(st) &PyDateTime_DateTimeType +#define TIME_TYPE(st) &PyDateTime_TimeType +#define DELTA_TYPE(st) &PyDateTime_DeltaType +#define TZINFO_TYPE(st) &PyDateTime_TZInfoType +#define TIMEZONE_TYPE(st) &PyDateTime_TimeZoneType +#define ISOCALENDAR_DATE_TYPE(st) st->isocalendar_date_type + +#define PyDate_Check(st, op) PyObject_TypeCheck(op, DATE_TYPE(st)) +#define PyDate_CheckExact(st, op) Py_IS_TYPE(op, DATE_TYPE(st)) + +#define PyDateTime_Check(st, op) PyObject_TypeCheck(op, DATETIME_TYPE(st)) +#define PyDateTime_CheckExact(st, op) Py_IS_TYPE(op, DATETIME_TYPE(st)) + +#define PyTime_Check(st, op) PyObject_TypeCheck(op, TIME_TYPE(st)) +#define PyTime_CheckExact(st, op) Py_IS_TYPE(op, TIME_TYPE(st)) + +#define PyDelta_Check(st, op) PyObject_TypeCheck(op, DELTA_TYPE(st)) +#define PyDelta_CheckExact(st, op) Py_IS_TYPE(op, DELTA_TYPE(st)) + +#define PyTZInfo_Check(st, op) PyObject_TypeCheck(op, TZINFO_TYPE(st)) +#define PyTZInfo_CheckExact(st, op) Py_IS_TYPE(op, TZINFO_TYPE(st)) + +#define PyTimezone_Check(st, op) PyObject_TypeCheck(op, TIMEZONE_TYPE(st)) + +#define CONST_US_PER_MS(st) st->us_per_ms +#define CONST_US_PER_SECOND(st) st->us_per_second +#define CONST_US_PER_MINUTE(st) st->us_per_minute +#define CONST_US_PER_HOUR(st) st->us_per_hour +#define CONST_US_PER_DAY(st) st->us_per_day +#define CONST_US_PER_WEEK(st) st->us_per_week +#define CONST_SEC_PER_DAY(st) st->seconds_per_day +#define CONST_EPOCH(st) st->epoch +#define CONST_UTC(st) ((PyObject *)&utc_timezone) + static datetime_state _datetime_global_state = {0}; static inline datetime_state* get_datetime_state(void) @@ -70,6 +107,7 @@ get_module_state(PyObject *module) return (datetime_state *)state; } + #define INTERP_KEY "cached-datetime-module" static PyObject * @@ -96,23 +134,6 @@ set_current_module(PyInterpreterState *interp, PyObject *mod) return PyDict_SetItemString(dict, INTERP_KEY, mod); } -#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType) -#define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType) - -#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType) -#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateTimeType) - -#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType) -#define PyTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TimeType) - -#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType) -#define PyDelta_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DeltaType) - -#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType) -#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TZInfoType) - -#define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) - /* 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 @@ -1020,8 +1041,8 @@ new_date_ex(int year, int month, int day, PyTypeObject *type) return (PyObject *)self; } -#define new_date(year, month, day) \ - new_date_ex(year, month, day, &PyDateTime_DateType) +#define new_date(st, year, month, day) \ + new_date_ex(year, month, day, DATE_TYPE(st)) // Forward declaration static PyObject * @@ -1032,11 +1053,12 @@ static PyObject * new_date_subclass_ex(int year, int month, int day, PyObject *cls) { PyObject *result; + datetime_state *st = GLOBAL_STATE; // We have "fast path" constructors for two subclasses: date and datetime - if ((PyTypeObject *)cls == &PyDateTime_DateType) { + if ((PyTypeObject *)cls == DATE_TYPE(st)) { result = new_date_ex(year, month, day, (PyTypeObject *)cls); } - else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) { + else if ((PyTypeObject *)cls == DATETIME_TYPE(st)) { result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None, (PyTypeObject *)cls); } @@ -1089,9 +1111,8 @@ new_datetime_ex(int year, int month, int day, int hour, int minute, tzinfo, 0, type); } -#define new_datetime(y, m, d, hh, mm, ss, us, tzinfo, fold) \ - new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, \ - &PyDateTime_DateTimeType) +#define new_datetime(st, y, m, d, hh, mm, ss, us, tzinfo, fold) \ + new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, DATETIME_TYPE(st)) static PyObject * call_subclass_fold(PyObject *cls, int fold, const char *format, ...) @@ -1133,9 +1154,10 @@ new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute int fold, PyObject *cls) { PyObject* dt; - if ((PyTypeObject*)cls == &PyDateTime_DateTimeType) { + datetime_state *st = GLOBAL_STATE; + if ((PyTypeObject*)cls == DATETIME_TYPE(st)) { // Use the fast path constructor - dt = new_datetime(year, month, day, hour, minute, second, usecond, + dt = new_datetime(st, year, month, day, hour, minute, second, usecond, tzinfo, fold); } else { @@ -1194,17 +1216,18 @@ new_time_ex(int hour, int minute, int second, int usecond, return new_time_ex2(hour, minute, second, usecond, tzinfo, 0, type); } -#define new_time(hh, mm, ss, us, tzinfo, fold) \ - new_time_ex2(hh, mm, ss, us, tzinfo, fold, &PyDateTime_TimeType) +#define new_time(st, hh, mm, ss, us, tzinfo, fold) \ + new_time_ex2(hh, mm, ss, us, tzinfo, fold, TIME_TYPE(st)) static PyObject * new_time_subclass_fold_ex(int hour, int minute, int second, int usecond, PyObject *tzinfo, int fold, PyObject *cls) { PyObject *t; - if ((PyTypeObject*)cls == &PyDateTime_TimeType) { + datetime_state *st = GLOBAL_STATE; + if ((PyTypeObject*)cls == TIME_TYPE(st)) { // Use the fast path constructor - t = new_time(hour, minute, second, usecond, tzinfo, fold); + t = new_time(st, hour, minute, second, usecond, tzinfo, fold); } else { // Subclass @@ -1253,8 +1276,8 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize, return (PyObject *) self; } -#define new_delta(d, s, us, normalize) \ - new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType) +#define new_delta(st, d, s, us, normalize) \ + new_delta_ex(d, s, us, normalize, DELTA_TYPE(st)) typedef struct @@ -1274,10 +1297,11 @@ static PyObject * create_timezone(PyObject *offset, PyObject *name) { PyDateTime_TimeZone *self; - PyTypeObject *type = &PyDateTime_TimeZoneType; + datetime_state *st = GLOBAL_STATE; + PyTypeObject *type = TIMEZONE_TYPE(st); assert(offset != NULL); - assert(PyDelta_Check(offset)); + assert(PyDelta_Check(st, offset)); assert(name == NULL || PyUnicode_Check(name)); self = look_up_timezone(offset, name); @@ -1302,11 +1326,12 @@ static PyObject * new_timezone(PyObject *offset, PyObject *name) { assert(offset != NULL); - assert(PyDelta_Check(offset)); + datetime_state *st = GLOBAL_STATE; + assert(PyDelta_Check(st, offset)); assert(name == NULL || PyUnicode_Check(name)); if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) { - return Py_NewRef(&utc_timezone); + return Py_NewRef(CONST_UTC(st)); } if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && @@ -1332,7 +1357,8 @@ new_timezone(PyObject *offset, PyObject *name) static int check_tzinfo_subclass(PyObject *p) { - if (p == Py_None || PyTZInfo_Check(p)) + datetime_state *st = GLOBAL_STATE; + if (p == Py_None || PyTZInfo_Check(st, p)) return 0; PyErr_Format(PyExc_TypeError, "tzinfo argument must be None or of a tzinfo subclass, " @@ -1350,9 +1376,9 @@ get_tzinfo_member(PyObject *self) { PyObject *tzinfo = NULL; - if (PyDateTime_Check(self) && HASTZINFO(self)) + if (PyDateTime_Check(st, self) && HASTZINFO(self)) tzinfo = ((PyDateTime_DateTime *)self)->tzinfo; - else if (PyTime_Check(self) && HASTZINFO(self)) + else if (PyTime_Check(st, self) && HASTZINFO(self)) tzinfo = ((PyDateTime_Time *)self)->tzinfo; return tzinfo; @@ -1371,7 +1397,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) PyObject *offset; assert(tzinfo != NULL); - assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None); + assert(PyTZInfo_Check(st, tzinfo) || tzinfo == Py_None); assert(tzinfoarg != NULL); if (tzinfo == Py_None) @@ -1379,7 +1405,8 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); if (offset == Py_None || offset == NULL) return offset; - if (PyDelta_Check(offset)) { + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, offset)) { if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && GET_TD_MICROSECONDS(offset) < 1) || @@ -1519,10 +1546,11 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) if (rv == 1) { // Create a timezone from offset in seconds (0 returns UTC) if (tzoffset == 0) { - return Py_NewRef(&utc_timezone); + datetime_state *st = GLOBAL_STATE; + return Py_NewRef(CONST_UTC(st)); } - PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1); + PyObject *delta = new_delta(st, 0, tzoffset, tz_useconds, 1); if (delta == NULL) { return NULL; } @@ -1687,9 +1715,9 @@ static PyObject * make_freplacement(PyObject *object) { char freplacement[64]; - if (PyTime_Check(object)) + if (PyTime_Check(st, object)) sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object)); - else if (PyDateTime_Check(object)) + else if (PyDateTime_Check(st, object)) sprintf(freplacement, "%06d", DATE_GET_MICROSECOND(object)); else sprintf(freplacement, "%06d", 0); @@ -1952,7 +1980,7 @@ delta_to_microseconds(PyDateTime_Delta *self) if (x1 == NULL) goto Done; datetime_state *st = get_datetime_state(); - x2 = PyNumber_Multiply(x1, st->seconds_per_day); /* days in seconds */ + x2 = PyNumber_Multiply(x1, CONST_SEC_PER_DAY(st)); /* days in seconds */ if (x2 == NULL) goto Done; Py_SETREF(x1, NULL); @@ -1969,7 +1997,7 @@ delta_to_microseconds(PyDateTime_Delta *self) /* x1 = */ x2 = NULL; /* x3 has days+seconds in seconds */ - x1 = PyNumber_Multiply(x3, st->us_per_second); /* us */ + x1 = PyNumber_Multiply(x3, CONST_US_PER_SECOND(st)); /* us */ if (x1 == NULL) goto Done; Py_SETREF(x3, NULL); @@ -2025,7 +2053,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *result = NULL; datetime_state *st = get_datetime_state(); - tuple = checked_divmod(pyus, st->us_per_second); + tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st)); if (tuple == NULL) { goto Done; } @@ -2043,7 +2071,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) num = Py_NewRef(PyTuple_GET_ITEM(tuple, 0)); /* leftover seconds */ Py_DECREF(tuple); - tuple = checked_divmod(num, st->seconds_per_day); + tuple = checked_divmod(num, CONST_SEC_PER_DAY(st)); if (tuple == NULL) goto Done; Py_DECREF(num); @@ -2076,8 +2104,8 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) goto Done; } -#define microseconds_to_delta(pymicros) \ - microseconds_to_delta_ex(pymicros, &PyDateTime_DeltaType) +#define microseconds_to_delta(st, pymicros) \ + microseconds_to_delta_ex(pymicros, DELTA_TYPE(st)) static PyObject * multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) @@ -2095,7 +2123,8 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) if (pyus_out == NULL) return NULL; - result = microseconds_to_delta(pyus_out); + datetime_state *st = GLOBAL_STATE; + result = microseconds_to_delta(st, pyus_out); Py_DECREF(pyus_out); return result; } @@ -2150,7 +2179,8 @@ multiply_truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *floatobj, Py_DECREF(temp); if (pyus_out == NULL) goto error; - result = microseconds_to_delta(pyus_out); + datetime_state *st = GLOBAL_STATE; + result = microseconds_to_delta(st, pyus_out); Py_DECREF(pyus_out); error: Py_XDECREF(pyus_in); @@ -2175,7 +2205,8 @@ divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj) if (pyus_out == NULL) return NULL; - result = microseconds_to_delta(pyus_out); + datetime_state *st = GLOBAL_STATE; + result = microseconds_to_delta(st, pyus_out); Py_DECREF(pyus_out); return result; } @@ -2238,7 +2269,8 @@ truedivide_timedelta_int(PyDateTime_Delta *delta, PyObject *i) Py_DECREF(pyus_in); if (pyus_out == NULL) return NULL; - result = microseconds_to_delta(pyus_out); + datetime_state *st = GLOBAL_STATE; + result = microseconds_to_delta(st, pyus_out); Py_DECREF(pyus_out); return result; @@ -2249,7 +2281,8 @@ delta_add(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left) && PyDelta_Check(right)) { + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, left) && PyDelta_Check(st, right)) { /* delta + delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2258,7 +2291,7 @@ delta_add(PyObject *left, PyObject *right) int seconds = GET_TD_SECONDS(left) + GET_TD_SECONDS(right); int microseconds = GET_TD_MICROSECONDS(left) + GET_TD_MICROSECONDS(right); - result = new_delta(days, seconds, microseconds, 1); + result = new_delta(st, days, seconds, microseconds, 1); } if (result == Py_NotImplemented) @@ -2269,7 +2302,9 @@ delta_add(PyObject *left, PyObject *right) static PyObject * delta_negative(PyDateTime_Delta *self) { - return new_delta(-GET_TD_DAYS(self), + datetime_state *st = GLOBAL_STATE; + return new_delta(st, + -GET_TD_DAYS(self), -GET_TD_SECONDS(self), -GET_TD_MICROSECONDS(self), 1); @@ -2281,7 +2316,9 @@ delta_positive(PyDateTime_Delta *self) /* Could optimize this (by returning self) if this isn't a * subclass -- but who uses unary + ? Approximately nobody. */ - return new_delta(GET_TD_DAYS(self), + datetime_state *st = GLOBAL_STATE; + return new_delta(st, + GET_TD_DAYS(self), GET_TD_SECONDS(self), GET_TD_MICROSECONDS(self), 0); @@ -2308,7 +2345,8 @@ delta_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left) && PyDelta_Check(right)) { + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, left) && PyDelta_Check(st, right)) { /* delta - delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2317,7 +2355,7 @@ delta_subtract(PyObject *left, PyObject *right) int seconds = GET_TD_SECONDS(left) - GET_TD_SECONDS(right); int microseconds = GET_TD_MICROSECONDS(left) - GET_TD_MICROSECONDS(right); - result = new_delta(days, seconds, microseconds, 1); + result = new_delta(st, days, seconds, microseconds, 1); } if (result == Py_NotImplemented) @@ -2341,7 +2379,8 @@ delta_cmp(PyObject *self, PyObject *other) static PyObject * delta_richcompare(PyObject *self, PyObject *other, int op) { - if (PyDelta_Check(other)) { + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, other)) { int diff = delta_cmp(self, other); return diff_to_bool(diff, op); } @@ -2370,7 +2409,8 @@ delta_multiply(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left)) { + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, left)) { /* delta * ??? */ if (PyLong_Check(right)) result = multiply_int_timedelta(right, @@ -2396,13 +2436,14 @@ delta_divide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left)) { + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, left)) { /* delta * ??? */ if (PyLong_Check(right)) result = divide_timedelta_int( (PyDateTime_Delta *)left, right); - else if (PyDelta_Check(right)) + else if (PyDelta_Check(st, right)) result = divide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2418,8 +2459,9 @@ delta_truedivide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left)) { - if (PyDelta_Check(right)) + datetime_state *st = GLOBAL_STATE; + if (PyDelta_Check(st, left)) { + if (PyDelta_Check(st, right)) result = truedivide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2444,7 +2486,8 @@ delta_remainder(PyObject *left, PyObject *right) PyObject *pyus_remainder; PyObject *remainder; - if (!PyDelta_Check(left) || !PyDelta_Check(right)) + datetime_state *st = GLOBAL_STATE; + if (!PyDelta_Check(st, left) || !PyDelta_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -2463,7 +2506,7 @@ delta_remainder(PyObject *left, PyObject *right) if (pyus_remainder == NULL) return NULL; - remainder = microseconds_to_delta(pyus_remainder); + remainder = microseconds_to_delta(st, pyus_remainder); Py_DECREF(pyus_remainder); if (remainder == NULL) return NULL; @@ -2480,7 +2523,8 @@ delta_divmod(PyObject *left, PyObject *right) PyObject *delta; PyObject *result; - if (!PyDelta_Check(left) || !PyDelta_Check(right)) + datetime_state *st = GLOBAL_STATE; + if (!PyDelta_Check(st, left) || !PyDelta_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -2499,7 +2543,7 @@ delta_divmod(PyObject *left, PyObject *right) if (divmod == NULL) return NULL; - delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1)); + delta = microseconds_to_delta(st, PyTuple_GET_ITEM(divmod, 1)); if (delta == NULL) { Py_DECREF(divmod); return NULL; @@ -2645,27 +2689,27 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) } datetime_state *st = get_datetime_state(); if (ms) { - y = accum("milliseconds", x, ms, st->us_per_ms, &leftover_us); + y = accum("milliseconds", x, ms, CONST_US_PER_MS(st), &leftover_us); CLEANUP; } if (second) { - y = accum("seconds", x, second, st->us_per_second, &leftover_us); + y = accum("seconds", x, second, CONST_US_PER_SECOND(st), &leftover_us); CLEANUP; } if (minute) { - y = accum("minutes", x, minute, st->us_per_minute, &leftover_us); + y = accum("minutes", x, minute, CONST_US_PER_MINUTE(st), &leftover_us); CLEANUP; } if (hour) { - y = accum("hours", x, hour, st->us_per_hour, &leftover_us); + y = accum("hours", x, hour, CONST_US_PER_HOUR(st), &leftover_us); CLEANUP; } if (day) { - y = accum("days", x, day, st->us_per_day, &leftover_us); + y = accum("days", x, day, CONST_US_PER_DAY(st), &leftover_us); CLEANUP; } if (week) { - y = accum("weeks", x, week, st->us_per_week, &leftover_us); + y = accum("weeks", x, week, CONST_US_PER_WEEK(st), &leftover_us); CLEANUP; } if (leftover_us) { @@ -2821,7 +2865,7 @@ delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored)) return NULL; datetime_state *st = get_datetime_state(); - total_seconds = PyNumber_TrueDivide(total_microseconds, st->us_per_second); + total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st)); Py_DECREF(total_microseconds); return total_seconds; @@ -3272,12 +3316,14 @@ add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate) static PyObject * date_add(PyObject *left, PyObject *right) { - if (PyDateTime_Check(left) || PyDateTime_Check(right)) + datetime_state *st = GLOBAL_STATE; + + if (PyDateTime_Check(st, left) || PyDateTime_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(left)) { + if (PyDate_Check(st, left)) { /* date + ??? */ - if (PyDelta_Check(right)) + if (PyDelta_Check(st, right)) /* date + delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3287,7 +3333,7 @@ date_add(PyObject *left, PyObject *right) /* ??? + date * 'right' must be one of us, or we wouldn't have been called */ - if (PyDelta_Check(left)) + if (PyDelta_Check(st, left)) /* delta + date */ return add_date_timedelta((PyDateTime_Date *) right, (PyDateTime_Delta *) left, @@ -3299,11 +3345,13 @@ date_add(PyObject *left, PyObject *right) static PyObject * date_subtract(PyObject *left, PyObject *right) { - if (PyDateTime_Check(left) || PyDateTime_Check(right)) + datetime_state *st = GLOBAL_STATE; + + if (PyDateTime_Check(st, left) || PyDateTime_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(left)) { - if (PyDate_Check(right)) { + if (PyDate_Check(st, left)) { + if (PyDate_Check(st, right)) { /* date - date */ int left_ord = ymd_to_ord(GET_YEAR(left), GET_MONTH(left), @@ -3311,9 +3359,9 @@ date_subtract(PyObject *left, PyObject *right) int right_ord = ymd_to_ord(GET_YEAR(right), GET_MONTH(right), GET_DAY(right)); - return new_delta(left_ord - right_ord, 0, 0, 0); + return new_delta(st, left_ord - right_ord, 0, 0, 0); } - if (PyDelta_Check(right)) { + if (PyDelta_Check(st, right)) { /* date - delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3576,7 +3624,7 @@ date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) } datetime_state *st = get_datetime_state(); - PyObject *v = iso_calendar_date_new_impl(st->isocalendar_date_type, + PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st), year, week + 1, day + 1); if (v == NULL) { return NULL; @@ -3597,7 +3645,7 @@ date_richcompare(PyObject *self, PyObject *other, int op) * The behavior is the same as if Date and DateTime were independent * classes. */ - if (PyDate_Check(other) && !PyDateTime_Check(other)) { + if (PyDate_Check(st, other) && !PyDateTime_Check(st, other)) { int diff = memcmp(((PyDateTime_Date *)self)->data, ((PyDateTime_Date *)other)->data, _PyDateTime_DATE_DATASIZE); @@ -3873,7 +3921,7 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt) PyObject *off = NULL, *dst = NULL; PyDateTime_Delta *delta = NULL; - if (!PyDateTime_Check(dt)) { + if (!PyDateTime_Check(st, dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -4046,8 +4094,9 @@ timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *offset; PyObject *name = NULL; + datetime_state *st = GLOBAL_STATE; if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws, - &PyDateTime_DeltaType, &offset, &name)) + DELTA_TYPE(st), &offset, &name)) return new_timezone(offset, name); return NULL; @@ -4067,7 +4116,7 @@ timezone_richcompare(PyDateTime_TimeZone *self, { if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED; - if (!PyTimezone_Check(other)) { + if (!PyTimezone_Check(st, other)) { Py_RETURN_NOTIMPLEMENTED; } return delta_richcompare(self->offset, other->offset, op); @@ -4086,7 +4135,7 @@ timezone_hash(PyDateTime_TimeZone *self) static int _timezone_check_argument(PyObject *dt, const char *meth) { - if (dt == Py_None || PyDateTime_Check(dt)) + if (dt == Py_None || PyDateTime_Check(st, dt)) return 0; PyErr_Format(PyExc_TypeError, "%s(dt) argument must be a datetime instance" " or None, not %.200s", meth, Py_TYPE(dt)->tp_name); @@ -4100,7 +4149,8 @@ timezone_repr(PyDateTime_TimeZone *self) to use Py_TYPE(self)->tp_name here. */ const char *type_name = Py_TYPE(self)->tp_name; - if (self == &utc_timezone) { + datetime_state *st = GLOBAL_STATE; + if ((PyObject *)self == CONST_UTC(st)) { return PyUnicode_FromFormat("%s.utc", type_name); } @@ -4122,7 +4172,8 @@ timezone_str(PyDateTime_TimeZone *self) if (self->name != NULL) { return Py_NewRef(self->name); } - if (self == &utc_timezone || + datetime_state *st = GLOBAL_STATE; + if ((PyObject *)self == CONST_UTC(st) || (GET_TD_DAYS(self->offset) == 0 && GET_TD_SECONDS(self->offset) == 0 && GET_TD_MICROSECONDS(self->offset) == 0)) @@ -4188,7 +4239,7 @@ timezone_dst(PyObject *self, PyObject *dt) static PyObject * timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) { - if (!PyDateTime_Check(dt)) { + if (!PyDateTime_Check(st, dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -4285,7 +4336,8 @@ static PyDateTime_TimeZone * look_up_timezone(PyObject *offset, PyObject *name) { if (offset == utc_timezone.offset && name == NULL) { - return &utc_timezone; + datetime_state *st = GLOBAL_STATE; + return (PyDateTime_TimeZone *)CONST_UTC(st); } return NULL; } @@ -4617,7 +4669,7 @@ time_richcompare(PyObject *self, PyObject *other, int op) PyObject *offset1, *offset2; int diff; - if (! PyTime_Check(other)) + if (! PyTime_Check(st, other)) Py_RETURN_NOTIMPLEMENTED; if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) { @@ -4636,8 +4688,9 @@ time_richcompare(PyObject *self, PyObject *other, int op) * we get off cheap. Note that if they're both naive, offset1 == * offset2 == Py_None at this point. */ + datetime_state *st = GLOBAL_STATE; if ((offset1 == offset2) || - (PyDelta_Check(offset1) && PyDelta_Check(offset2) && + (PyDelta_Check(st, offset1) && PyDelta_Check(st, offset2) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_Time *)self)->data, ((PyDateTime_Time *)other)->data, @@ -4717,7 +4770,7 @@ time_hash(PyDateTime_Time *self) TIME_GET_MINUTE(self) * 60 + TIME_GET_SECOND(self); microseconds = TIME_GET_MICROSECOND(self); - temp1 = new_delta(0, seconds, microseconds, 1); + temp1 = new_delta(st, 0, seconds, microseconds, 1); if (temp1 == NULL) { Py_DECREF(offset); return -1; @@ -4802,8 +4855,9 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } PyObject *t; - if ( (PyTypeObject *)cls == &PyDateTime_TimeType) { - t = new_time(hour, minute, second, microsecond, tzinfo, 0); + datetime_state *st = GLOBAL_STATE; + if ( (PyTypeObject *)cls == TIME_TYPE(st)) { + t = new_time(st, hour, minute, second, microsecond, tzinfo, 0); } else { t = PyObject_CallFunction(cls, "iiiiO", hour, minute, second, microsecond, tzinfo); @@ -5400,9 +5454,10 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) PyObject *tzinfo = NULL; PyObject *result = NULL; + datetime_state *st = GLOBAL_STATE; if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords, - &PyDateTime_DateType, &date, - &PyDateTime_TimeType, &time, &tzinfo)) { + DATE_TYPE(st), &date, + TIME_TYPE(st), &time, &tzinfo)) { if (tzinfo == NULL) { if (HASTZINFO(time)) tzinfo = ((PyDateTime_Time *)time)->tzinfo; @@ -5745,16 +5800,17 @@ add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta, static PyObject * datetime_add(PyObject *left, PyObject *right) { - if (PyDateTime_Check(left)) { + datetime_state *st = GLOBAL_STATE; + if (PyDateTime_Check(st, left)) { /* datetime + ??? */ - if (PyDelta_Check(right)) + if (PyDelta_Check(st, right)) /* datetime + delta */ return add_datetime_timedelta( (PyDateTime_DateTime *)left, (PyDateTime_Delta *)right, 1); } - else if (PyDelta_Check(left)) { + else if (PyDelta_Check(st, left)) { /* delta + datetime */ return add_datetime_timedelta((PyDateTime_DateTime *) right, (PyDateTime_Delta *) left, @@ -5768,9 +5824,10 @@ datetime_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDateTime_Check(left)) { + datetime_state *st = GLOBAL_STATE; + if (PyDateTime_Check(st, left)) { /* datetime - ??? */ - if (PyDateTime_Check(right)) { + if (PyDateTime_Check(st, right)) { /* datetime - datetime */ PyObject *offset1, *offset2, *offdiff = NULL; int delta_d, delta_s, delta_us; @@ -5826,7 +5883,7 @@ datetime_subtract(PyObject *left, PyObject *right) DATE_GET_SECOND(right)); delta_us = DATE_GET_MICROSECOND(left) - DATE_GET_MICROSECOND(right); - result = new_delta(delta_d, delta_s, delta_us, 1); + result = new_delta(st, delta_d, delta_s, delta_us, 1); if (result == NULL) return NULL; @@ -5835,7 +5892,7 @@ datetime_subtract(PyObject *left, PyObject *right) Py_DECREF(offdiff); } } - else if (PyDelta_Check(right)) { + else if (PyDelta_Check(st, right)) { /* datetime - delta */ result = add_datetime_timedelta( (PyDateTime_DateTime *)left, @@ -6050,7 +6107,7 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) PyObject *offset1, *offset2; int diff; - if (!PyDateTime_Check(other)) { + if (!PyDateTime_Check(st, other)) { Py_RETURN_NOTIMPLEMENTED; } @@ -6070,8 +6127,9 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) * we get off cheap. Note that if they're both naive, offset1 == * offset2 == Py_None at this point. */ + datetime_state *st = GLOBAL_STATE; if ((offset1 == offset2) || - (PyDelta_Check(offset1) && PyDelta_Check(offset2) && + (PyDelta_Check(st, offset1) && PyDelta_Check(st, offset2) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_DateTime *)self)->data, ((PyDateTime_DateTime *)other)->data, @@ -6166,7 +6224,8 @@ datetime_hash(PyDateTime_DateTime *self) seconds = DATE_GET_HOUR(self) * 3600 + DATE_GET_MINUTE(self) * 60 + DATE_GET_SECOND(self); - temp1 = new_delta(days, seconds, + datetime_state *st = GLOBAL_STATE; + temp1 = new_delta(st, days, seconds, DATE_GET_MICROSECOND(self), 1); if (temp1 == NULL) { @@ -6229,7 +6288,8 @@ local_timezone_from_timestamp(time_t timestamp) return NULL; #ifdef HAVE_STRUCT_TM_TM_ZONE zone = local_time_tm.tm_zone; - delta = new_delta(0, local_time_tm.tm_gmtoff, 0, 1); + datetime_state *st = GLOBAL_STATE; + delta = new_delta(st, 0, local_time_tm.tm_gmtoff, 0, 1); #else /* HAVE_STRUCT_TM_TM_ZONE */ { datetime_state *st = get_datetime_state(); @@ -6238,7 +6298,8 @@ local_timezone_from_timestamp(time_t timestamp) char buf[100]; strftime(buf, sizeof(buf), "%Z", &local_time_tm); zone = buf; - local_time = new_datetime(local_time_tm.tm_year + 1900, + local_time = new_datetime(st, + local_time_tm.tm_year + 1900, local_time_tm.tm_mon + 1, local_time_tm.tm_mday, local_time_tm.tm_hour, @@ -6249,7 +6310,8 @@ local_timezone_from_timestamp(time_t timestamp) } if (_PyTime_gmtime(timestamp, &utc_time_tm) != 0) return NULL; - utc_time = new_datetime(utc_time_tm.tm_year + 1900, + utc_time = new_datetime(st, + utc_time_tm.tm_year + 1900, utc_time_tm.tm_mon + 1, utc_time_tm.tm_mday, utc_time_tm.tm_hour, @@ -6288,11 +6350,11 @@ local_timezone(PyDateTime_DateTime *utc_time) PyObject *seconds; datetime_state *st = get_datetime_state(); - delta = datetime_subtract((PyObject *)utc_time, st->epoch); + delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st)); if (delta == NULL) return NULL; - one_second = new_delta(0, 1, 0, 0); + one_second = new_delta(st, 0, 1, 0, 0); if (one_second == NULL) { Py_DECREF(delta); return NULL; @@ -6382,13 +6444,14 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) /* Convert self to UTC. */ offset = call_utcoffset(self_tzinfo, (PyObject *)self); Py_DECREF(self_tzinfo); + datetime_state *st = GLOBAL_STATE; if (offset == NULL) return NULL; else if(offset == Py_None) { Py_DECREF(offset); goto naive; } - else if (!PyDelta_Check(offset)) { + else if (!PyDelta_Check(st, offset)) { Py_DECREF(offset); PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s," " expected timedelta or None", Py_TYPE(offset)->tp_name); @@ -6412,7 +6475,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) DATE_GET_MINUTE(result), DATE_GET_SECOND(result), DATE_GET_MICROSECOND(result), - (PyObject *)&utc_timezone, + CONST_UTC(st), DATE_GET_FOLD(result), Py_TYPE(result)); Py_DECREF(temp); @@ -6421,7 +6484,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) } else { /* Result is already aware - just replace tzinfo. */ - Py_SETREF(result->tzinfo, Py_NewRef(&utc_timezone)); + Py_SETREF(result->tzinfo, Py_NewRef(CONST_UTC(st))); } /* Attach new tzinfo and let fromutc() do the rest. */ @@ -6527,7 +6590,7 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) if (HASTZINFO(self) && self->tzinfo != Py_None) { datetime_state *st = get_datetime_state(); PyObject *delta; - delta = datetime_subtract((PyObject *)self, st->epoch); + delta = datetime_subtract((PyObject *)self, CONST_EPOCH(st)); if (delta == NULL) return NULL; result = delta_total_seconds(delta, NULL); @@ -6553,7 +6616,9 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { - return new_date(GET_YEAR(self), + datetime_state *st = GLOBAL_STATE; + return new_date(st, + GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); } @@ -6561,7 +6626,9 @@ datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { - return new_time(DATE_GET_HOUR(self), + datetime_state *st = GLOBAL_STATE; + return new_time(st, + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), DATE_GET_MICROSECOND(self), @@ -6572,7 +6639,9 @@ datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_gettimetz(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { - return new_time(DATE_GET_HOUR(self), + datetime_state *st = GLOBAL_STATE; + return new_time(st, + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), DATE_GET_MICROSECOND(self), @@ -6864,7 +6933,8 @@ get_datetime_capi(void) static PyObject * create_timezone_from_delta(int days, int sec, int ms, int normalize) { - PyObject *delta = new_delta(days, sec, ms, normalize); + datetime_state *st = GLOBAL_STATE; + PyObject *delta = new_delta(st, days, sec, ms, normalize); if (delta == NULL) { return NULL; } @@ -6951,7 +7021,7 @@ init_state(datetime_state *st, PyObject *module) /* Init Unix epoch */ st->epoch = new_datetime( - 1970, 1, 1, 0, 0, 0, 0, (PyObject *)&utc_timezone, 0); + st, 1970, 1, 1, 0, 0, 0, 0, (PyObject *)&utc_timezone, 0); if (st->epoch == NULL) { return -1; } @@ -7027,6 +7097,7 @@ _datetime_exec(PyObject *module) &PyDateTime_TimeType, &PyDateTime_DeltaType, &PyDateTime_TZInfoType, + /* Indirectly, via the utc object. */ &PyDateTime_TimeZoneType, }; @@ -7067,30 +7138,31 @@ _datetime_exec(PyObject *module) /* timedelta values */ PyObject *d = PyDateTime_DeltaType.tp_dict; - DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); - DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0)); - DATETIME_ADD_MACRO(d, "max", - new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "min", new_delta(st_global, -MAX_DELTA_DAYS, 0, 0, 0)); + DATETIME_ADD_MACRO( + d, "max", new_delta(st_global, MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); /* date values */ d = PyDateTime_DateType.tp_dict; - DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1)); - DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0)); + DATETIME_ADD_MACRO(d, "min", new_date(st_global, 1, 1, 1)); + DATETIME_ADD_MACRO(d, "max", new_date(st_global, MAXYEAR, 12, 31)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 1, 0, 0, 0)); /* time values */ d = PyDateTime_TimeType.tp_dict; - DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0)); - DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "min", new_time(st_global, 0, 0, 0, 0, Py_None, 0)); + DATETIME_ADD_MACRO(d, "max", new_time(st_global, 23, 59, 59, 999999, Py_None, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 0, 0, 1, 0)); /* datetime values */ d = PyDateTime_DateTimeType.tp_dict; - DATETIME_ADD_MACRO(d, "min", - new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None, 0)); - DATETIME_ADD_MACRO(d, "max", new_datetime(MAXYEAR, 12, 31, 23, 59, 59, - 999999, Py_None, 0)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); + DATETIME_ADD_MACRO( + d, "min", new_datetime(st_global, 1, 1, 1, 0, 0, 0, 0, Py_None, 0)); + DATETIME_ADD_MACRO( + d, "max", new_datetime( + st_global, MAXYEAR, 12, 31, 23, 59, 59, 999999, Py_None, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 0, 0, 1, 0)); /* timezone values */ d = PyDateTime_TimeZoneType.tp_dict; From 1f1f6fc52d33cc384c560bf49ee9a529992eda93 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 15:03:03 -0600 Subject: [PATCH 09/38] Reduce churn. --- Modules/_datetimemodule.c | 298 ++++++++++++++++---------------------- 1 file changed, 123 insertions(+), 175 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 9a4a7f25b0503c..30d56c8e4c673d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -55,7 +55,7 @@ typedef struct { int initialized; } datetime_state; -#define GLOBAL_STATE NULL; (void)st +#define NO_STATE NULL #define DATE_TYPE(st) &PyDateTime_DateType #define DATETIME_TYPE(st) &PyDateTime_DateTimeType @@ -65,22 +65,22 @@ typedef struct { #define TIMEZONE_TYPE(st) &PyDateTime_TimeZoneType #define ISOCALENDAR_DATE_TYPE(st) st->isocalendar_date_type -#define PyDate_Check(st, op) PyObject_TypeCheck(op, DATE_TYPE(st)) -#define PyDate_CheckExact(st, op) Py_IS_TYPE(op, DATE_TYPE(st)) +#define PyDate_Check(op) PyObject_TypeCheck(op, DATE_TYPE(NO_STATE)) +#define PyDate_CheckExact(op) Py_IS_TYPE(op, DATE_TYPE(NO_STATE)) -#define PyDateTime_Check(st, op) PyObject_TypeCheck(op, DATETIME_TYPE(st)) -#define PyDateTime_CheckExact(st, op) Py_IS_TYPE(op, DATETIME_TYPE(st)) +#define PyDateTime_Check(op) PyObject_TypeCheck(op, DATETIME_TYPE(NO_STATE)) +#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, DATETIME_TYPE(NO_STATE)) -#define PyTime_Check(st, op) PyObject_TypeCheck(op, TIME_TYPE(st)) -#define PyTime_CheckExact(st, op) Py_IS_TYPE(op, TIME_TYPE(st)) +#define PyTime_Check(op) PyObject_TypeCheck(op, TIME_TYPE(NO_STATE)) +#define PyTime_CheckExact(op) Py_IS_TYPE(op, TIME_TYPE(NO_STATE)) -#define PyDelta_Check(st, op) PyObject_TypeCheck(op, DELTA_TYPE(st)) -#define PyDelta_CheckExact(st, op) Py_IS_TYPE(op, DELTA_TYPE(st)) +#define PyDelta_Check(op) PyObject_TypeCheck(op, DELTA_TYPE(NO_STATE)) +#define PyDelta_CheckExact(op) Py_IS_TYPE(op, DELTA_TYPE(NO_STATE)) -#define PyTZInfo_Check(st, op) PyObject_TypeCheck(op, TZINFO_TYPE(st)) -#define PyTZInfo_CheckExact(st, op) Py_IS_TYPE(op, TZINFO_TYPE(st)) +#define PyTZInfo_Check(op) PyObject_TypeCheck(op, TZINFO_TYPE(NO_STATE)) +#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, TZINFO_TYPE(NO_STATE)) -#define PyTimezone_Check(st, op) PyObject_TypeCheck(op, TIMEZONE_TYPE(st)) +#define PyTimezone_Check(op) PyObject_TypeCheck(op, TIMEZONE_TYPE(NO_STATE)) #define CONST_US_PER_MS(st) st->us_per_ms #define CONST_US_PER_SECOND(st) st->us_per_second @@ -1041,8 +1041,8 @@ new_date_ex(int year, int month, int day, PyTypeObject *type) return (PyObject *)self; } -#define new_date(st, year, month, day) \ - new_date_ex(year, month, day, DATE_TYPE(st)) +#define new_date(year, month, day) \ + new_date_ex(year, month, day, DATE_TYPE(NO_STATE)) // Forward declaration static PyObject * @@ -1053,12 +1053,11 @@ static PyObject * new_date_subclass_ex(int year, int month, int day, PyObject *cls) { PyObject *result; - datetime_state *st = GLOBAL_STATE; // We have "fast path" constructors for two subclasses: date and datetime - if ((PyTypeObject *)cls == DATE_TYPE(st)) { + if ((PyTypeObject *)cls == DATE_TYPE(NO_STATE)) { result = new_date_ex(year, month, day, (PyTypeObject *)cls); } - else if ((PyTypeObject *)cls == DATETIME_TYPE(st)) { + else if ((PyTypeObject *)cls == DATETIME_TYPE(NO_STATE)) { result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None, (PyTypeObject *)cls); } @@ -1111,8 +1110,8 @@ new_datetime_ex(int year, int month, int day, int hour, int minute, tzinfo, 0, type); } -#define new_datetime(st, y, m, d, hh, mm, ss, us, tzinfo, fold) \ - new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, DATETIME_TYPE(st)) +#define new_datetime(y, m, d, hh, mm, ss, us, tzinfo, fold) \ + new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, DATETIME_TYPE(NO_STATE)) static PyObject * call_subclass_fold(PyObject *cls, int fold, const char *format, ...) @@ -1154,10 +1153,9 @@ new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute int fold, PyObject *cls) { PyObject* dt; - datetime_state *st = GLOBAL_STATE; - if ((PyTypeObject*)cls == DATETIME_TYPE(st)) { + if ((PyTypeObject*)cls == DATETIME_TYPE(NO_STATE)) { // Use the fast path constructor - dt = new_datetime(st, year, month, day, hour, minute, second, usecond, + dt = new_datetime(year, month, day, hour, minute, second, usecond, tzinfo, fold); } else { @@ -1216,18 +1214,17 @@ new_time_ex(int hour, int minute, int second, int usecond, return new_time_ex2(hour, minute, second, usecond, tzinfo, 0, type); } -#define new_time(st, hh, mm, ss, us, tzinfo, fold) \ - new_time_ex2(hh, mm, ss, us, tzinfo, fold, TIME_TYPE(st)) +#define new_time(hh, mm, ss, us, tzinfo, fold) \ + new_time_ex2(hh, mm, ss, us, tzinfo, fold, TIME_TYPE(NO_STATE)) static PyObject * new_time_subclass_fold_ex(int hour, int minute, int second, int usecond, PyObject *tzinfo, int fold, PyObject *cls) { PyObject *t; - datetime_state *st = GLOBAL_STATE; - if ((PyTypeObject*)cls == TIME_TYPE(st)) { + if ((PyTypeObject*)cls == TIME_TYPE(NO_STATE)) { // Use the fast path constructor - t = new_time(st, hour, minute, second, usecond, tzinfo, fold); + t = new_time(hour, minute, second, usecond, tzinfo, fold); } else { // Subclass @@ -1276,8 +1273,8 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize, return (PyObject *) self; } -#define new_delta(st, d, s, us, normalize) \ - new_delta_ex(d, s, us, normalize, DELTA_TYPE(st)) +#define new_delta(d, s, us, normalize) \ + new_delta_ex(d, s, us, normalize, DELTA_TYPE(NO_STATE)) typedef struct @@ -1297,11 +1294,10 @@ static PyObject * create_timezone(PyObject *offset, PyObject *name) { PyDateTime_TimeZone *self; - datetime_state *st = GLOBAL_STATE; - PyTypeObject *type = TIMEZONE_TYPE(st); + PyTypeObject *type = TIMEZONE_TYPE(NO_STATE); assert(offset != NULL); - assert(PyDelta_Check(st, offset)); + assert(PyDelta_Check(offset)); assert(name == NULL || PyUnicode_Check(name)); self = look_up_timezone(offset, name); @@ -1326,12 +1322,11 @@ static PyObject * new_timezone(PyObject *offset, PyObject *name) { assert(offset != NULL); - datetime_state *st = GLOBAL_STATE; - assert(PyDelta_Check(st, offset)); + assert(PyDelta_Check(offset)); assert(name == NULL || PyUnicode_Check(name)); if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) { - return Py_NewRef(CONST_UTC(st)); + return Py_NewRef(CONST_UTC(NO_STATE)); } if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && @@ -1357,8 +1352,7 @@ new_timezone(PyObject *offset, PyObject *name) static int check_tzinfo_subclass(PyObject *p) { - datetime_state *st = GLOBAL_STATE; - if (p == Py_None || PyTZInfo_Check(st, p)) + if (p == Py_None || PyTZInfo_Check(p)) return 0; PyErr_Format(PyExc_TypeError, "tzinfo argument must be None or of a tzinfo subclass, " @@ -1376,9 +1370,9 @@ get_tzinfo_member(PyObject *self) { PyObject *tzinfo = NULL; - if (PyDateTime_Check(st, self) && HASTZINFO(self)) + if (PyDateTime_Check(self) && HASTZINFO(self)) tzinfo = ((PyDateTime_DateTime *)self)->tzinfo; - else if (PyTime_Check(st, self) && HASTZINFO(self)) + else if (PyTime_Check(self) && HASTZINFO(self)) tzinfo = ((PyDateTime_Time *)self)->tzinfo; return tzinfo; @@ -1397,7 +1391,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) PyObject *offset; assert(tzinfo != NULL); - assert(PyTZInfo_Check(st, tzinfo) || tzinfo == Py_None); + assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None); assert(tzinfoarg != NULL); if (tzinfo == Py_None) @@ -1405,8 +1399,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); if (offset == Py_None || offset == NULL) return offset; - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, offset)) { + if (PyDelta_Check(offset)) { if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && GET_TD_MICROSECONDS(offset) < 1) || @@ -1546,11 +1539,10 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) if (rv == 1) { // Create a timezone from offset in seconds (0 returns UTC) if (tzoffset == 0) { - datetime_state *st = GLOBAL_STATE; - return Py_NewRef(CONST_UTC(st)); + return Py_NewRef(CONST_UTC(NO_STATE)); } - PyObject *delta = new_delta(st, 0, tzoffset, tz_useconds, 1); + PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1); if (delta == NULL) { return NULL; } @@ -1715,9 +1707,9 @@ static PyObject * make_freplacement(PyObject *object) { char freplacement[64]; - if (PyTime_Check(st, object)) + if (PyTime_Check(object)) sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object)); - else if (PyDateTime_Check(st, object)) + else if (PyDateTime_Check(object)) sprintf(freplacement, "%06d", DATE_GET_MICROSECOND(object)); else sprintf(freplacement, "%06d", 0); @@ -2104,8 +2096,8 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) goto Done; } -#define microseconds_to_delta(st, pymicros) \ - microseconds_to_delta_ex(pymicros, DELTA_TYPE(st)) +#define microseconds_to_delta(pymicros) \ + microseconds_to_delta_ex(pymicros, DELTA_TYPE(NO_STATE)) static PyObject * multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) @@ -2123,8 +2115,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) if (pyus_out == NULL) return NULL; - datetime_state *st = GLOBAL_STATE; - result = microseconds_to_delta(st, pyus_out); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); return result; } @@ -2179,8 +2170,7 @@ multiply_truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *floatobj, Py_DECREF(temp); if (pyus_out == NULL) goto error; - datetime_state *st = GLOBAL_STATE; - result = microseconds_to_delta(st, pyus_out); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); error: Py_XDECREF(pyus_in); @@ -2205,8 +2195,7 @@ divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj) if (pyus_out == NULL) return NULL; - datetime_state *st = GLOBAL_STATE; - result = microseconds_to_delta(st, pyus_out); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); return result; } @@ -2269,8 +2258,7 @@ truedivide_timedelta_int(PyDateTime_Delta *delta, PyObject *i) Py_DECREF(pyus_in); if (pyus_out == NULL) return NULL; - datetime_state *st = GLOBAL_STATE; - result = microseconds_to_delta(st, pyus_out); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); return result; @@ -2281,8 +2269,7 @@ delta_add(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, left) && PyDelta_Check(st, right)) { + if (PyDelta_Check(left) && PyDelta_Check(right)) { /* delta + delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2291,7 +2278,7 @@ delta_add(PyObject *left, PyObject *right) int seconds = GET_TD_SECONDS(left) + GET_TD_SECONDS(right); int microseconds = GET_TD_MICROSECONDS(left) + GET_TD_MICROSECONDS(right); - result = new_delta(st, days, seconds, microseconds, 1); + result = new_delta(days, seconds, microseconds, 1); } if (result == Py_NotImplemented) @@ -2302,9 +2289,7 @@ delta_add(PyObject *left, PyObject *right) static PyObject * delta_negative(PyDateTime_Delta *self) { - datetime_state *st = GLOBAL_STATE; - return new_delta(st, - -GET_TD_DAYS(self), + return new_delta(-GET_TD_DAYS(self), -GET_TD_SECONDS(self), -GET_TD_MICROSECONDS(self), 1); @@ -2316,9 +2301,7 @@ delta_positive(PyDateTime_Delta *self) /* Could optimize this (by returning self) if this isn't a * subclass -- but who uses unary + ? Approximately nobody. */ - datetime_state *st = GLOBAL_STATE; - return new_delta(st, - GET_TD_DAYS(self), + return new_delta(GET_TD_DAYS(self), GET_TD_SECONDS(self), GET_TD_MICROSECONDS(self), 0); @@ -2345,8 +2328,7 @@ delta_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, left) && PyDelta_Check(st, right)) { + if (PyDelta_Check(left) && PyDelta_Check(right)) { /* delta - delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2355,7 +2337,7 @@ delta_subtract(PyObject *left, PyObject *right) int seconds = GET_TD_SECONDS(left) - GET_TD_SECONDS(right); int microseconds = GET_TD_MICROSECONDS(left) - GET_TD_MICROSECONDS(right); - result = new_delta(st, days, seconds, microseconds, 1); + result = new_delta(days, seconds, microseconds, 1); } if (result == Py_NotImplemented) @@ -2379,8 +2361,7 @@ delta_cmp(PyObject *self, PyObject *other) static PyObject * delta_richcompare(PyObject *self, PyObject *other, int op) { - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, other)) { + if (PyDelta_Check(other)) { int diff = delta_cmp(self, other); return diff_to_bool(diff, op); } @@ -2409,8 +2390,7 @@ delta_multiply(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, left)) { + if (PyDelta_Check(left)) { /* delta * ??? */ if (PyLong_Check(right)) result = multiply_int_timedelta(right, @@ -2436,14 +2416,13 @@ delta_divide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, left)) { + if (PyDelta_Check(left)) { /* delta * ??? */ if (PyLong_Check(right)) result = divide_timedelta_int( (PyDateTime_Delta *)left, right); - else if (PyDelta_Check(st, right)) + else if (PyDelta_Check(right)) result = divide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2459,9 +2438,8 @@ delta_truedivide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - datetime_state *st = GLOBAL_STATE; - if (PyDelta_Check(st, left)) { - if (PyDelta_Check(st, right)) + if (PyDelta_Check(left)) { + if (PyDelta_Check(right)) result = truedivide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2486,8 +2464,7 @@ delta_remainder(PyObject *left, PyObject *right) PyObject *pyus_remainder; PyObject *remainder; - datetime_state *st = GLOBAL_STATE; - if (!PyDelta_Check(st, left) || !PyDelta_Check(st, right)) + if (!PyDelta_Check(left) || !PyDelta_Check(right)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -2506,7 +2483,7 @@ delta_remainder(PyObject *left, PyObject *right) if (pyus_remainder == NULL) return NULL; - remainder = microseconds_to_delta(st, pyus_remainder); + remainder = microseconds_to_delta(pyus_remainder); Py_DECREF(pyus_remainder); if (remainder == NULL) return NULL; @@ -2523,8 +2500,7 @@ delta_divmod(PyObject *left, PyObject *right) PyObject *delta; PyObject *result; - datetime_state *st = GLOBAL_STATE; - if (!PyDelta_Check(st, left) || !PyDelta_Check(st, right)) + if (!PyDelta_Check(left) || !PyDelta_Check(right)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -2543,7 +2519,7 @@ delta_divmod(PyObject *left, PyObject *right) if (divmod == NULL) return NULL; - delta = microseconds_to_delta(st, PyTuple_GET_ITEM(divmod, 1)); + delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1)); if (delta == NULL) { Py_DECREF(divmod); return NULL; @@ -3316,14 +3292,12 @@ add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate) static PyObject * date_add(PyObject *left, PyObject *right) { - datetime_state *st = GLOBAL_STATE; - - if (PyDateTime_Check(st, left) || PyDateTime_Check(st, right)) + if (PyDateTime_Check(left) || PyDateTime_Check(right)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(st, left)) { + if (PyDate_Check(left)) { /* date + ??? */ - if (PyDelta_Check(st, right)) + if (PyDelta_Check(right)) /* date + delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3333,7 +3307,7 @@ date_add(PyObject *left, PyObject *right) /* ??? + date * 'right' must be one of us, or we wouldn't have been called */ - if (PyDelta_Check(st, left)) + if (PyDelta_Check(left)) /* delta + date */ return add_date_timedelta((PyDateTime_Date *) right, (PyDateTime_Delta *) left, @@ -3345,13 +3319,11 @@ date_add(PyObject *left, PyObject *right) static PyObject * date_subtract(PyObject *left, PyObject *right) { - datetime_state *st = GLOBAL_STATE; - - if (PyDateTime_Check(st, left) || PyDateTime_Check(st, right)) + if (PyDateTime_Check(left) || PyDateTime_Check(right)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(st, left)) { - if (PyDate_Check(st, right)) { + if (PyDate_Check(left)) { + if (PyDate_Check(right)) { /* date - date */ int left_ord = ymd_to_ord(GET_YEAR(left), GET_MONTH(left), @@ -3359,9 +3331,9 @@ date_subtract(PyObject *left, PyObject *right) int right_ord = ymd_to_ord(GET_YEAR(right), GET_MONTH(right), GET_DAY(right)); - return new_delta(st, left_ord - right_ord, 0, 0, 0); + return new_delta(left_ord - right_ord, 0, 0, 0); } - if (PyDelta_Check(st, right)) { + if (PyDelta_Check(right)) { /* date - delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3645,7 +3617,7 @@ date_richcompare(PyObject *self, PyObject *other, int op) * The behavior is the same as if Date and DateTime were independent * classes. */ - if (PyDate_Check(st, other) && !PyDateTime_Check(st, other)) { + if (PyDate_Check(other) && !PyDateTime_Check(other)) { int diff = memcmp(((PyDateTime_Date *)self)->data, ((PyDateTime_Date *)other)->data, _PyDateTime_DATE_DATASIZE); @@ -3921,7 +3893,7 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt) PyObject *off = NULL, *dst = NULL; PyDateTime_Delta *delta = NULL; - if (!PyDateTime_Check(st, dt)) { + if (!PyDateTime_Check(dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -4094,9 +4066,8 @@ timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *offset; PyObject *name = NULL; - datetime_state *st = GLOBAL_STATE; if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws, - DELTA_TYPE(st), &offset, &name)) + DELTA_TYPE(NO_STATE), &offset, &name)) return new_timezone(offset, name); return NULL; @@ -4116,7 +4087,7 @@ timezone_richcompare(PyDateTime_TimeZone *self, { if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED; - if (!PyTimezone_Check(st, other)) { + if (!PyTimezone_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } return delta_richcompare(self->offset, other->offset, op); @@ -4135,7 +4106,7 @@ timezone_hash(PyDateTime_TimeZone *self) static int _timezone_check_argument(PyObject *dt, const char *meth) { - if (dt == Py_None || PyDateTime_Check(st, dt)) + if (dt == Py_None || PyDateTime_Check(dt)) return 0; PyErr_Format(PyExc_TypeError, "%s(dt) argument must be a datetime instance" " or None, not %.200s", meth, Py_TYPE(dt)->tp_name); @@ -4149,8 +4120,7 @@ timezone_repr(PyDateTime_TimeZone *self) to use Py_TYPE(self)->tp_name here. */ const char *type_name = Py_TYPE(self)->tp_name; - datetime_state *st = GLOBAL_STATE; - if ((PyObject *)self == CONST_UTC(st)) { + if ((PyObject *)self == CONST_UTC(NO_STATE)) { return PyUnicode_FromFormat("%s.utc", type_name); } @@ -4172,8 +4142,7 @@ timezone_str(PyDateTime_TimeZone *self) if (self->name != NULL) { return Py_NewRef(self->name); } - datetime_state *st = GLOBAL_STATE; - if ((PyObject *)self == CONST_UTC(st) || + if ((PyObject *)self == CONST_UTC(NO_STATE) || (GET_TD_DAYS(self->offset) == 0 && GET_TD_SECONDS(self->offset) == 0 && GET_TD_MICROSECONDS(self->offset) == 0)) @@ -4239,7 +4208,7 @@ timezone_dst(PyObject *self, PyObject *dt) static PyObject * timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) { - if (!PyDateTime_Check(st, dt)) { + if (!PyDateTime_Check(dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -4336,8 +4305,7 @@ static PyDateTime_TimeZone * look_up_timezone(PyObject *offset, PyObject *name) { if (offset == utc_timezone.offset && name == NULL) { - datetime_state *st = GLOBAL_STATE; - return (PyDateTime_TimeZone *)CONST_UTC(st); + return (PyDateTime_TimeZone *)CONST_UTC(NO_STATE); } return NULL; } @@ -4669,7 +4637,7 @@ time_richcompare(PyObject *self, PyObject *other, int op) PyObject *offset1, *offset2; int diff; - if (! PyTime_Check(st, other)) + if (! PyTime_Check(other)) Py_RETURN_NOTIMPLEMENTED; if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) { @@ -4688,9 +4656,8 @@ time_richcompare(PyObject *self, PyObject *other, int op) * we get off cheap. Note that if they're both naive, offset1 == * offset2 == Py_None at this point. */ - datetime_state *st = GLOBAL_STATE; if ((offset1 == offset2) || - (PyDelta_Check(st, offset1) && PyDelta_Check(st, offset2) && + (PyDelta_Check(offset1) && PyDelta_Check(offset2) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_Time *)self)->data, ((PyDateTime_Time *)other)->data, @@ -4770,7 +4737,7 @@ time_hash(PyDateTime_Time *self) TIME_GET_MINUTE(self) * 60 + TIME_GET_SECOND(self); microseconds = TIME_GET_MICROSECOND(self); - temp1 = new_delta(st, 0, seconds, microseconds, 1); + temp1 = new_delta(0, seconds, microseconds, 1); if (temp1 == NULL) { Py_DECREF(offset); return -1; @@ -4855,9 +4822,8 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } PyObject *t; - datetime_state *st = GLOBAL_STATE; - if ( (PyTypeObject *)cls == TIME_TYPE(st)) { - t = new_time(st, hour, minute, second, microsecond, tzinfo, 0); + if ( (PyTypeObject *)cls == TIME_TYPE(NO_STATE)) { + t = new_time(hour, minute, second, microsecond, tzinfo, 0); } else { t = PyObject_CallFunction(cls, "iiiiO", hour, minute, second, microsecond, tzinfo); @@ -5454,10 +5420,9 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) PyObject *tzinfo = NULL; PyObject *result = NULL; - datetime_state *st = GLOBAL_STATE; if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords, - DATE_TYPE(st), &date, - TIME_TYPE(st), &time, &tzinfo)) { + DATE_TYPE(NO_STATE), &date, + TIME_TYPE(NO_STATE), &time, &tzinfo)) { if (tzinfo == NULL) { if (HASTZINFO(time)) tzinfo = ((PyDateTime_Time *)time)->tzinfo; @@ -5800,17 +5765,16 @@ add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta, static PyObject * datetime_add(PyObject *left, PyObject *right) { - datetime_state *st = GLOBAL_STATE; - if (PyDateTime_Check(st, left)) { + if (PyDateTime_Check(left)) { /* datetime + ??? */ - if (PyDelta_Check(st, right)) + if (PyDelta_Check(right)) /* datetime + delta */ return add_datetime_timedelta( (PyDateTime_DateTime *)left, (PyDateTime_Delta *)right, 1); } - else if (PyDelta_Check(st, left)) { + else if (PyDelta_Check(left)) { /* delta + datetime */ return add_datetime_timedelta((PyDateTime_DateTime *) right, (PyDateTime_Delta *) left, @@ -5824,10 +5788,9 @@ datetime_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - datetime_state *st = GLOBAL_STATE; - if (PyDateTime_Check(st, left)) { + if (PyDateTime_Check(left)) { /* datetime - ??? */ - if (PyDateTime_Check(st, right)) { + if (PyDateTime_Check(right)) { /* datetime - datetime */ PyObject *offset1, *offset2, *offdiff = NULL; int delta_d, delta_s, delta_us; @@ -5883,7 +5846,7 @@ datetime_subtract(PyObject *left, PyObject *right) DATE_GET_SECOND(right)); delta_us = DATE_GET_MICROSECOND(left) - DATE_GET_MICROSECOND(right); - result = new_delta(st, delta_d, delta_s, delta_us, 1); + result = new_delta(delta_d, delta_s, delta_us, 1); if (result == NULL) return NULL; @@ -5892,7 +5855,7 @@ datetime_subtract(PyObject *left, PyObject *right) Py_DECREF(offdiff); } } - else if (PyDelta_Check(st, right)) { + else if (PyDelta_Check(right)) { /* datetime - delta */ result = add_datetime_timedelta( (PyDateTime_DateTime *)left, @@ -6107,7 +6070,7 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) PyObject *offset1, *offset2; int diff; - if (!PyDateTime_Check(st, other)) { + if (!PyDateTime_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } @@ -6127,9 +6090,8 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) * we get off cheap. Note that if they're both naive, offset1 == * offset2 == Py_None at this point. */ - datetime_state *st = GLOBAL_STATE; if ((offset1 == offset2) || - (PyDelta_Check(st, offset1) && PyDelta_Check(st, offset2) && + (PyDelta_Check(offset1) && PyDelta_Check(offset2) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_DateTime *)self)->data, ((PyDateTime_DateTime *)other)->data, @@ -6224,8 +6186,7 @@ datetime_hash(PyDateTime_DateTime *self) seconds = DATE_GET_HOUR(self) * 3600 + DATE_GET_MINUTE(self) * 60 + DATE_GET_SECOND(self); - datetime_state *st = GLOBAL_STATE; - temp1 = new_delta(st, days, seconds, + temp1 = new_delta(days, seconds, DATE_GET_MICROSECOND(self), 1); if (temp1 == NULL) { @@ -6288,18 +6249,15 @@ local_timezone_from_timestamp(time_t timestamp) return NULL; #ifdef HAVE_STRUCT_TM_TM_ZONE zone = local_time_tm.tm_zone; - datetime_state *st = GLOBAL_STATE; - delta = new_delta(st, 0, local_time_tm.tm_gmtoff, 0, 1); + delta = new_delta(0, local_time_tm.tm_gmtoff, 0, 1); #else /* HAVE_STRUCT_TM_TM_ZONE */ { - datetime_state *st = get_datetime_state(); PyObject *local_time, *utc_time; struct tm utc_time_tm; char buf[100]; strftime(buf, sizeof(buf), "%Z", &local_time_tm); zone = buf; - local_time = new_datetime(st, - local_time_tm.tm_year + 1900, + local_time = new_datetime(local_time_tm.tm_year + 1900, local_time_tm.tm_mon + 1, local_time_tm.tm_mday, local_time_tm.tm_hour, @@ -6310,8 +6268,7 @@ local_timezone_from_timestamp(time_t timestamp) } if (_PyTime_gmtime(timestamp, &utc_time_tm) != 0) return NULL; - utc_time = new_datetime(st, - utc_time_tm.tm_year + 1900, + utc_time = new_datetime(utc_time_tm.tm_year + 1900, utc_time_tm.tm_mon + 1, utc_time_tm.tm_mday, utc_time_tm.tm_hour, @@ -6354,7 +6311,7 @@ local_timezone(PyDateTime_DateTime *utc_time) if (delta == NULL) return NULL; - one_second = new_delta(st, 0, 1, 0, 0); + one_second = new_delta(0, 1, 0, 0); if (one_second == NULL) { Py_DECREF(delta); return NULL; @@ -6444,14 +6401,13 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) /* Convert self to UTC. */ offset = call_utcoffset(self_tzinfo, (PyObject *)self); Py_DECREF(self_tzinfo); - datetime_state *st = GLOBAL_STATE; if (offset == NULL) return NULL; else if(offset == Py_None) { Py_DECREF(offset); goto naive; } - else if (!PyDelta_Check(st, offset)) { + else if (!PyDelta_Check(offset)) { Py_DECREF(offset); PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s," " expected timedelta or None", Py_TYPE(offset)->tp_name); @@ -6475,7 +6431,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) DATE_GET_MINUTE(result), DATE_GET_SECOND(result), DATE_GET_MICROSECOND(result), - CONST_UTC(st), + CONST_UTC(NO_STATE), DATE_GET_FOLD(result), Py_TYPE(result)); Py_DECREF(temp); @@ -6484,7 +6440,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) } else { /* Result is already aware - just replace tzinfo. */ - Py_SETREF(result->tzinfo, Py_NewRef(CONST_UTC(st))); + Py_SETREF(result->tzinfo, Py_NewRef(CONST_UTC(NO_STATE))); } /* Attach new tzinfo and let fromutc() do the rest. */ @@ -6616,9 +6572,7 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { - datetime_state *st = GLOBAL_STATE; - return new_date(st, - GET_YEAR(self), + return new_date(GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); } @@ -6626,9 +6580,7 @@ datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { - datetime_state *st = GLOBAL_STATE; - return new_time(st, - DATE_GET_HOUR(self), + return new_time(DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), DATE_GET_MICROSECOND(self), @@ -6639,9 +6591,7 @@ datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_gettimetz(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { - datetime_state *st = GLOBAL_STATE; - return new_time(st, - DATE_GET_HOUR(self), + return new_time(DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), DATE_GET_MICROSECOND(self), @@ -6933,8 +6883,7 @@ get_datetime_capi(void) static PyObject * create_timezone_from_delta(int days, int sec, int ms, int normalize) { - datetime_state *st = GLOBAL_STATE; - PyObject *delta = new_delta(st, days, sec, ms, normalize); + PyObject *delta = new_delta(days, sec, ms, normalize); if (delta == NULL) { return NULL; } @@ -7021,7 +6970,7 @@ init_state(datetime_state *st, PyObject *module) /* Init Unix epoch */ st->epoch = new_datetime( - st, 1970, 1, 1, 0, 0, 0, 0, (PyObject *)&utc_timezone, 0); + 1970, 1, 1, 0, 0, 0, 0, (PyObject *)&utc_timezone, 0); if (st->epoch == NULL) { return -1; } @@ -7138,31 +7087,30 @@ _datetime_exec(PyObject *module) /* timedelta values */ PyObject *d = PyDateTime_DeltaType.tp_dict; - DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 0, 0, 1, 0)); - DATETIME_ADD_MACRO(d, "min", new_delta(st_global, -MAX_DELTA_DAYS, 0, 0, 0)); - DATETIME_ADD_MACRO( - d, "max", new_delta(st_global, MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0)); + DATETIME_ADD_MACRO(d, "max", + new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); /* date values */ d = PyDateTime_DateType.tp_dict; - DATETIME_ADD_MACRO(d, "min", new_date(st_global, 1, 1, 1)); - DATETIME_ADD_MACRO(d, "max", new_date(st_global, MAXYEAR, 12, 31)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 1, 0, 0, 0)); + DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1)); + DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0)); /* time values */ d = PyDateTime_TimeType.tp_dict; - DATETIME_ADD_MACRO(d, "min", new_time(st_global, 0, 0, 0, 0, Py_None, 0)); - DATETIME_ADD_MACRO(d, "max", new_time(st_global, 23, 59, 59, 999999, Py_None, 0)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0)); + DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* datetime values */ d = PyDateTime_DateTimeType.tp_dict; - DATETIME_ADD_MACRO( - d, "min", new_datetime(st_global, 1, 1, 1, 0, 0, 0, 0, Py_None, 0)); - DATETIME_ADD_MACRO( - d, "max", new_datetime( - st_global, MAXYEAR, 12, 31, 23, 59, 59, 999999, Py_None, 0)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(st_global, 0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "min", + new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None, 0)); + DATETIME_ADD_MACRO(d, "max", new_datetime(MAXYEAR, 12, 31, 23, 59, 59, + 999999, Py_None, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* timezone values */ d = PyDateTime_TimeZoneType.tp_dict; From d44d6e90609466196a4d4879d0bc7399ac24ec60 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 17:02:18 -0600 Subject: [PATCH 10/38] Drop _datetime_global_state. --- Modules/_datetimemodule.c | 57 +++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 30d56c8e4c673d..0d718534970720 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -92,13 +92,6 @@ typedef struct { #define CONST_EPOCH(st) st->epoch #define CONST_UTC(st) ((PyObject *)&utc_timezone) -static datetime_state _datetime_global_state = {0}; - -static inline datetime_state* get_datetime_state(void) -{ - return &_datetime_global_state; -} - static datetime_state * get_module_state(PyObject *module) { @@ -124,6 +117,18 @@ get_current_module(PyInterpreterState *interp) return mod; } +#define GET_CURRENT_STATE(ST_VAR) \ + NULL; \ + PyObject *current_mod = NULL; \ + do { \ + PyInterpreterState *interp = PyInterpreterState_Get(); \ + current_mod = get_current_module(interp); \ + assert(current_mod != NULL); \ + ST_VAR = get_module_state(current_mod); \ + } while (0) +#define RELEASE_CURRENT_STATE(ST_VAR) \ + Py_DECREF(current_mod) + static int set_current_module(PyInterpreterState *interp, PyObject *mod) { @@ -1968,10 +1973,11 @@ delta_to_microseconds(PyDateTime_Delta *self) PyObject *x3 = NULL; PyObject *result = NULL; + datetime_state *st = GET_CURRENT_STATE(st); + x1 = PyLong_FromLong(GET_TD_DAYS(self)); if (x1 == NULL) goto Done; - datetime_state *st = get_datetime_state(); x2 = PyNumber_Multiply(x1, CONST_SEC_PER_DAY(st)); /* days in seconds */ if (x2 == NULL) goto Done; @@ -2005,6 +2011,7 @@ delta_to_microseconds(PyDateTime_Delta *self) Py_XDECREF(x1); Py_XDECREF(x2); Py_XDECREF(x3); + RELEASE_CURRENT_STATE(st); return result; } @@ -2044,7 +2051,8 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *num = NULL; PyObject *result = NULL; - datetime_state *st = get_datetime_state(); + datetime_state *st = GET_CURRENT_STATE(st); + tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st)); if (tuple == NULL) { goto Done; @@ -2088,6 +2096,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) Done: Py_XDECREF(tuple); Py_XDECREF(num); + RELEASE_CURRENT_STATE(st); return result; BadDivmod: @@ -2625,6 +2634,8 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; + datetime_state *st = GET_CURRENT_STATE(st); + /* Argument objects. */ PyObject *day = NULL; PyObject *second = NULL; @@ -2663,7 +2674,6 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) y = accum("microseconds", x, us, _PyLong_GetOne(), &leftover_us); CLEANUP; } - datetime_state *st = get_datetime_state(); if (ms) { y = accum("milliseconds", x, ms, CONST_US_PER_MS(st), &leftover_us); CLEANUP; @@ -2727,7 +2737,9 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) self = microseconds_to_delta_ex(x, type); Py_DECREF(x); + Done: + RELEASE_CURRENT_STATE(st); return self; #undef CLEANUP @@ -2840,9 +2852,11 @@ delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored)) if (total_microseconds == NULL) return NULL; - datetime_state *st = get_datetime_state(); + datetime_state *st = GET_CURRENT_STATE(st); + total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st)); + RELEASE_CURRENT_STATE(st); Py_DECREF(total_microseconds); return total_seconds; } @@ -3595,9 +3609,11 @@ date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) week = 0; } - datetime_state *st = get_datetime_state(); + datetime_state *st = GET_CURRENT_STATE(st); + PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st), year, week + 1, day + 1); + RELEASE_CURRENT_STATE(st); if (v == NULL) { return NULL; } @@ -6306,8 +6322,10 @@ local_timezone(PyDateTime_DateTime *utc_time) PyObject *one_second; PyObject *seconds; - datetime_state *st = get_datetime_state(); + datetime_state *st = GET_CURRENT_STATE(st); + delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st)); + RELEASE_CURRENT_STATE(st); if (delta == NULL) return NULL; @@ -6544,9 +6562,11 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) PyObject *result; if (HASTZINFO(self) && self->tzinfo != Py_None) { - datetime_state *st = get_datetime_state(); + datetime_state *st = GET_CURRENT_STATE(st); + PyObject *delta; delta = datetime_subtract((PyObject *)self, CONST_EPOCH(st)); + RELEASE_CURRENT_STATE(st); if (delta == NULL) return NULL; result = delta_total_seconds(delta, NULL); @@ -7024,7 +7044,6 @@ _datetime_exec(PyObject *module) { int rc = -1; datetime_state *st = get_module_state(module); - datetime_state *st_global = get_datetime_state(); PyInterpreterState *interp = PyInterpreterState_Get(); PyObject *old_module = get_current_module(interp); @@ -7056,10 +7075,6 @@ _datetime_exec(PyObject *module) } } - if (init_state(st_global, module) < 0) { - goto error; - } - if (old_module != NULL) { datetime_state *st_old = get_module_state(old_module); if (copy_state(st, st_old) < 0) { @@ -7179,7 +7194,6 @@ _datetime_exec(PyObject *module) error: clear_state(st); - clear_state(st_global); finally: Py_XDECREF(old_module); @@ -7199,7 +7213,6 @@ module_traverse(PyObject *mod, visitproc visit, void *arg) { datetime_state *st = get_module_state(mod); traverse_state(st, visit, arg); - traverse_state(get_datetime_state(), visit, arg); return 0; } @@ -7208,7 +7221,6 @@ module_clear(PyObject *mod) { datetime_state *st = get_module_state(mod); clear_state(st); - clear_state(get_datetime_state()); return 0; } @@ -7217,7 +7229,6 @@ module_free(void *mod) { datetime_state *st = get_module_state(mod); clear_state(st); - clear_state(get_datetime_state()); } static PyModuleDef datetimemodule = { From a3094748d62621409cdff8adb2f5bbc372500ad7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 17:30:52 -0600 Subject: [PATCH 11/38] Drop datetime_state.initialized. --- Modules/_datetimemodule.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0d718534970720..469c5a6cb5a4d6 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -50,9 +50,6 @@ typedef struct { /* The interned Unix epoch datetime instance */ PyObject *epoch; - - /* While we use a global state, we ensure it's only initialized once */ - int initialized; } datetime_state; #define NO_STATE NULL @@ -6933,14 +6930,6 @@ copy_state(datetime_state *st, datetime_state *from) static int init_state(datetime_state *st, PyObject *module) { - // While datetime uses global module "state", we unly initialize it once. - // The PyLong objects created here (once per process) are not decref'd. - if (st->initialized) { - assert(st->initialized > 0); - st->initialized += 1; - return 0; - } - /* Per-module heap types. */ #define ADD_TYPE(FIELD, SPEC, BASE) \ do { \ @@ -6995,20 +6984,12 @@ init_state(datetime_state *st, PyObject *module) return -1; } - st->initialized = 1; - return 0; } static int traverse_state(datetime_state *st, visitproc visit, void *arg) { - if (!st->initialized) { - return 0; - } - if (st->initialized > 1) { - return 0; - } /* heap types */ Py_VISIT(st->isocalendar_date_type); @@ -7018,15 +6999,6 @@ traverse_state(datetime_state *st, visitproc visit, void *arg) static int clear_state(datetime_state *st) { - assert(st->initialized >= 0); - if (!st->initialized) { - return 0; - } - st->initialized -= 1; - if (st->initialized) { - return 0; - } - Py_CLEAR(st->isocalendar_date_type); Py_CLEAR(st->us_per_ms); Py_CLEAR(st->us_per_second); From ede4415c638334fa64b3b695c5f08a431f274b4c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 29 May 2024 20:54:41 -0600 Subject: [PATCH 12/38] Fix refleaks. --- Modules/_datetimemodule.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 469c5a6cb5a4d6..6d5e6cf4e048ff 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7059,8 +7059,12 @@ _datetime_exec(PyObject *module) } } + /* For now we only set the objects on the static types once. + * We will relax that once each types __dict__ is per-interpreter. */ #define DATETIME_ADD_MACRO(dict, c, value_expr) \ do { \ + if (PyDict_GetItemString(dict, c) == NULL) { \ + assert(!PyErr_Occurred()); \ PyObject *value = (value_expr); \ if (value == NULL) { \ goto error; \ @@ -7070,6 +7074,7 @@ _datetime_exec(PyObject *module) goto error; \ } \ Py_DECREF(value); \ + } \ } while(0) /* timedelta values */ @@ -7117,6 +7122,8 @@ _datetime_exec(PyObject *module) PyObject *max = create_timezone_from_delta(0, (23 * 60 + 59) * 60, 0, 0); DATETIME_ADD_MACRO(d, "max", max); +#undef DATETIME_ADD_MACRO + /* Add module level attributes */ if (PyModule_AddIntMacro(module, MINYEAR) < 0) { goto error; @@ -7171,7 +7178,6 @@ _datetime_exec(PyObject *module) Py_XDECREF(old_module); return rc; } -#undef DATETIME_ADD_MACRO static PyModuleDef_Slot module_slots[] = { {Py_mod_exec, _datetime_exec}, From 5a8b1aaede5eab1b9e443779b60e057441538812 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 30 May 2024 12:40:04 -0600 Subject: [PATCH 13/38] Clear the "current" module when finalizing the module. --- Modules/_datetimemodule.c | 44 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 6d5e6cf4e048ff..fcc3a581c7b2ab 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -136,6 +136,41 @@ set_current_module(PyInterpreterState *interp, PyObject *mod) return PyDict_SetItemString(dict, INTERP_KEY, mod); } +static void +clear_current_module(PyInterpreterState *interp, PyObject *expected) +{ + PyObject *exc = PyErr_GetRaisedException(); + + PyObject *dict = PyInterpreterState_GetDict(interp); + if (dict == NULL) { + goto error; + } + + if (expected != NULL) { + PyObject *current = NULL; + if (PyDict_GetItemStringRef(dict, INTERP_KEY, ¤t) < 0) { + goto error; + } + if (current != expected) { + goto finally; + } + } + + if (PyDict_DelItemString(dict, INTERP_KEY) < 0) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + goto error; + } + } + + goto finally; + +error: + PyErr_Print(); + +finally: + PyErr_SetRaisedException(exc); +} + /* 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 @@ -7199,14 +7234,21 @@ module_clear(PyObject *mod) { datetime_state *st = get_module_state(mod); clear_state(st); + + PyInterpreterState *interp = PyInterpreterState_Get(); + clear_current_module(interp, mod); + return 0; } static void module_free(void *mod) { - datetime_state *st = get_module_state(mod); + datetime_state *st = get_module_state((PyObject *)mod); clear_state(st); + + PyInterpreterState *interp = PyInterpreterState_Get(); + clear_current_module(interp, (PyObject *)mod); } static PyModuleDef datetimemodule = { From 3de1cd36ef864d15e42fb1feadde123cc6fcb0ae Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 30 May 2024 14:36:09 -0600 Subject: [PATCH 14/38] Use a weakref when tracking the "current" module. --- Modules/_datetimemodule.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index fcc3a581c7b2ab..a15b8d3be5df35 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -107,10 +107,16 @@ get_current_module(PyInterpreterState *interp) if (dict == NULL) { return NULL; } - PyObject *mod = NULL; - if (PyDict_GetItemStringRef(dict, INTERP_KEY, &mod) < 0) { + PyObject *ref = NULL; + if (PyDict_GetItemStringRef(dict, INTERP_KEY, &ref) < 0) { + return NULL; + } + if (ref == NULL) { return NULL; } + PyObject *mod = NULL; + (void)PyWeakref_GetRef(ref, &mod); + Py_DECREF(ref); return mod; } @@ -129,11 +135,18 @@ get_current_module(PyInterpreterState *interp) static int set_current_module(PyInterpreterState *interp, PyObject *mod) { + assert(mod != NULL); PyObject *dict = PyInterpreterState_GetDict(interp); if (dict == NULL) { return -1; } - return PyDict_SetItemString(dict, INTERP_KEY, mod); + PyObject *ref = PyWeakref_NewRef(mod, NULL); + if (ref == NULL) { + return -1; + } + int rc = PyDict_SetItemString(dict, INTERP_KEY, ref); + Py_DECREF(ref); + return rc; } static void @@ -141,14 +154,21 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) { PyObject *exc = PyErr_GetRaisedException(); + PyObject *current = NULL; + PyObject *dict = PyInterpreterState_GetDict(interp); if (dict == NULL) { goto error; } if (expected != NULL) { - PyObject *current = NULL; - if (PyDict_GetItemStringRef(dict, INTERP_KEY, ¤t) < 0) { + PyObject *ref = NULL; + if (PyDict_GetItemStringRef(dict, INTERP_KEY, &ref) < 0) { + goto error; + } + int rc = PyWeakref_GetRef(ref, ¤t); + Py_DECREF(ref); + if (rc < 0) { goto error; } if (current != expected) { @@ -168,6 +188,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) PyErr_Print(); finally: + Py_XDECREF(current); PyErr_SetRaisedException(exc); } From 62b3d5e1fc565dc869fd92d72a7a309ebcae843e Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 30 May 2024 16:31:04 -0600 Subject: [PATCH 15/38] Fix clear_current_module(). --- Modules/_datetimemodule.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index a15b8d3be5df35..0b969ffaac07a9 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -166,13 +166,15 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) if (PyDict_GetItemStringRef(dict, INTERP_KEY, &ref) < 0) { goto error; } - int rc = PyWeakref_GetRef(ref, ¤t); - Py_DECREF(ref); - if (rc < 0) { - goto error; - } - if (current != expected) { - goto finally; + if (ref != NULL) { + int rc = PyWeakref_GetRef(ref, ¤t); + Py_DECREF(ref); + if (rc < 0) { + goto error; + } + if (current != expected) { + goto finally; + } } } From 5c25927d83c97505ed1cc94433261bfa78bb6426 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 11:04:49 -0600 Subject: [PATCH 16/38] Give each module its own heap types. --- Modules/_datetimemodule.c | 58 +++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0b969ffaac07a9..af6f03242ab977 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6968,27 +6968,8 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize) } static int -copy_state(datetime_state *st, datetime_state *from) -{ - *st = (datetime_state){ - .isocalendar_date_type - = (PyTypeObject *)Py_NewRef(from->isocalendar_date_type), - .us_per_ms = Py_NewRef(from->us_per_ms), - .us_per_second = Py_NewRef(from->us_per_second), - .us_per_minute = Py_NewRef(from->us_per_minute), - .us_per_hour = Py_NewRef(from->us_per_hour), - .us_per_day = Py_NewRef(from->us_per_day), - .us_per_week = Py_NewRef(from->us_per_week), - .seconds_per_day = Py_NewRef(from->seconds_per_day), - .epoch = Py_NewRef(from->epoch), - }; - return 0; -} - -static int -init_state(datetime_state *st, PyObject *module) +init_heap_types(datetime_state *st, PyObject *module) { - /* Per-module heap types. */ #define ADD_TYPE(FIELD, SPEC, BASE) \ do { \ PyObject *cls = PyType_FromModuleAndSpec( \ @@ -7002,6 +6983,15 @@ init_state(datetime_state *st, PyObject *module) ADD_TYPE(isocalendar_date_type, &isocal_spec, &PyTuple_Type); #undef ADD_TYPE + return 0; +} + +static int +init_state(datetime_state *st) +{ + /* st->isocalendar_date_type was set via init_heap_types(). */ + assert(st->isocalendar_date_type != NULL); + st->us_per_ms = PyLong_FromLong(1000); if (st->us_per_ms == NULL) { return -1; @@ -7045,6 +7035,28 @@ init_state(datetime_state *st, PyObject *module) return 0; } +static int +copy_state(datetime_state *st, datetime_state *from) +{ + /* isocalendar_date_type is set via init_heap_types(). */ + assert(st->isocalendar_date_type != NULL); + assert(st->isocalendar_date_type != from->isocalendar_date_type); + + *st = (datetime_state){ + .isocalendar_date_type = st->isocalendar_date_type, + .us_per_ms = Py_NewRef(from->us_per_ms), + .us_per_second = Py_NewRef(from->us_per_second), + .us_per_minute = Py_NewRef(from->us_per_minute), + .us_per_hour = Py_NewRef(from->us_per_hour), + .us_per_day = Py_NewRef(from->us_per_day), + .us_per_week = Py_NewRef(from->us_per_week), + .seconds_per_day = Py_NewRef(from->seconds_per_day), + .epoch = Py_NewRef(from->epoch), + }; + + return 0; +} + static int traverse_state(datetime_state *st, visitproc visit, void *arg) { @@ -7105,6 +7117,10 @@ _datetime_exec(PyObject *module) } } + if (init_heap_types(st, module) < 0) { + goto error; + } + if (old_module != NULL) { datetime_state *st_old = get_module_state(old_module); if (copy_state(st, st_old) < 0) { @@ -7112,7 +7128,7 @@ _datetime_exec(PyObject *module) } } else { - if (init_state(st, module) < 0) { + if (init_state(st) < 0) { goto error; } } From da246742846287aa7f0517c637d6468af3e9de80 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 11:13:39 -0600 Subject: [PATCH 17/38] Consolidate into init_state(). --- Modules/_datetimemodule.c | 63 ++++++++++++--------------------------- 1 file changed, 19 insertions(+), 44 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index af6f03242ab977..4619345c8e857c 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6968,8 +6968,9 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize) } static int -init_heap_types(datetime_state *st, PyObject *module) +init_state(datetime_state *st, PyObject *module, PyObject *old_module) { + /* Each module gets its own heap types. */ #define ADD_TYPE(FIELD, SPEC, BASE) \ do { \ PyObject *cls = PyType_FromModuleAndSpec( \ @@ -6983,14 +6984,22 @@ init_heap_types(datetime_state *st, PyObject *module) ADD_TYPE(isocalendar_date_type, &isocal_spec, &PyTuple_Type); #undef ADD_TYPE - return 0; -} - -static int -init_state(datetime_state *st) -{ - /* st->isocalendar_date_type was set via init_heap_types(). */ - assert(st->isocalendar_date_type != NULL); + if (old_module != NULL) { + assert(old_module != module); + datetime_state *st_old = get_module_state(old_module); + *st = (datetime_state){ + .isocalendar_date_type = st->isocalendar_date_type, + .us_per_ms = Py_NewRef(st_old->us_per_ms), + .us_per_second = Py_NewRef(st_old->us_per_second), + .us_per_minute = Py_NewRef(st_old->us_per_minute), + .us_per_hour = Py_NewRef(st_old->us_per_hour), + .us_per_day = Py_NewRef(st_old->us_per_day), + .us_per_week = Py_NewRef(st_old->us_per_week), + .seconds_per_day = Py_NewRef(st_old->seconds_per_day), + .epoch = Py_NewRef(st_old->epoch), + }; + return 0; + } st->us_per_ms = PyLong_FromLong(1000); if (st->us_per_ms == NULL) { @@ -7035,28 +7044,6 @@ init_state(datetime_state *st) return 0; } -static int -copy_state(datetime_state *st, datetime_state *from) -{ - /* isocalendar_date_type is set via init_heap_types(). */ - assert(st->isocalendar_date_type != NULL); - assert(st->isocalendar_date_type != from->isocalendar_date_type); - - *st = (datetime_state){ - .isocalendar_date_type = st->isocalendar_date_type, - .us_per_ms = Py_NewRef(from->us_per_ms), - .us_per_second = Py_NewRef(from->us_per_second), - .us_per_minute = Py_NewRef(from->us_per_minute), - .us_per_hour = Py_NewRef(from->us_per_hour), - .us_per_day = Py_NewRef(from->us_per_day), - .us_per_week = Py_NewRef(from->us_per_week), - .seconds_per_day = Py_NewRef(from->seconds_per_day), - .epoch = Py_NewRef(from->epoch), - }; - - return 0; -} - static int traverse_state(datetime_state *st, visitproc visit, void *arg) { @@ -7117,22 +7104,10 @@ _datetime_exec(PyObject *module) } } - if (init_heap_types(st, module) < 0) { + if (init_state(st, module, old_module) < 0) { goto error; } - if (old_module != NULL) { - datetime_state *st_old = get_module_state(old_module); - if (copy_state(st, st_old) < 0) { - goto error; - } - } - else { - if (init_state(st) < 0) { - goto error; - } - } - /* For now we only set the objects on the static types once. * We will relax that once each types __dict__ is per-interpreter. */ #define DATETIME_ADD_MACRO(dict, c, value_expr) \ From f8420ea43415ea007c972a7ef754eadb9546ebf0 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 11:21:25 -0600 Subject: [PATCH 18/38] Use _Py_ID() for INTERP_KEY. --- .../internal/pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + Include/internal/pycore_runtime_init_generated.h | 1 + Include/internal/pycore_unicodeobject_generated.h | 3 +++ Modules/_datetimemodule.c | 10 +++++----- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index a0f8fb71c1ff37..f3f85821b30bca 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -830,6 +830,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(c_call)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(c_exception)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(c_return)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cached_datetime_module)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cached_statements)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cadata)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cafile)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 57d85020f14e05..220271e13cbe74 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -319,6 +319,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(c_call) STRUCT_FOR_ID(c_exception) STRUCT_FOR_ID(c_return) + STRUCT_FOR_ID(cached_datetime_module) STRUCT_FOR_ID(cached_statements) STRUCT_FOR_ID(cadata) STRUCT_FOR_ID(cafile) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index e62ebd659d30e8..77705b55cd1dd3 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -828,6 +828,7 @@ extern "C" { INIT_ID(c_call), \ INIT_ID(c_exception), \ INIT_ID(c_return), \ + INIT_ID(cached_datetime_module), \ INIT_ID(cached_statements), \ INIT_ID(cadata), \ INIT_ID(cafile), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 892f580e8a6846..ee7b9b238d18da 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -798,6 +798,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(c_return); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(cached_datetime_module); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(cached_statements); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 4619345c8e857c..4fcd4b35f8c950 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -98,7 +98,7 @@ get_module_state(PyObject *module) } -#define INTERP_KEY "cached-datetime-module" +#define INTERP_KEY ((PyObject *)&_Py_ID(cached_datetime_module)) static PyObject * get_current_module(PyInterpreterState *interp) @@ -108,7 +108,7 @@ get_current_module(PyInterpreterState *interp) return NULL; } PyObject *ref = NULL; - if (PyDict_GetItemStringRef(dict, INTERP_KEY, &ref) < 0) { + if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { return NULL; } if (ref == NULL) { @@ -144,7 +144,7 @@ set_current_module(PyInterpreterState *interp, PyObject *mod) if (ref == NULL) { return -1; } - int rc = PyDict_SetItemString(dict, INTERP_KEY, ref); + int rc = PyDict_SetItem(dict, INTERP_KEY, ref); Py_DECREF(ref); return rc; } @@ -163,7 +163,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) if (expected != NULL) { PyObject *ref = NULL; - if (PyDict_GetItemStringRef(dict, INTERP_KEY, &ref) < 0) { + if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { goto error; } if (ref != NULL) { @@ -178,7 +178,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) } } - if (PyDict_DelItemString(dict, INTERP_KEY) < 0) { + if (PyDict_DelItem(dict, INTERP_KEY) < 0) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) { goto error; } From 2a2c0b166d702b2051f9a46cff234c93091bf72d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 11:56:45 -0600 Subject: [PATCH 19/38] Handle PyWeakref_GetRef() returning None. --- Modules/_datetimemodule.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 4fcd4b35f8c950..757ac5e02871a6 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -116,6 +116,9 @@ get_current_module(PyInterpreterState *interp) } PyObject *mod = NULL; (void)PyWeakref_GetRef(ref, &mod); + if (mod == Py_None) { + Py_CLEAR(mod); + } Py_DECREF(ref); return mod; } From c519e3c9d2e9960e202825e7921cde329fe9b160 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 12:11:11 -0600 Subject: [PATCH 20/38] Fix refleak. --- Modules/_datetimemodule.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 757ac5e02871a6..9a027aefc4d5ce 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7167,12 +7167,11 @@ _datetime_exec(PyObject *module) * values. This may change in the future.*/ /* -23:59 */ - PyObject *min = create_timezone_from_delta(-1, 60, 0, 1); - DATETIME_ADD_MACRO(d, "min", min); + DATETIME_ADD_MACRO(d, "min", create_timezone_from_delta(-1, 60, 0, 1)); /* +23:59 */ - PyObject *max = create_timezone_from_delta(0, (23 * 60 + 59) * 60, 0, 0); - DATETIME_ADD_MACRO(d, "max", max); + DATETIME_ADD_MACRO( + d, "max", create_timezone_from_delta(0, (23 * 60 + 59) * 60, 0, 0)); #undef DATETIME_ADD_MACRO From 05acc56c6bdd017fc02a2fb586597f66249353a4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 13:52:53 -0600 Subject: [PATCH 21/38] Make sure the module exists in static type methods. --- Modules/_datetimemodule.c | 47 +++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 9a027aefc4d5ce..252cc050b20292 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -123,15 +123,44 @@ get_current_module(PyInterpreterState *interp) return mod; } -#define GET_CURRENT_STATE(ST_VAR) \ - NULL; \ - PyObject *current_mod = NULL; \ - do { \ - PyInterpreterState *interp = PyInterpreterState_Get(); \ - current_mod = get_current_module(interp); \ - assert(current_mod != NULL); \ - ST_VAR = get_module_state(current_mod); \ - } while (0) +static PyModuleDef datetimemodule; + +static datetime_state * +_get_current_state(PyObject **p_mod) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + PyObject *mod = get_current_module(interp); + if (mod == NULL) { + assert(!PyErr_Occurred()); + if (PyErr_Occurred()) { + return NULL; + } + /* The static types can outlive the module, so we must + * temporarily load the module if one of the static types' + * methods needs module state. We can cut some corners + * since the module was necessarily already imported successfully + * and the module will only be around temporarily. We don't even + * need a spec. Normally the module would have been found + * and the temporary module would never happen. */ + mod = PyModule_New("_datetime"); + if (mod == NULL) { + return NULL; + } + ((PyModuleObject*)mod)->md_def = &datetimemodule; + if (PyModule_ExecDef(mod, &datetimemodule) < 0) { + Py_DECREF(mod); + return NULL; + } + } + datetime_state *st = get_module_state(mod); + *p_mod = mod; + return st; +} + +#define GET_CURRENT_STATE(ST_VAR) \ + NULL; \ + PyObject *current_mod = NULL; \ + ST_VAR = _get_current_state(¤t_mod); #define RELEASE_CURRENT_STATE(ST_VAR) \ Py_DECREF(current_mod) From 07e3b650a3b22375c114cfc337ea577fc92836b6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 13:57:44 -0600 Subject: [PATCH 22/38] Don't bother making the module temporary. --- Modules/_datetimemodule.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 252cc050b20292..8fa2a8c24b10c1 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -135,22 +135,12 @@ _get_current_state(PyObject **p_mod) if (PyErr_Occurred()) { return NULL; } - /* The static types can outlive the module, so we must - * temporarily load the module if one of the static types' - * methods needs module state. We can cut some corners - * since the module was necessarily already imported successfully - * and the module will only be around temporarily. We don't even - * need a spec. Normally the module would have been found - * and the temporary module would never happen. */ - mod = PyModule_New("_datetime"); + /* The static types can outlive the module, + * so we must re-import the module. */ + mod = PyImport_ImportModule("_datetime"); if (mod == NULL) { return NULL; } - ((PyModuleObject*)mod)->md_def = &datetimemodule; - if (PyModule_ExecDef(mod, &datetimemodule) < 0) { - Py_DECREF(mod); - return NULL; - } } datetime_state *st = get_module_state(mod); *p_mod = mod; From 1e3005d1874534121aba7de890e91300ac5fac84 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 14:45:04 -0600 Subject: [PATCH 23/38] _PyStaticType_Dealloc() -> _PyStaticType_FiniBuiltin() --- Include/internal/pycore_typeobject.h | 2 +- Objects/exceptions.c | 2 +- Objects/object.c | 2 +- Objects/structseq.c | 2 +- Objects/typeobject.c | 2 +- Objects/unicodeobject.c | 6 +++--- Python/crossinterp_exceptions.h | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 7e533bd138469b..d0414ed27c8ecb 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -154,7 +154,7 @@ _PyType_GetModuleState(PyTypeObject *type) extern int _PyStaticType_InitBuiltin(PyInterpreterState *, PyTypeObject *type); extern static_builtin_state * _PyStaticType_GetState(PyInterpreterState *, PyTypeObject *); extern void _PyStaticType_ClearWeakRefs(PyInterpreterState *, PyTypeObject *type); -extern void _PyStaticType_Dealloc(PyInterpreterState *, PyTypeObject *); +extern void _PyStaticType_FiniBuiltin(PyInterpreterState *, PyTypeObject *); // Export for 'math' shared extension, used via _PyType_IsReady() static inline // function diff --git a/Objects/exceptions.c b/Objects/exceptions.c index f9cd577c1c16be..3a72cce1dff0c7 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -3725,7 +3725,7 @@ _PyExc_FiniTypes(PyInterpreterState *interp) { for (Py_ssize_t i=Py_ARRAY_LENGTH(static_exceptions) - 1; i >= 0; i--) { PyTypeObject *exc = static_exceptions[i].exc; - _PyStaticType_Dealloc(interp, exc); + _PyStaticType_FiniBuiltin(interp, exc); } } diff --git a/Objects/object.c b/Objects/object.c index d4fe14c5b3d1aa..40efc08ef019de 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2355,7 +2355,7 @@ _PyTypes_FiniTypes(PyInterpreterState *interp) // their base classes. for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types)-1; i>=0; i--) { PyTypeObject *type = static_types[i]; - _PyStaticType_Dealloc(interp, type); + _PyStaticType_FiniBuiltin(interp, type); } } diff --git a/Objects/structseq.c b/Objects/structseq.c index ec5c5ab45ba813..d8289f2638db0f 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -718,7 +718,7 @@ _PyStructSequence_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type) return; } - _PyStaticType_Dealloc(interp, type); + _PyStaticType_FiniBuiltin(interp, type); if (_Py_IsMainInterpreter(interp)) { // Undo _PyStructSequence_InitBuiltinWithFlags(). diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 9d849d83082ccd..82ddc6dd26bb5d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5686,7 +5686,7 @@ clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type) } void -_PyStaticType_Dealloc(PyInterpreterState *interp, PyTypeObject *type) +_PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type) { assert(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); assert(_Py_IsImmortal((PyObject *)type)); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index eb37b478cc4de1..8df6907a4a3009 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15520,9 +15520,9 @@ unicode_is_finalizing(void) void _PyUnicode_FiniTypes(PyInterpreterState *interp) { - _PyStaticType_Dealloc(interp, &EncodingMapType); - _PyStaticType_Dealloc(interp, &PyFieldNameIter_Type); - _PyStaticType_Dealloc(interp, &PyFormatterIter_Type); + _PyStaticType_FiniBuiltin(interp, &EncodingMapType); + _PyStaticType_FiniBuiltin(interp, &PyFieldNameIter_Type); + _PyStaticType_FiniBuiltin(interp, &PyFormatterIter_Type); } diff --git a/Python/crossinterp_exceptions.h b/Python/crossinterp_exceptions.h index 6ecc10c7955fd8..278511da615c75 100644 --- a/Python/crossinterp_exceptions.h +++ b/Python/crossinterp_exceptions.h @@ -90,6 +90,6 @@ static void fini_exceptions(PyInterpreterState *interp) { // Likewise with _fini_not_shareable_error_type(). - _PyStaticType_Dealloc(interp, &_PyExc_InterpreterNotFoundError); - _PyStaticType_Dealloc(interp, &_PyExc_InterpreterError); + _PyStaticType_FiniBuiltin(interp, &_PyExc_InterpreterNotFoundError); + _PyStaticType_FiniBuiltin(interp, &_PyExc_InterpreterError); } From ab388578d09706cc746e3787fd6314b83797a777 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 31 May 2024 16:34:35 -0600 Subject: [PATCH 24/38] Add _PyStaticType_InitForExtension() and _PyStaticType_FiniForExtension(). --- Include/internal/pycore_object.h | 2 +- Include/internal/pycore_typeobject.h | 48 +++++-- Objects/typeobject.c | 206 +++++++++++++++++++-------- Objects/weakrefobject.c | 2 +- 4 files changed, 185 insertions(+), 73 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 7602248f956405..92ac4417451ddb 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -586,7 +586,7 @@ _PyObject_GET_WEAKREFS_LISTPTR(PyObject *op) if (PyType_Check(op) && ((PyTypeObject *)op)->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState( + managed_static_type_state *state = _PyStaticType_GetState( interp, (PyTypeObject *)op); return _PyStaticType_GET_WEAKREFS_LISTPTR(state); } diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index d0414ed27c8ecb..8664ae0e44533f 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -44,10 +44,12 @@ struct type_cache { /* For now we hard-code this to a value for which we are confident all the static builtin types will fit (for all builds). */ -#define _Py_MAX_STATIC_BUILTIN_TYPES 200 +#define _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES 200 +#define _Py_MAX_MANAGED_STATIC_EXT_TYPES 10 typedef struct { PyTypeObject *type; + int isbuiltin; int readying; int ready; // XXX tp_dict can probably be statically allocated, @@ -59,7 +61,7 @@ typedef struct { are also some diagnostic uses for the list of weakrefs, so we still keep it. */ PyObject *tp_weaklist; -} static_builtin_state; +} managed_static_type_state; struct types_state { /* Used to set PyTypeObject.tp_version_tag. @@ -105,8 +107,16 @@ struct types_state { num_builtins_initialized is incremented once for each static builtin type. Once initialization is over for a subinterpreter, the value will be the same as for all other interpreters. */ - size_t num_builtins_initialized; - static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES]; + struct { + size_t num_initialized; + managed_static_type_state initialized[_Py_MAX_MANAGED_STATIC_BUILTIN_TYPES]; + } builtins; + /* We apply a similar strategy for managed extension modules. */ + struct { + size_t num_initialized; + size_t next_index; + managed_static_type_state initialized[_Py_MAX_MANAGED_STATIC_EXT_TYPES]; + } for_extensions; PyMutex mutex; }; @@ -130,12 +140,35 @@ typedef struct wrapperbase pytype_slotdef; static inline PyObject ** -_PyStaticType_GET_WEAKREFS_LISTPTR(static_builtin_state *state) +_PyStaticType_GET_WEAKREFS_LISTPTR(managed_static_type_state *state) { assert(state != NULL); return &state->tp_weaklist; } +extern int _PyStaticType_InitBuiltin( + PyInterpreterState *interp, + PyTypeObject *type); +extern void _PyStaticType_FiniBuiltin( + PyInterpreterState *interp, + PyTypeObject *type); +extern void _PyStaticType_ClearWeakRefs( + PyInterpreterState *interp, + PyTypeObject *type); +extern managed_static_type_state * _PyStaticType_GetState( + PyInterpreterState *interp, + PyTypeObject *type); + +// Export for '_datetime' shared extension. +PyAPI_FUNC(int) _PyStaticType_InitForExtension( + PyInterpreterState *interp, + PyTypeObject *self); +PyAPI_FUNC(void) _PyStaticType_FiniForExtension( + PyInterpreterState *interp, + PyTypeObject *self, + int final); + + /* Like PyType_GetModuleState, but skips verification * that type is a heap type with an associated module */ static inline void * @@ -151,11 +184,6 @@ _PyType_GetModuleState(PyTypeObject *type) } -extern int _PyStaticType_InitBuiltin(PyInterpreterState *, PyTypeObject *type); -extern static_builtin_state * _PyStaticType_GetState(PyInterpreterState *, PyTypeObject *); -extern void _PyStaticType_ClearWeakRefs(PyInterpreterState *, PyTypeObject *type); -extern void _PyStaticType_FiniBuiltin(PyInterpreterState *, PyTypeObject *); - // Export for 'math' shared extension, used via _PyType_IsReady() static inline // function PyAPI_FUNC(PyObject *) _PyType_GetDict(PyTypeObject *); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 82ddc6dd26bb5d..d741ceddac57c5 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -131,93 +131,147 @@ type_from_ref(PyObject *ref) #ifndef NDEBUG static inline int -static_builtin_index_is_set(PyTypeObject *self) +managed_static_type_index_is_set(PyTypeObject *self) { return self->tp_subclasses != NULL; } #endif static inline size_t -static_builtin_index_get(PyTypeObject *self) +managed_static_type_index_get(PyTypeObject *self) { - assert(static_builtin_index_is_set(self)); + assert(managed_static_type_index_is_set(self)); /* We store a 1-based index so 0 can mean "not initialized". */ return (size_t)self->tp_subclasses - 1; } static inline void -static_builtin_index_set(PyTypeObject *self, size_t index) +managed_static_type_index_set(PyTypeObject *self, size_t index) { - assert(index < _Py_MAX_STATIC_BUILTIN_TYPES); + assert(index < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES); /* We store a 1-based index so 0 can mean "not initialized". */ self->tp_subclasses = (PyObject *)(index + 1); } static inline void -static_builtin_index_clear(PyTypeObject *self) +managed_static_type_index_clear(PyTypeObject *self) { self->tp_subclasses = NULL; } -static inline static_builtin_state * +static inline managed_static_type_state * static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self) { - return &(interp->types.builtins[static_builtin_index_get(self)]); + return &(interp->types.builtins.initialized[ + managed_static_type_index_get(self)]); +} + +static inline managed_static_type_state * +static_ext_type_state_get(PyInterpreterState *interp, PyTypeObject *self) +{ + return &(interp->types.for_extensions.initialized[ + managed_static_type_index_get(self)]); +} + +static managed_static_type_state * +managed_static_type_state_get(PyInterpreterState *interp, PyTypeObject *self) +{ + // It's probably a builtin type. + size_t index = managed_static_type_index_get(self); + managed_static_type_state *state = + &(interp->types.builtins.initialized[index]); + if (state->type == self) { + return state; + } + if (index > _Py_MAX_MANAGED_STATIC_EXT_TYPES) { + return state; + } + return &(interp->types.for_extensions.initialized[index]); } /* For static types we store some state in an array on each interpreter. */ -static_builtin_state * +managed_static_type_state * _PyStaticType_GetState(PyInterpreterState *interp, PyTypeObject *self) { assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); - return static_builtin_state_get(interp, self); + return managed_static_type_state_get(interp, self); } /* Set the type's per-interpreter state. */ static void -static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) +managed_static_type_state_init(PyInterpreterState *interp, PyTypeObject *self, + int isbuiltin, int initial) { - if (_Py_IsMainInterpreter(interp)) { - assert(!static_builtin_index_is_set(self)); - static_builtin_index_set(self, interp->types.num_builtins_initialized); + size_t index = interp->types.builtins.num_initialized; + if (!isbuiltin) { + PyMutex_Lock(&interp->types.mutex); + index = interp->types.for_extensions.next_index; + interp->types.for_extensions.next_index++; + PyMutex_Unlock(&interp->types.mutex); + } + + if (initial) { + assert(!managed_static_type_index_is_set(self)); + managed_static_type_index_set(self, index); } else { - assert(static_builtin_index_get(self) == - interp->types.num_builtins_initialized); + assert(managed_static_type_index_get(self) == index); } - static_builtin_state *state = static_builtin_state_get(interp, self); + managed_static_type_state *state = isbuiltin + ? &(interp->types.builtins.initialized[index]) + : &(interp->types.for_extensions.initialized[index]); /* It should only be called once for each builtin type. */ assert(state->type == NULL); state->type = self; + state->isbuiltin = isbuiltin; /* state->tp_subclasses is left NULL until init_subclasses() sets it. */ /* state->tp_weaklist is left NULL until insert_head() or insert_after() (in weakrefobject.c) sets it. */ - interp->types.num_builtins_initialized++; + if (isbuiltin) { + interp->types.builtins.num_initialized++; + } + else { + interp->types.for_extensions.num_initialized++; + } } /* Reset the type's per-interpreter state. - This basically undoes what static_builtin_state_init() did. */ + This basically undoes what managed_static_type_state_init() did. */ static void -static_builtin_state_clear(PyInterpreterState *interp, PyTypeObject *self) +managed_static_type_state_clear(PyInterpreterState *interp, PyTypeObject *self, + int isbuiltin, int final) { - static_builtin_state *state = static_builtin_state_get(interp, self); + managed_static_type_state *state = isbuiltin + ? static_builtin_state_get(interp, self) + : static_ext_type_state_get(interp, self); assert(state->type != NULL); state->type = NULL; assert(state->tp_weaklist == NULL); // It was already cleared out. - if (_Py_IsMainInterpreter(interp)) { - static_builtin_index_clear(self); + if (final) { + managed_static_type_index_clear(self); } - assert(interp->types.num_builtins_initialized > 0); - interp->types.num_builtins_initialized--; + if (isbuiltin) { + assert(interp->types.builtins.num_initialized > 0); + interp->types.builtins.num_initialized--; + } + else { + PyMutex_Lock(&interp->types.mutex); + assert(interp->types.for_extensions.num_initialized > 0); + interp->types.for_extensions.num_initialized--; + if (interp->types.for_extensions.num_initialized == 0) { + interp->types.for_extensions.next_index = 0; + } + PyMutex_Unlock(&interp->types.mutex); + } } -// Also see _PyStaticType_InitBuiltin() and _PyStaticType_Dealloc(). +// Also see _PyStaticType_InitBuiltin() and _PyStaticType_FiniBuiltin(). /* end static builtin helpers */ @@ -227,7 +281,7 @@ start_readying(PyTypeObject *type) { if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = static_builtin_state_get(interp, type); + managed_static_type_state *state = managed_static_type_state_get(interp, type); assert(state != NULL); assert(!state->readying); state->readying = 1; @@ -242,7 +296,7 @@ stop_readying(PyTypeObject *type) { if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = static_builtin_state_get(interp, type); + managed_static_type_state *state = managed_static_type_state_get(interp, type); assert(state != NULL); assert(state->readying); state->readying = 0; @@ -257,7 +311,7 @@ is_readying(PyTypeObject *type) { if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = static_builtin_state_get(interp, type); + managed_static_type_state *state = managed_static_type_state_get(interp, type); assert(state != NULL); return state->readying; } @@ -272,7 +326,7 @@ lookup_tp_dict(PyTypeObject *self) { if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState(interp, self); + managed_static_type_state *state = _PyStaticType_GetState(interp, self); assert(state != NULL); return state->tp_dict; } @@ -298,7 +352,7 @@ set_tp_dict(PyTypeObject *self, PyObject *dict) { if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState(interp, self); + managed_static_type_state *state = _PyStaticType_GetState(interp, self); assert(state != NULL); state->tp_dict = dict; return; @@ -311,7 +365,7 @@ clear_tp_dict(PyTypeObject *self) { if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState(interp, self); + managed_static_type_state *state = _PyStaticType_GetState(interp, self); assert(state != NULL); Py_CLEAR(state->tp_dict); return; @@ -363,10 +417,10 @@ set_tp_bases(PyTypeObject *self, PyObject *bases) } static inline void -clear_tp_bases(PyTypeObject *self) +clear_tp_bases(PyTypeObject *self, int final) { if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { - if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) { + if (final) { if (self->tp_bases != NULL) { if (PyTuple_GET_SIZE(self->tp_bases) == 0) { Py_CLEAR(self->tp_bases); @@ -428,10 +482,10 @@ set_tp_mro(PyTypeObject *self, PyObject *mro) } static inline void -clear_tp_mro(PyTypeObject *self) +clear_tp_mro(PyTypeObject *self, int final) { if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { - if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) { + if (final) { if (self->tp_mro != NULL) { if (PyTuple_GET_SIZE(self->tp_mro) == 0) { Py_CLEAR(self->tp_mro); @@ -457,7 +511,7 @@ init_tp_subclasses(PyTypeObject *self) } if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState(interp, self); + managed_static_type_state *state = _PyStaticType_GetState(interp, self); state->tp_subclasses = subclasses; return subclasses; } @@ -473,7 +527,7 @@ clear_tp_subclasses(PyTypeObject *self) has no subclass. */ if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState(interp, self); + managed_static_type_state *state = _PyStaticType_GetState(interp, self); Py_CLEAR(state->tp_subclasses); return; } @@ -485,7 +539,7 @@ lookup_tp_subclasses(PyTypeObject *self) { if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { PyInterpreterState *interp = _PyInterpreterState_GET(); - static_builtin_state *state = _PyStaticType_GetState(interp, self); + managed_static_type_state *state = _PyStaticType_GetState(interp, self); assert(state != NULL); return state->tp_subclasses; } @@ -774,10 +828,10 @@ _PyTypes_Fini(PyInterpreterState *interp) struct type_cache *cache = &interp->types.type_cache; type_cache_clear(cache, NULL); - assert(interp->types.num_builtins_initialized == 0); + assert(interp->types.builtins.num_initialized == 0); // All the static builtin types should have been finalized already. - for (size_t i = 0; i < _Py_MAX_STATIC_BUILTIN_TYPES; i++) { - assert(interp->types.builtins[i].type == NULL); + for (size_t i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) { + assert(interp->types.builtins.initialized[i].type == NULL); } } @@ -5628,7 +5682,7 @@ type_dealloc_common(PyTypeObject *type) static void -clear_static_tp_subclasses(PyTypeObject *type) +clear_static_tp_subclasses(PyTypeObject *type, int isbuiltin) { PyObject *subclasses = lookup_tp_subclasses(type); if (subclasses == NULL) { @@ -5665,47 +5719,64 @@ clear_static_tp_subclasses(PyTypeObject *type) continue; } // All static builtin subtypes should have been finalized already. - assert(!(subclass->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN)); + assert(!isbuiltin || !(subclass->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN)); Py_DECREF(subclass); } +#else + (void)isbuiltin; #endif clear_tp_subclasses(type); } static void -clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type) +clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type, + int isbuiltin, int final) { - if (_Py_IsMainInterpreter(interp)) { + if (final) { Py_CLEAR(type->tp_cache); } clear_tp_dict(type); - clear_tp_bases(type); - clear_tp_mro(type); - clear_static_tp_subclasses(type); + clear_tp_bases(type, final); + clear_tp_mro(type, final); + clear_static_tp_subclasses(type, isbuiltin); } + void -_PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type) +fini_static_type(PyInterpreterState *interp, PyTypeObject *type, + int isbuiltin, int final) { assert(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); assert(_Py_IsImmortal((PyObject *)type)); type_dealloc_common(type); - clear_static_type_objects(interp, type); + clear_static_type_objects(interp, type, isbuiltin, final); - if (_Py_IsMainInterpreter(interp)) { + if (final) { type->tp_flags &= ~Py_TPFLAGS_READY; type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; type->tp_version_tag = 0; } _PyStaticType_ClearWeakRefs(interp, type); - static_builtin_state_clear(interp, type); + managed_static_type_state_clear(interp, type, isbuiltin, final); /* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */ } +void +_PyStaticType_FiniForExtension(PyInterpreterState *interp, PyTypeObject *type, int final) +{ + fini_static_type(interp, type, 0, final); +} + +void +_PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type) +{ + fini_static_type(interp, type, 1, _Py_IsMainInterpreter(interp)); +} + static void type_dealloc(PyObject *self) @@ -8268,17 +8339,18 @@ PyType_Ready(PyTypeObject *type) return res; } -int -_PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) + +static int +init_static_type(PyInterpreterState *interp, PyTypeObject *self, + int isbuiltin, int initial) { assert(_Py_IsImmortal((PyObject *)self)); assert(!(self->tp_flags & Py_TPFLAGS_HEAPTYPE)); assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_DICT)); assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF)); - int ismain = _Py_IsMainInterpreter(interp); if ((self->tp_flags & Py_TPFLAGS_READY) == 0) { - assert(ismain); + assert(initial); self->tp_flags |= _Py_TPFLAGS_STATIC_BUILTIN; self->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; @@ -8288,24 +8360,36 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) self->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG; } else { - assert(!ismain); + assert(!initial); assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); assert(self->tp_flags & Py_TPFLAGS_VALID_VERSION_TAG); } - static_builtin_state_init(interp, self); + managed_static_type_state_init(interp, self, isbuiltin, initial); int res; BEGIN_TYPE_LOCK(); - res = type_ready(self, !ismain); + res = type_ready(self, !initial); END_TYPE_LOCK() if (res < 0) { _PyStaticType_ClearWeakRefs(interp, self); - static_builtin_state_clear(interp, self); + managed_static_type_state_clear(interp, self, isbuiltin, initial); } return res; } +int +_PyStaticType_InitForExtension(PyInterpreterState *interp, PyTypeObject *self) +{ + return init_static_type(interp, self, 0, ((self->tp_flags & Py_TPFLAGS_READY) == 0)); +} + +int +_PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) +{ + return init_static_type(interp, self, 1, _Py_IsMainInterpreter(interp)); +} + static int add_subclass(PyTypeObject *base, PyTypeObject *type) diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 88afaec86827ed..3b027e1b518ba6 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1066,7 +1066,7 @@ PyObject_ClearWeakRefs(PyObject *object) void _PyStaticType_ClearWeakRefs(PyInterpreterState *interp, PyTypeObject *type) { - static_builtin_state *state = _PyStaticType_GetState(interp, type); + managed_static_type_state *state = _PyStaticType_GetState(interp, type); PyObject **list = _PyStaticType_GET_WEAKREFS_LISTPTR(state); // This is safe to do without holding the lock in free-threaded builds; // there is only one thread running and no new threads can be created. From 0fb6bb3fa575f62158583f478772681f94d032e0 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 1 Jun 2024 12:33:03 -0600 Subject: [PATCH 25/38] Use them for the _datetime module. --- Modules/_datetimemodule.c | 180 +++++++++++++++++++++++++++----------- 1 file changed, 130 insertions(+), 50 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8fa2a8c24b10c1..e9dc9c4717da5b 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -101,26 +101,37 @@ get_module_state(PyObject *module) #define INTERP_KEY ((PyObject *)&_Py_ID(cached_datetime_module)) static PyObject * -get_current_module(PyInterpreterState *interp) +get_current_module(PyInterpreterState *interp, int *p_reloading) { + PyObject *mod = NULL; + int reloading = 0; + PyObject *dict = PyInterpreterState_GetDict(interp); if (dict == NULL) { - return NULL; + goto error; } PyObject *ref = NULL; if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { - return NULL; + goto error; } - if (ref == NULL) { - return NULL; + if (ref != NULL) { + reloading = 1; + if (ref != Py_None) { + (void)PyWeakref_GetRef(ref, &mod); + if (mod == Py_None) { + Py_CLEAR(mod); + } + Py_DECREF(ref); + } } - PyObject *mod = NULL; - (void)PyWeakref_GetRef(ref, &mod); - if (mod == Py_None) { - Py_CLEAR(mod); + if (p_reloading != NULL) { + *p_reloading = reloading; } - Py_DECREF(ref); return mod; + +error: + assert(PyErr_Occurred()); + return NULL; } static PyModuleDef datetimemodule; @@ -129,7 +140,7 @@ static datetime_state * _get_current_state(PyObject **p_mod) { PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *mod = get_current_module(interp); + PyObject *mod = get_current_module(interp, NULL); if (mod == NULL) { assert(!PyErr_Occurred()); if (PyErr_Occurred()) { @@ -200,10 +211,9 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) } } - if (PyDict_DelItem(dict, INTERP_KEY) < 0) { - if (!PyErr_ExceptionMatches(PyExc_KeyError)) { - goto error; - } + /* We use None to to identify tha the module was previously loaded. */ + if (PyDict_SetItem(dict, INTERP_KEY, Py_None) < 0) { + goto error; } goto finally; @@ -6932,14 +6942,19 @@ static PyTypeObject PyDateTime_DateTimeType = { }; /* --------------------------------------------------------------------------- - * Module methods and initialization. + * datetime C-API. */ -static PyMethodDef module_methods[] = { - {NULL, NULL} +static PyTypeObject * const capi_types[] = { + &PyDateTime_DateType, + &PyDateTime_DateTimeType, + &PyDateTime_TimeType, + &PyDateTime_DeltaType, + &PyDateTime_TZInfoType, + /* Indirectly, via the utc object. */ + &PyDateTime_TimeZoneType, }; - /* The C-API is process-global. This violates interpreter isolation * due to the objects stored here. Thus each of those objects must * be managed carefully. */ @@ -6989,6 +7004,11 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize) return tz; } + +/* --------------------------------------------------------------------------- + * Module state lifecycle. + */ + static int init_state(datetime_state *st, PyObject *module, PyObject *old_module) { @@ -7090,38 +7110,103 @@ clear_state(datetime_state *st) return 0; } + +/* --------------------------------------------------------------------------- + * Global module state. + */ + +// If we make _PyStaticType_*ForExtension() public +// then all this should be managed by the runtime. + +static struct { + PyMutex mutex; + int64_t interp_count; +} _globals = {0}; + +static void +callback_for_interp_exit(void *Py_UNUSED(data)) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + + assert(_globals.interp_count > 0); + PyMutex_Lock(&_globals.mutex); + int64_t final = !_globals.interp_count; + _globals.interp_count -= 1; + PyMutex_Unlock(&_globals.mutex); + + for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { + PyTypeObject *type = capi_types[i]; + _PyStaticType_FiniForExtension(interp, type, final); + } +} + +static int +init_static_types(PyInterpreterState *interp, int reloading) +{ + if (reloading) { + return 0; + } + + // `&...` 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 + PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType; + PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType; + + for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { + PyTypeObject *type = capi_types[i]; + if (_PyStaticType_InitForExtension(interp, type) < 0) { + return -1; + } + } + + PyMutex_Lock(&_globals.mutex); + assert(_globals.interp_count >= 0); + _globals.interp_count += 1; + PyMutex_Unlock(&_globals.mutex); + + // XXX Add one callback per type? + if (PyUnstable_AtExit(interp, callback_for_interp_exit, NULL) < 0) { + return -1; + } + + return 0; +} + + +/* --------------------------------------------------------------------------- + * Module methods and initialization. + */ + +static PyMethodDef module_methods[] = { + {NULL, NULL} +}; + + static int _datetime_exec(PyObject *module) { int rc = -1; datetime_state *st = get_module_state(module); + int reloading = 0; PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *old_module = get_current_module(interp); + PyObject *old_module = get_current_module(interp, &reloading); if (PyErr_Occurred()) { assert(old_module == NULL); goto error; } /* We actually set the "current" module right before a successful return. */ - // `&...` 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 - PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType; - PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType; - - PyTypeObject *capi_types[] = { - &PyDateTime_DateType, - &PyDateTime_DateTimeType, - &PyDateTime_TimeType, - &PyDateTime_DeltaType, - &PyDateTime_TZInfoType, - /* Indirectly, via the utc object. */ - &PyDateTime_TimeZoneType, - }; + if (init_static_types(interp, reloading) < 0) { + goto error; + } for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { - if (PyModule_AddType(module, capi_types[i]) < 0) { + PyTypeObject *type = capi_types[i]; + const char *name = _PyType_Name(type); + assert(name != NULL); + if (PyModule_AddObjectRef(module, name, (PyObject *)type) < 0) { goto error; } } @@ -7130,11 +7215,8 @@ _datetime_exec(PyObject *module) goto error; } - /* For now we only set the objects on the static types once. - * We will relax that once each types __dict__ is per-interpreter. */ #define DATETIME_ADD_MACRO(dict, c, value_expr) \ do { \ - if (PyDict_GetItemString(dict, c) == NULL) { \ assert(!PyErr_Occurred()); \ PyObject *value = (value_expr); \ if (value == NULL) { \ @@ -7145,30 +7227,29 @@ _datetime_exec(PyObject *module) goto error; \ } \ Py_DECREF(value); \ - } \ } while(0) /* timedelta values */ - PyObject *d = PyDateTime_DeltaType.tp_dict; + PyObject *d = _PyType_GetDict(&PyDateTime_DeltaType); DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0)); DATETIME_ADD_MACRO(d, "max", new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); /* date values */ - d = PyDateTime_DateType.tp_dict; + d = _PyType_GetDict(&PyDateTime_DateType); DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1)); DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31)); DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0)); /* time values */ - d = PyDateTime_TimeType.tp_dict; + d = _PyType_GetDict(&PyDateTime_TimeType); DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0)); DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0)); DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* datetime values */ - d = PyDateTime_DateTimeType.tp_dict; + d = _PyType_GetDict(&PyDateTime_DateTimeType); DATETIME_ADD_MACRO(d, "min", new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None, 0)); DATETIME_ADD_MACRO(d, "max", new_datetime(MAXYEAR, 12, 31, 23, 59, 59, @@ -7176,7 +7257,7 @@ _datetime_exec(PyObject *module) DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* timezone values */ - d = PyDateTime_TimeZoneType.tp_dict; + d = _PyType_GetDict(&PyDateTime_TimeZoneType); if (PyDict_SetItemString(d, "utc", (PyObject *)&utc_timezone) < 0) { goto error; } @@ -7273,17 +7354,16 @@ module_clear(PyObject *mod) PyInterpreterState *interp = PyInterpreterState_Get(); clear_current_module(interp, mod); + // We take care of the static types via an interpreter atexit hook. + // See callback_for_interp_exit() above. + return 0; } static void module_free(void *mod) { - datetime_state *st = get_module_state((PyObject *)mod); - clear_state(st); - - PyInterpreterState *interp = PyInterpreterState_Get(); - clear_current_module(interp, (PyObject *)mod); + (void)module_clear((PyObject *)mod); } static PyModuleDef datetimemodule = { From 9e4eee6b66135be2f1e697e47664aab5789354d7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 1 Jun 2024 16:57:46 -0600 Subject: [PATCH 26/38] Mark the module as safe for subinterpreters. --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index e9dc9c4717da5b..78cc784628d917 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7332,7 +7332,7 @@ _datetime_exec(PyObject *module) static PyModuleDef_Slot module_slots[] = { {Py_mod_exec, _datetime_exec}, - {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; From 293fd2ec1d3ec16e3edc87ff72f9c21e092fc4d7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 1 Jun 2024 16:58:52 -0600 Subject: [PATCH 27/38] Add a NEWS entry. --- .../next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst b/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst new file mode 100644 index 00000000000000..20b4a807f4b6c6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst @@ -0,0 +1,2 @@ +The ``_datetime`` module (C implementation for ``datetime``) now supports +being imported in multiple interpreters. From f0db33a4105f747c61d9ab1b1523e7591e178b7f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 1 Jun 2024 17:14:16 -0600 Subject: [PATCH 28/38] Ignore the new global vars in the C analyzer tool. --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 + Tools/c-analyzer/cpython/ignored.tsv | 1 + 2 files changed, 2 insertions(+) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 711ae343a8d876..4586a59f6ac2ef 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -307,6 +307,7 @@ Python/crossinterp_exceptions.h - PyExc_InterpreterNotFoundError - Modules/_datetimemodule.c - zero_delta - Modules/_datetimemodule.c - utc_timezone - Modules/_datetimemodule.c - capi - +Modules/_datetimemodule.c - _globals - Objects/boolobject.c - _Py_FalseStruct - Objects/boolobject.c - _Py_TrueStruct - Objects/dictobject.c - empty_keys_struct - diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index a3bdf0396fd3e1..466f25daa14dc6 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -217,6 +217,7 @@ Modules/_datetimemodule.c - max_fold_seconds - Modules/_datetimemodule.c datetime_isoformat specs - Modules/_datetimemodule.c parse_hh_mm_ss_ff correction - Modules/_datetimemodule.c time_isoformat specs - +Modules/_datetimemodule.c - capi_types - Modules/_decimal/_decimal.c - cond_map_template - Modules/_decimal/_decimal.c - dec_signal_string - Modules/_decimal/_decimal.c - dflt_ctx - From 326d95797bd8ba6439a33fb20218b4609f4bbb3d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 1 Jun 2024 17:30:10 -0600 Subject: [PATCH 29/38] Fix a smelly name. --- Objects/typeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d741ceddac57c5..df29e63dc6a1c7 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5743,7 +5743,7 @@ clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type, } -void +static void fini_static_type(PyInterpreterState *interp, PyTypeObject *type, int isbuiltin, int final) { From 04eb383943e561ab2db32aa23ae31b3ef558a343 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 11:48:21 -0600 Subject: [PATCH 30/38] Fix a comment. Co-authored-by: neonene <53406459+neonene@users.noreply.github.com> --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 78cc784628d917..a24beb9ef11327 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -211,7 +211,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) } } - /* We use None to to identify tha the module was previously loaded. */ + /* We use None to identify that the module was previously loaded. */ if (PyDict_SetItem(dict, INTERP_KEY, Py_None) < 0) { goto error; } From 0e8c3b178ad8c351cace85e6598cc4c9b6116e7a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 12:19:05 -0600 Subject: [PATCH 31/38] Fix callback_for_interp_exit(). --- Modules/_datetimemodule.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index a24beb9ef11327..04d164513d7258 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7130,12 +7130,14 @@ callback_for_interp_exit(void *Py_UNUSED(data)) assert(_globals.interp_count > 0); PyMutex_Lock(&_globals.mutex); - int64_t final = !_globals.interp_count; _globals.interp_count -= 1; + int final = !_globals.interp_count; PyMutex_Unlock(&_globals.mutex); - for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { - PyTypeObject *type = capi_types[i]; + /* They must be done in reverse order so subclasses are finalized + * before base classes. */ + for (size_t i = Py_ARRAY_LENGTH(capi_types); i > 0; i--) { + PyTypeObject *type = capi_types[i-1]; _PyStaticType_FiniForExtension(interp, type, final); } } @@ -7153,6 +7155,8 @@ init_static_types(PyInterpreterState *interp, int reloading) PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType; PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType; + /* Bases classes must be initialized before subclasses, + * so capi_types must have the types in the appropriate order. */ for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { PyTypeObject *type = capi_types[i]; if (_PyStaticType_InitForExtension(interp, type) < 0) { From 67812dc9efec8ff37b4f5a3fb3eb9f7ad4c0021a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 12:59:50 -0600 Subject: [PATCH 32/38] Pass around "initial" instead of using _Py_IsMainInterpreter(). --- Objects/typeobject.c | 58 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index df29e63dc6a1c7..e9b65c774d1914 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -394,13 +394,13 @@ _PyType_GetBases(PyTypeObject *self) } static inline void -set_tp_bases(PyTypeObject *self, PyObject *bases) +set_tp_bases(PyTypeObject *self, PyObject *bases, int initial) { assert(PyTuple_CheckExact(bases)); if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { // XXX tp_bases can probably be statically allocated for each // static builtin type. - assert(_Py_IsMainInterpreter(_PyInterpreterState_GET())); + assert(initial); assert(self->tp_bases == NULL); if (PyTuple_GET_SIZE(bases) == 0) { assert(self->tp_base == NULL); @@ -467,13 +467,13 @@ _PyType_GetMRO(PyTypeObject *self) } static inline void -set_tp_mro(PyTypeObject *self, PyObject *mro) +set_tp_mro(PyTypeObject *self, PyObject *mro, int initial) { assert(PyTuple_CheckExact(mro)); if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { // XXX tp_mro can probably be statically allocated for each // static builtin type. - assert(_Py_IsMainInterpreter(_PyInterpreterState_GET())); + assert(initial); assert(self->tp_mro == NULL); /* Other checks are done via set_tp_bases. */ _Py_SetImmortal(mro); @@ -1498,7 +1498,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) Py_XDECREF(tuple); if (res < 0) { - set_tp_mro(type, old_mro); + set_tp_mro(type, old_mro, 0); Py_DECREF(new_mro); return -1; } @@ -1599,7 +1599,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, void *context) assert(old_bases != NULL); PyTypeObject *old_base = type->tp_base; - set_tp_bases(type, Py_NewRef(new_bases)); + set_tp_bases(type, Py_NewRef(new_bases), 0); type->tp_base = (PyTypeObject *)Py_NewRef(new_base); PyObject *temp = PyList_New(0); @@ -1647,7 +1647,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, void *context) "", 2, 3, &cls, &new_mro, &old_mro); /* Do not rollback if cls has a newer version of MRO. */ if (lookup_tp_mro(cls) == new_mro) { - set_tp_mro(cls, Py_XNewRef(old_mro)); + set_tp_mro(cls, Py_XNewRef(old_mro), 0); Py_DECREF(new_mro); } } @@ -1657,7 +1657,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, void *context) if (lookup_tp_bases(type) == new_bases) { assert(type->tp_base == new_base); - set_tp_bases(type, old_bases); + set_tp_bases(type, old_bases, 0); type->tp_base = old_base; Py_DECREF(new_bases); @@ -3138,7 +3138,7 @@ mro_invoke(PyTypeObject *type) - Returns -1 in case of an error. */ static int -mro_internal_unlocked(PyTypeObject *type, PyObject **p_old_mro) +mro_internal_unlocked(PyTypeObject *type, int initial, PyObject **p_old_mro) { ASSERT_TYPE_LOCK_HELD(); @@ -3161,7 +3161,7 @@ mro_internal_unlocked(PyTypeObject *type, PyObject **p_old_mro) return 0; } - set_tp_mro(type, new_mro); + set_tp_mro(type, new_mro, initial); type_mro_modified(type, new_mro); /* corner case: the super class might have been hidden @@ -3191,7 +3191,7 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro) { int res; BEGIN_TYPE_LOCK() - res = mro_internal_unlocked(type, p_old_mro); + res = mro_internal_unlocked(type, 0, p_old_mro); END_TYPE_LOCK() return res; } @@ -3786,7 +3786,7 @@ type_new_alloc(type_new_ctx *ctx) type->tp_as_mapping = &et->as_mapping; type->tp_as_buffer = &et->as_buffer; - set_tp_bases(type, Py_NewRef(ctx->bases)); + set_tp_bases(type, Py_NewRef(ctx->bases), 1); type->tp_base = (PyTypeObject *)Py_NewRef(ctx->base); type->tp_dealloc = subtype_dealloc; @@ -4780,7 +4780,7 @@ _PyType_FromMetaclass_impl( /* Set slots we have prepared */ type->tp_base = (PyTypeObject *)Py_NewRef(base); - set_tp_bases(type, bases); + set_tp_bases(type, bases, 1); bases = NULL; // We give our reference to bases to the type type->tp_doc = tp_doc; @@ -7830,10 +7830,10 @@ type_ready_set_type(PyTypeObject *type) } static int -type_ready_set_bases(PyTypeObject *type) +type_ready_set_bases(PyTypeObject *type, int initial) { if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { - if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) { + if (!initial) { assert(lookup_tp_bases(type) != NULL); return 0; } @@ -7852,7 +7852,7 @@ type_ready_set_bases(PyTypeObject *type) if (bases == NULL) { return -1; } - set_tp_bases(type, bases); + set_tp_bases(type, bases, 1); } return 0; } @@ -7962,12 +7962,12 @@ type_ready_preheader(PyTypeObject *type) } static int -type_ready_mro(PyTypeObject *type) +type_ready_mro(PyTypeObject *type, int initial) { ASSERT_TYPE_LOCK_HELD(); if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { - if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) { + if (!initial) { assert(lookup_tp_mro(type) != NULL); return 0; } @@ -7975,7 +7975,7 @@ type_ready_mro(PyTypeObject *type) } /* Calculate method resolution order */ - if (mro_internal_unlocked(type, NULL) < 0) { + if (mro_internal_unlocked(type, initial, NULL) < 0) { return -1; } PyObject *mro = lookup_tp_mro(type); @@ -8130,7 +8130,7 @@ type_ready_add_subclasses(PyTypeObject *type) // Set tp_new and the "__new__" key in the type dictionary. // Use the Py_TPFLAGS_DISALLOW_INSTANTIATION flag. static int -type_ready_set_new(PyTypeObject *type, int rerunbuiltin) +type_ready_set_new(PyTypeObject *type, int initial) { PyTypeObject *base = type->tp_base; /* The condition below could use some explanation. @@ -8152,7 +8152,7 @@ type_ready_set_new(PyTypeObject *type, int rerunbuiltin) if (!(type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION)) { if (type->tp_new != NULL) { - if (!rerunbuiltin || base == NULL || type->tp_new != base->tp_new) { + if (initial || base == NULL || type->tp_new != base->tp_new) { // If "__new__" key does not exists in the type dictionary, // set it to tp_new_wrapper(). if (add_tp_new_wrapper(type) < 0) { @@ -8234,7 +8234,7 @@ type_ready_post_checks(PyTypeObject *type) static int -type_ready(PyTypeObject *type, int rerunbuiltin) +type_ready(PyTypeObject *type, int initial) { ASSERT_TYPE_LOCK_HELD(); @@ -8264,19 +8264,19 @@ type_ready(PyTypeObject *type, int rerunbuiltin) if (type_ready_set_type(type) < 0) { goto error; } - if (type_ready_set_bases(type) < 0) { + if (type_ready_set_bases(type, initial) < 0) { goto error; } - if (type_ready_mro(type) < 0) { + if (type_ready_mro(type, initial) < 0) { goto error; } - if (type_ready_set_new(type, rerunbuiltin) < 0) { + if (type_ready_set_new(type, initial) < 0) { goto error; } if (type_ready_fill_dict(type) < 0) { goto error; } - if (!rerunbuiltin) { + if (initial) { if (type_ready_inherit(type) < 0) { goto error; } @@ -8290,7 +8290,7 @@ type_ready(PyTypeObject *type, int rerunbuiltin) if (type_ready_add_subclasses(type) < 0) { goto error; } - if (!rerunbuiltin) { + if (initial) { if (type_ready_managed_dict(type) < 0) { goto error; } @@ -8330,7 +8330,7 @@ PyType_Ready(PyTypeObject *type) int res; BEGIN_TYPE_LOCK() if (!(type->tp_flags & Py_TPFLAGS_READY)) { - res = type_ready(type, 0); + res = type_ready(type, 1); } else { res = 0; assert(_PyType_CheckConsistency(type)); @@ -8369,7 +8369,7 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self, int res; BEGIN_TYPE_LOCK(); - res = type_ready(self, !initial); + res = type_ready(self, initial); END_TYPE_LOCK() if (res < 0) { _PyStaticType_ClearWeakRefs(interp, self); From e5ab5486eace68b46d3cc98f23a40a4c3d156f2d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 13:05:55 -0600 Subject: [PATCH 33/38] Finalize the static types if PyUnstable_AtExit() fails. --- Modules/_datetimemodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 04d164513d7258..7565a64f654d29 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7171,6 +7171,7 @@ init_static_types(PyInterpreterState *interp, int reloading) // XXX Add one callback per type? if (PyUnstable_AtExit(interp, callback_for_interp_exit, NULL) < 0) { + callback_for_interp_exit(NULL); return -1; } From 4f21a2806e75904f0551c02465e1fe9eaa9e8757 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 13:58:16 -0600 Subject: [PATCH 34/38] Make sure we're using the right index for extension static types. --- Objects/typeobject.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e9b65c774d1914..147b3b1d011c6c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -202,26 +202,38 @@ static void managed_static_type_state_init(PyInterpreterState *interp, PyTypeObject *self, int isbuiltin, int initial) { - size_t index = interp->types.builtins.num_initialized; - if (!isbuiltin) { - PyMutex_Lock(&interp->types.mutex); - index = interp->types.for_extensions.next_index; - interp->types.for_extensions.next_index++; - PyMutex_Unlock(&interp->types.mutex); - } - + size_t index; if (initial) { assert(!managed_static_type_index_is_set(self)); + if (isbuiltin) { + index = interp->types.builtins.num_initialized; + assert(index < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES); + } + else { + PyMutex_Lock(&interp->types.mutex); + index = interp->types.for_extensions.next_index; + interp->types.for_extensions.next_index++; + PyMutex_Unlock(&interp->types.mutex); + assert(index < _Py_MAX_MANAGED_STATIC_EXT_TYPES); + } managed_static_type_index_set(self, index); } else { - assert(managed_static_type_index_get(self) == index); + index = managed_static_type_index_get(self); + if (isbuiltin) { + assert(index == interp->types.builtins.num_initialized); + assert(index < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES); + } + else { + assert(index < _Py_MAX_MANAGED_STATIC_EXT_TYPES); + } } + managed_static_type_state *state = isbuiltin ? &(interp->types.builtins.initialized[index]) : &(interp->types.for_extensions.initialized[index]); - /* It should only be called once for each builtin type. */ + /* It should only be called once for each builtin type per interpreter. */ assert(state->type == NULL); state->type = self; state->isbuiltin = isbuiltin; From 0fd9fae3cf7bc9fc3584af054e569ae7a287fa94 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 14:37:56 -0600 Subject: [PATCH 35/38] Use PyErr_WriteUnraisable(). --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 7565a64f654d29..4262e96c0262f2 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -219,7 +219,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) goto finally; error: - PyErr_Print(); + PyErr_WriteUnraisable(NULL); finally: Py_XDECREF(current); From 3905ae837529c92e4b55e09a1d9dc9d38a467f62 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 15:43:07 -0600 Subject: [PATCH 36/38] Clean up clear_current_module() a little. --- Modules/_datetimemodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 4262e96c0262f2..83f6208f7b5799 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -187,8 +187,6 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) { PyObject *exc = PyErr_GetRaisedException(); - PyObject *current = NULL; - PyObject *dict = PyInterpreterState_GetDict(interp); if (dict == NULL) { goto error; @@ -200,7 +198,10 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) goto error; } if (ref != NULL) { + PyObject *current = NULL; int rc = PyWeakref_GetRef(ref, ¤t); + /* We only need "current" for pointer comparison. */ + Py_XDECREF(current); Py_DECREF(ref); if (rc < 0) { goto error; @@ -222,7 +223,6 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) PyErr_WriteUnraisable(NULL); finally: - Py_XDECREF(current); PyErr_SetRaisedException(exc); } From 7216e24bf56877e8590abcc7d16fca015ee25e65 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 16:06:00 -0600 Subject: [PATCH 37/38] Update a TODO comment. --- Modules/_datetimemodule.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 83f6208f7b5799..d3f1bf6709fac4 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7169,7 +7169,9 @@ init_static_types(PyInterpreterState *interp, int reloading) _globals.interp_count += 1; PyMutex_Unlock(&_globals.mutex); - // XXX Add one callback per type? + /* It could make sense to add a separate callback + * for each of the types. However, for now we can take the simpler + * approach of a single callback. */ if (PyUnstable_AtExit(interp, callback_for_interp_exit, NULL) < 0) { callback_for_interp_exit(NULL); return -1; From e057548ceccbd792c9f46b7b392bf0c676453559 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 3 Jun 2024 16:45:30 -0600 Subject: [PATCH 38/38] Update Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst --- .../next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst b/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst index 20b4a807f4b6c6..b0fe06663248f6 100644 --- a/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst +++ b/Misc/NEWS.d/next/Library/2024-06-01-16-58-43.gh-issue-117398.kR0RW7.rst @@ -1,2 +1,2 @@ -The ``_datetime`` module (C implementation for ``datetime``) now supports +The ``_datetime`` module (C implementation for :mod:`datetime`) now supports being imported in multiple interpreters.