@@ -1185,19 +1185,51 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path)
1185
1185
return 0 ;
1186
1186
}
1187
1187
1188
+ #ifndef NDEBUG
1189
+ static bool
1190
+ is_singlephase (PyModuleDef * def )
1191
+ {
1192
+ if (def == NULL ) {
1193
+ /* It must be a module created by reload_singlephase_extension()
1194
+ * from m_copy. Ideally we'd do away with this case. */
1195
+ return true;
1196
+ }
1197
+ else if (def -> m_slots == NULL ) {
1198
+ return true;
1199
+ }
1200
+ else {
1201
+ return false;
1202
+ }
1203
+ }
1204
+ #endif
1205
+
1206
+
1207
+ struct singlephase_global_update {
1208
+ PyObject * m_dict ;
1209
+ };
1188
1210
1189
1211
static int
1190
1212
update_global_state_for_extension (PyThreadState * tstate ,
1191
- PyObject * mod , PyModuleDef * def ,
1192
- PyObject * name , PyObject * path )
1213
+ PyObject * path , PyObject * name ,
1214
+ PyModuleDef * def ,
1215
+ struct singlephase_global_update * singlephase )
1193
1216
{
1194
- assert (mod != NULL && PyModule_Check (mod ));
1195
- assert (def == _PyModule_GetDef (mod ));
1196
-
1197
- // bpo-44050: Extensions and def->m_base.m_copy can be updated
1198
- // when the extension module doesn't support sub-interpreters.
1199
- if (def -> m_size == -1 ) {
1200
- if (!is_core_module (tstate -> interp , name , path )) {
1217
+ /* Copy the module's __dict__, if applicable. */
1218
+ if (singlephase == NULL ) {
1219
+ assert (def -> m_base .m_copy == NULL );
1220
+ }
1221
+ else {
1222
+ assert (def -> m_base .m_init != NULL
1223
+ || is_core_module (tstate -> interp , name , path ));
1224
+ if (singlephase -> m_dict == NULL ) {
1225
+ assert (def -> m_base .m_copy == NULL );
1226
+ }
1227
+ else {
1228
+ assert (PyDict_Check (singlephase -> m_dict ));
1229
+ // gh-88216: Extensions and def->m_base.m_copy can be updated
1230
+ // when the extension module doesn't support sub-interpreters.
1231
+ assert (def -> m_size == -1 );
1232
+ assert (!is_core_module (tstate -> interp , name , path ));
1201
1233
assert (PyUnicode_CompareWithASCIIString (name , "sys" ) != 0 );
1202
1234
assert (PyUnicode_CompareWithASCIIString (name , "builtins" ) != 0 );
1203
1235
if (def -> m_base .m_copy ) {
@@ -1206,17 +1238,16 @@ update_global_state_for_extension(PyThreadState *tstate,
1206
1238
XXX this should really not happen. */
1207
1239
Py_CLEAR (def -> m_base .m_copy );
1208
1240
}
1209
- PyObject * dict = PyModule_GetDict (mod );
1210
- if (dict == NULL ) {
1211
- return -1 ;
1212
- }
1213
- def -> m_base .m_copy = PyDict_Copy (dict );
1241
+ def -> m_base .m_copy = PyDict_Copy (singlephase -> m_dict );
1214
1242
if (def -> m_base .m_copy == NULL ) {
1243
+ // XXX Ignore this error? Doing so would effectively
1244
+ // mark the module as not loadable. */
1215
1245
return -1 ;
1216
1246
}
1217
1247
}
1218
1248
}
1219
1249
1250
+ /* Add the module's def to the global cache. */
1220
1251
// XXX Why special-case the main interpreter?
1221
1252
if (_Py_IsMainInterpreter (tstate -> interp ) || def -> m_size == -1 ) {
1222
1253
#ifndef NDEBUG
@@ -1258,6 +1289,8 @@ int
1258
1289
_PyImport_FixupExtensionObject (PyObject * mod , PyObject * name ,
1259
1290
PyObject * filename , PyObject * modules )
1260
1291
{
1292
+ PyThreadState * tstate = _PyThreadState_GET ();
1293
+
1261
1294
if (mod == NULL || !PyModule_Check (mod )) {
1262
1295
PyErr_BadInternalCall ();
1263
1296
return -1 ;
@@ -1268,15 +1301,28 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name,
1268
1301
return -1 ;
1269
1302
}
1270
1303
1271
- PyThreadState * tstate = _PyThreadState_GET ();
1304
+ /* Only single-phase init extension modules can reach here. */
1305
+ assert (is_singlephase (def ));
1306
+ assert (!is_core_module (tstate -> interp , name , filename ));
1307
+ assert (!is_core_module (tstate -> interp , name , name ));
1308
+
1309
+ struct singlephase_global_update singlephase = {0 };
1310
+ // gh-88216: Extensions and def->m_base.m_copy can be updated
1311
+ // when the extension module doesn't support sub-interpreters.
1312
+ if (def -> m_size == -1 ) {
1313
+ singlephase .m_dict = PyModule_GetDict (mod );
1314
+ assert (singlephase .m_dict != NULL );
1315
+ }
1272
1316
if (update_global_state_for_extension (
1273
- tstate , mod , def , name , filename ) < 0 )
1317
+ tstate , filename , name , def , & singlephase ) < 0 )
1274
1318
{
1275
1319
return -1 ;
1276
1320
}
1321
+
1277
1322
if (finish_singlephase_extension (tstate , mod , def , name , modules ) < 0 ) {
1278
1323
return -1 ;
1279
1324
}
1325
+
1280
1326
return 0 ;
1281
1327
}
1282
1328
@@ -1407,11 +1453,25 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name,
1407
1453
goto finally ;
1408
1454
}
1409
1455
1456
+ /* We only use _PyImport_FixupBuiltin() for the core builtin modules
1457
+ * (sys and builtins). These modules are single-phase init with no
1458
+ * module state, but we also don't populate def->m_base.m_copy
1459
+ * for them. */
1460
+ assert (is_core_module (tstate -> interp , nameobj , nameobj ));
1461
+ assert (is_singlephase (def ));
1462
+ assert (def -> m_size == -1 );
1463
+ assert (def -> m_base .m_copy == NULL );
1464
+
1465
+ struct singlephase_global_update singlephase = {
1466
+ /* We don't want def->m_base.m_copy populated. */
1467
+ .m_dict = NULL ,
1468
+ };
1410
1469
if (update_global_state_for_extension (
1411
- tstate , mod , def , nameobj , nameobj ) < 0 )
1470
+ tstate , nameobj , nameobj , def , & singlephase ) < 0 )
1412
1471
{
1413
1472
goto finally ;
1414
1473
}
1474
+
1415
1475
if (finish_singlephase_extension (tstate , mod , def , nameobj , modules ) < 0 ) {
1416
1476
goto finally ;
1417
1477
}
@@ -1444,6 +1504,7 @@ is_builtin(PyObject *name)
1444
1504
static PyObject *
1445
1505
create_builtin (PyThreadState * tstate , PyObject * name , PyObject * spec )
1446
1506
{
1507
+ PyModuleDef * def = NULL ;
1447
1508
PyObject * mod = import_find_extension (tstate , name , name );
1448
1509
if (mod || _PyErr_Occurred (tstate )) {
1449
1510
return mod ;
@@ -1473,20 +1534,32 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
1473
1534
}
1474
1535
1475
1536
if (PyObject_TypeCheck (mod , & PyModuleDef_Type )) {
1476
- return PyModule_FromDefAndSpec ((PyModuleDef * )mod , spec );
1537
+ def = (PyModuleDef * )mod ;
1538
+ assert (!is_singlephase (def ));
1539
+ return PyModule_FromDefAndSpec (def , spec );
1477
1540
}
1478
1541
else {
1479
1542
assert (PyModule_Check (mod ));
1480
- PyModuleDef * def = PyModule_GetDef (mod );
1543
+ def = PyModule_GetDef (mod );
1481
1544
if (def == NULL ) {
1482
1545
return NULL ;
1483
1546
}
1547
+ assert (is_singlephase (def ));
1484
1548
1485
1549
/* Remember pointer to module init function. */
1486
1550
def -> m_base .m_init = p0 ;
1487
1551
1552
+ struct singlephase_global_update singlephase = {0 };
1553
+ // gh-88216: Extensions and def->m_base.m_copy can be updated
1554
+ // when the extension module doesn't support sub-interpreters.
1555
+ if (def -> m_size == -1
1556
+ && !is_core_module (tstate -> interp , name , name ))
1557
+ {
1558
+ singlephase .m_dict = PyModule_GetDict (mod );
1559
+ assert (singlephase .m_dict != NULL );
1560
+ }
1488
1561
if (update_global_state_for_extension (
1489
- tstate , mod , def , name , name ) < 0 )
1562
+ tstate , name , name , def , & singlephase ) < 0 )
1490
1563
{
1491
1564
return NULL ;
1492
1565
}
0 commit comments