Skip to content

Commit f621a0c

Browse files
committed
Making freezable types a weakset
Signed-off-by: Matthew A Johnson <matjoh@microsoft.com>
1 parent 2fab5cd commit f621a0c

3 files changed

Lines changed: 79 additions & 11 deletions

File tree

Include/internal/pycore_immutability.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct _Py_immutability_state {
1212
PyObject *module_locks;
1313
PyObject *blocking_on;
1414
PyObject *freezable_types;
15+
PyObject *destroy_cb;
1516
};
1617

1718
#ifdef __cplusplus

Python/immutability.c

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,38 @@
77
#include "pycore_immutability.h"
88

99

10+
static PyObject *
11+
_destroy(PyObject* set, PyObject *objweakref)
12+
{
13+
Py_INCREF(set);
14+
if (PySet_Discard(set, objweakref) < 0) {
15+
Py_DECREF(set);
16+
return NULL;
17+
}
18+
Py_DECREF(set);
19+
20+
Py_RETURN_NONE;
21+
}
22+
23+
static PyMethodDef _destroy_def = {
24+
"_destroy", (PyCFunction) _destroy, METH_O
25+
};
1026

11-
static int init_state(struct _Py_immutability_state *state)
27+
static PyObject *
28+
type_weakref(struct _Py_immutability_state *state, PyObject *obj)
29+
{
30+
if(state->destroy_cb == NULL){
31+
state->destroy_cb = PyCFunction_NewEx(&_destroy_def, state->freezable_types, NULL);
32+
if (state->destroy_cb == NULL) {
33+
return NULL;
34+
}
35+
}
36+
37+
return PyWeakref_NewRef(obj, state->destroy_cb);
38+
}
39+
40+
static
41+
int init_state(struct _Py_immutability_state *state)
1242
{
1343
PyObject* frozen_importlib = NULL;
1444

@@ -40,6 +70,8 @@ static int init_state(struct _Py_immutability_state *state)
4070
return -1;
4171
}
4272

73+
Py_DECREF(frozen_importlib);
74+
4375
return 0;
4476
}
4577

@@ -279,7 +311,8 @@ static int freeze_visit(PyObject* obj, void* frontier)
279311
return 0;
280312
}
281313

282-
static bool is_freezable_builtin(PyTypeObject *type)
314+
static bool
315+
is_freezable_builtin(PyTypeObject *type)
283316
{
284317
if(type == &PyType_Type ||
285318
type == &PyBaseObject_Type ||
@@ -319,28 +352,51 @@ static bool is_freezable_builtin(PyTypeObject *type)
319352
return false;
320353
}
321354

355+
static int
356+
is_explicitly_freezable(struct _Py_immutability_state *state, PyObject *obj)
357+
{
358+
int result = 0;
359+
PyObject *ref = type_weakref(state, (PyObject *)obj->ob_type);
360+
if(ref == NULL){
361+
return -1;
362+
}
363+
364+
result = PySet_Contains(state->freezable_types, ref);
365+
Py_DECREF(ref);
366+
return result;
367+
}
322368

323369
typedef enum {
324370
VALID_BUILTIN,
325371
VALID_EXPLICIT,
326372
VALID_IMPLICIT,
327373
INVALID_NOT_FREEZABLE,
328-
INVALID_C_EXTENSIONS
374+
INVALID_C_EXTENSIONS,
375+
ERROR
329376
} FreezableCheck;
330377

331378

332-
static FreezableCheck check_freezable(PyObject* obj, PyObject* freezable_types)
379+
static FreezableCheck check_freezable(struct _Py_immutability_state *state, PyObject* obj)
333380
{
334-
if(PyObject_IsInstance(obj, (PyObject*)&_PyNotFreezable_Type)){
381+
int result = 0;
382+
383+
result = PyObject_IsInstance(obj, (PyObject*)&_PyNotFreezable_Type);
384+
if(result == -1){
385+
return ERROR;
386+
}
387+
else if(result == 1){
335388
return INVALID_NOT_FREEZABLE;
336389
}
337390

338391
if(is_freezable_builtin(obj->ob_type)){
339392
return VALID_BUILTIN;
340393
}
341394

342-
PyObject* type = (PyObject*)obj->ob_type;
343-
if(PySet_Contains(freezable_types, type) == 1){
395+
result = is_explicitly_freezable(state, obj);
396+
if(result == -1){
397+
return ERROR;
398+
}
399+
else if(result == 1){
344400
return VALID_EXPLICIT;
345401
}
346402

@@ -360,7 +416,7 @@ int _PyImmutability_IsFreezable(PyObject *obj)
360416
return 0;
361417
}
362418

363-
switch(check_freezable(obj, state->freezable_types))
419+
switch(check_freezable(state, obj))
364420
{
365421
case VALID_BUILTIN:
366422
case VALID_EXPLICIT:
@@ -369,6 +425,8 @@ int _PyImmutability_IsFreezable(PyObject *obj)
369425
case INVALID_NOT_FREEZABLE:
370426
case INVALID_C_EXTENSIONS:
371427
return 0;
428+
case ERROR:
429+
return -1;
372430
}
373431

374432
PyErr_SetString(PyExc_RuntimeError, "Unknown state");
@@ -377,17 +435,22 @@ int _PyImmutability_IsFreezable(PyObject *obj)
377435

378436
int _PyImmutability_RegisterFreezable(PyTypeObject* tp)
379437
{
438+
PyObject *ref;
439+
int result;
380440
struct _Py_immutability_state *state = get_immutable_state();
381441
if(state == NULL){
382442
PyErr_SetString(PyExc_RuntimeError, "Failed to initialize immutability state");
383443
return -1;
384444
}
385445

386-
if(PySet_Add(state->freezable_types, _PyObject_CAST(tp)) == -1){
446+
ref = type_weakref(state, (PyObject*)tp);
447+
if(ref == NULL){
387448
return -1;
388449
}
389450

390-
return 0;
451+
result = PySet_Add(state->freezable_types, ref);
452+
Py_DECREF(ref);
453+
return result;
391454
}
392455

393456

@@ -424,7 +487,7 @@ int _PyImmutability_Freeze(PyObject* obj)
424487
continue;
425488
}
426489

427-
check = check_freezable(item, state->freezable_types);
490+
check = check_freezable(state, item);
428491
switch(check){
429492
case INVALID_NOT_FREEZABLE:
430493
PyErr_SetString(PyExc_TypeError, "Invalid freeze request: instance of NotFreezable");
@@ -441,6 +504,9 @@ int _PyImmutability_Freeze(PyObject* obj)
441504
case VALID_EXPLICIT:
442505
case VALID_IMPLICIT:
443506
break;
507+
508+
case ERROR:
509+
goto error;
444510

445511
default:
446512
PyErr_SetString(PyExc_RuntimeError, "Unknown freezable check value");

Python/pystate.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
860860
Py_CLEAR(interp->immutability.module_locks);
861861
Py_CLEAR(interp->immutability.blocking_on);
862862
Py_CLEAR(interp->immutability.freezable_types);
863+
Py_CLEAR(interp->immutability.destroy_cb);
863864

864865
Py_CLEAR(interp->sysdict_copy);
865866
Py_CLEAR(interp->builtins_copy);

0 commit comments

Comments
 (0)