Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b263387

Browse filesBrowse files
pythongh-117398: Statically Allocate the Datetime C-API (pythonGH-119472)
1 parent 7322ff1 commit b263387
Copy full SHA for b263387

File tree

Expand file treeCollapse file tree

3 files changed

+89
-34
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+89
-34
lines changed
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Objects in the datetime C-API are now all statically allocated, which means
2+
better memory safety, especially when the module is reloaded. This should be
3+
transparent to users.

‎Modules/_datetimemodule.c

Copy file name to clipboardExpand all lines: Modules/_datetimemodule.c
+83-34Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,8 @@ new_time_subclass_fold_ex(int hour, int minute, int second, int usecond,
11721172
return t;
11731173
}
11741174

1175+
static PyDateTime_Delta * look_up_delta(int, int, int, PyTypeObject *);
1176+
11751177
/* Create a timedelta instance. Normalize the members iff normalize is
11761178
* true. Passing false is a speed optimization, if you know for sure
11771179
* that seconds and microseconds are already in their proper ranges. In any
@@ -1192,6 +1194,12 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize,
11921194
if (check_delta_day_range(days) < 0)
11931195
return NULL;
11941196

1197+
self = look_up_delta(days, seconds, microseconds, type);
1198+
if (self != NULL) {
1199+
return (PyObject *)self;
1200+
}
1201+
assert(!PyErr_Occurred());
1202+
11951203
self = (PyDateTime_Delta *) (type->tp_alloc(type, 0));
11961204
if (self != NULL) {
11971205
self->hashcode = -1;
@@ -1213,6 +1221,8 @@ typedef struct
12131221
PyObject *name;
12141222
} PyDateTime_TimeZone;
12151223

1224+
static PyDateTime_TimeZone * look_up_timezone(PyObject *offset, PyObject *name);
1225+
12161226
/* Create new timezone instance checking offset range. This
12171227
function does not check the name argument. Caller must assure
12181228
that offset is a timedelta instance and name is either NULL
@@ -1227,6 +1237,12 @@ create_timezone(PyObject *offset, PyObject *name)
12271237
assert(PyDelta_Check(offset));
12281238
assert(name == NULL || PyUnicode_Check(name));
12291239

1240+
self = look_up_timezone(offset, name);
1241+
if (self != NULL) {
1242+
return (PyObject *)self;
1243+
}
1244+
assert(!PyErr_Occurred());
1245+
12301246
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
12311247
if (self == NULL) {
12321248
return NULL;
@@ -2885,6 +2901,25 @@ static PyTypeObject PyDateTime_DeltaType = {
28852901
0, /* tp_free */
28862902
};
28872903

2904+
// XXX Can we make this const?
2905+
static PyDateTime_Delta zero_delta = {
2906+
PyObject_HEAD_INIT(&PyDateTime_DeltaType)
2907+
/* Letting this be set lazily is a benign race. */
2908+
.hashcode = -1,
2909+
};
2910+
2911+
static PyDateTime_Delta *
2912+
look_up_delta(int days, int seconds, int microseconds, PyTypeObject *type)
2913+
{
2914+
if (days == 0 && seconds == 0 && microseconds == 0
2915+
&& type == zero_delta.ob_base.ob_type)
2916+
{
2917+
return &zero_delta;
2918+
}
2919+
return NULL;
2920+
}
2921+
2922+
28882923
/*
28892924
* PyDateTime_Date implementation.
28902925
*/
@@ -4175,6 +4210,23 @@ static PyTypeObject PyDateTime_TimeZoneType = {
41754210
timezone_new, /* tp_new */
41764211
};
41774212

4213+
// XXX Can we make this const?
4214+
static PyDateTime_TimeZone utc_timezone = {
4215+
PyObject_HEAD_INIT(&PyDateTime_TimeZoneType)
4216+
.offset = (PyObject *)&zero_delta,
4217+
.name = NULL,
4218+
};
4219+
4220+
static PyDateTime_TimeZone *
4221+
look_up_timezone(PyObject *offset, PyObject *name)
4222+
{
4223+
if (offset == utc_timezone.offset && name == NULL) {
4224+
return &utc_timezone;
4225+
}
4226+
return NULL;
4227+
}
4228+
4229+
41784230
/*
41794231
* PyDateTime_Time implementation.
41804232
*/
@@ -6706,44 +6758,42 @@ static PyMethodDef module_methods[] = {
67066758
{NULL, NULL}
67076759
};
67086760

6761+
6762+
/* The C-API is process-global. This violates interpreter isolation
6763+
* due to the objects stored here. Thus each of those objects must
6764+
* be managed carefully. */
6765+
// XXX Can we make this const?
6766+
static PyDateTime_CAPI capi = {
6767+
/* The classes must be readied before used here.
6768+
* That will happen the first time the module is loaded.
6769+
* They aren't safe to be shared between interpreters,
6770+
* but that's okay as long as the module is single-phase init. */
6771+
.DateType = &PyDateTime_DateType,
6772+
.DateTimeType = &PyDateTime_DateTimeType,
6773+
.TimeType = &PyDateTime_TimeType,
6774+
.DeltaType = &PyDateTime_DeltaType,
6775+
.TZInfoType = &PyDateTime_TZInfoType,
6776+
6777+
.TimeZone_UTC = (PyObject *)&utc_timezone,
6778+
6779+
.Date_FromDate = new_date_ex,
6780+
.DateTime_FromDateAndTime = new_datetime_ex,
6781+
.Time_FromTime = new_time_ex,
6782+
.Delta_FromDelta = new_delta_ex,
6783+
.TimeZone_FromTimeZone = new_timezone,
6784+
.DateTime_FromTimestamp = datetime_fromtimestamp,
6785+
.Date_FromTimestamp = datetime_date_fromtimestamp_capi,
6786+
.DateTime_FromDateAndTimeAndFold = new_datetime_ex2,
6787+
.Time_FromTimeAndFold = new_time_ex2,
6788+
};
6789+
67096790
/* Get a new C API by calling this function.
67106791
* Clients get at C API via PyDateTime_IMPORT, defined in datetime.h.
67116792
*/
67126793
static inline PyDateTime_CAPI *
67136794
get_datetime_capi(void)
67146795
{
6715-
PyDateTime_CAPI *capi = PyMem_Malloc(sizeof(PyDateTime_CAPI));
6716-
if (capi == NULL) {
6717-
PyErr_NoMemory();
6718-
return NULL;
6719-
}
6720-
capi->DateType = &PyDateTime_DateType;
6721-
capi->DateTimeType = &PyDateTime_DateTimeType;
6722-
capi->TimeType = &PyDateTime_TimeType;
6723-
capi->DeltaType = &PyDateTime_DeltaType;
6724-
capi->TZInfoType = &PyDateTime_TZInfoType;
6725-
capi->Date_FromDate = new_date_ex;
6726-
capi->DateTime_FromDateAndTime = new_datetime_ex;
6727-
capi->Time_FromTime = new_time_ex;
6728-
capi->Delta_FromDelta = new_delta_ex;
6729-
capi->TimeZone_FromTimeZone = new_timezone;
6730-
capi->DateTime_FromTimestamp = datetime_fromtimestamp;
6731-
capi->Date_FromTimestamp = datetime_date_fromtimestamp_capi;
6732-
capi->DateTime_FromDateAndTimeAndFold = new_datetime_ex2;
6733-
capi->Time_FromTimeAndFold = new_time_ex2;
6734-
// Make sure this function is called after utc has
6735-
// been initialized.
6736-
datetime_state *st = STATIC_STATE();
6737-
assert(st->utc != NULL);
6738-
capi->TimeZone_UTC = st->utc; // borrowed ref
6739-
return capi;
6740-
}
6741-
6742-
static void
6743-
datetime_destructor(PyObject *op)
6744-
{
6745-
void *ptr = PyCapsule_GetPointer(op, PyDateTime_CAPSULE_NAME);
6746-
PyMem_Free(ptr);
6796+
return &capi;
67476797
}
67486798

67496799
static int
@@ -6933,8 +6983,7 @@ _datetime_exec(PyObject *module)
69336983
if (capi == NULL) {
69346984
goto error;
69356985
}
6936-
PyObject *capsule = PyCapsule_New(capi, PyDateTime_CAPSULE_NAME,
6937-
datetime_destructor);
6986+
PyObject *capsule = PyCapsule_New(capi, PyDateTime_CAPSULE_NAME, NULL);
69386987
if (capsule == NULL) {
69396988
PyMem_Free(capi);
69406989
goto error;

‎Tools/c-analyzer/cpython/globals-to-fix.tsv

Copy file name to clipboardExpand all lines: Tools/c-analyzer/cpython/globals-to-fix.tsv
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ Python/crossinterp_exceptions.h - PyExc_InterpreterNotFoundError -
304304
##-----------------------
305305
## singletons
306306

307+
Modules/_datetimemodule.c - zero_delta -
308+
Modules/_datetimemodule.c - utc_timezone -
309+
Modules/_datetimemodule.c - capi -
307310
Objects/boolobject.c - _Py_FalseStruct -
308311
Objects/boolobject.c - _Py_TrueStruct -
309312
Objects/dictobject.c - empty_keys_struct -

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.