@@ -113,17 +113,18 @@ As a consequence of this, split keys have a maximum size of 16. |
113 | 113 | #define PyDict_MINSIZE 8 |
114 | 114 |
|
115 | 115 | #include "Python.h" |
116 | | -#include "pycore_bitutils.h" // _Py_bit_length |
117 | | -#include "pycore_call.h" // _PyObject_CallNoArgs() |
118 | | -#include "pycore_ceval.h" // _PyEval_GetBuiltin() |
119 | | -#include "pycore_code.h" // stats |
120 | | -#include "pycore_dict.h" // export _PyDict_SizeOf() |
121 | | -#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() |
122 | | -#include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats() |
123 | | -#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() |
124 | | -#include "pycore_pystate.h" // _PyThreadState_GET() |
125 | | -#include "pycore_setobject.h" // _PySet_NextEntry() |
126 | | -#include "stringlib/eq.h" // unicode_eq() |
| 116 | +#include "pycore_bitutils.h" // _Py_bit_length |
| 117 | +#include "pycore_call.h" // _PyObject_CallNoArgs() |
| 118 | +#include "pycore_ceval.h" // _PyEval_GetBuiltin() |
| 119 | +#include "pycore_code.h" // stats |
| 120 | +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION |
| 121 | +#include "pycore_dict.h" // export _PyDict_SizeOf() |
| 122 | +#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() |
| 123 | +#include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats() |
| 124 | +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() |
| 125 | +#include "pycore_pystate.h" // _PyThreadState_GET() |
| 126 | +#include "pycore_setobject.h" // _PySet_NextEntry() |
| 127 | +#include "stringlib/eq.h" // unicode_eq() |
127 | 128 |
|
128 | 129 | #include <stdbool.h> |
129 | 130 |
|
@@ -5540,6 +5541,9 @@ make_dict_from_instance_attributes(PyInterpreterState *interp, |
5540 | 5541 | track += _PyObject_GC_MAY_BE_TRACKED(val); |
5541 | 5542 | } |
5542 | 5543 | } |
| 5544 | + // free-threading TODO: We'll need to mark the values to be freed via qsbr |
| 5545 | + // so that a resize doesn't free them immediately while another thread |
| 5546 | + // still has a reference to the old values. |
5543 | 5547 | PyObject *res = new_dict(interp, keys, values, used, 0); |
5544 | 5548 | if (track && res) { |
5545 | 5549 | _PyObject_GC_TRACK(res); |
@@ -5572,19 +5576,26 @@ _PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv) |
5572 | 5576 | return false; |
5573 | 5577 | } |
5574 | 5578 | assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_HEAPTYPE)); |
| 5579 | + bool success = true; |
| 5580 | + Py_BEGIN_CRITICAL_SECTION(dict); |
5575 | 5581 | if (dict->ma_keys != CACHED_KEYS(Py_TYPE(obj)) || Py_REFCNT(dict) != 1) { |
5576 | | - return false; |
| 5582 | + success = false; |
| 5583 | + goto exit; |
5577 | 5584 | } |
5578 | 5585 | assert(dict->ma_values); |
5579 | 5586 | // We have an opportunity to do something *really* cool: dematerialize it! |
5580 | 5587 | _PyDictKeys_DecRef(dict->ma_keys); |
5581 | 5588 | _PyDictOrValues_SetValues(dorv, dict->ma_values); |
5582 | 5589 | OBJECT_STAT_INC(dict_dematerialized); |
| 5590 | + // Lock free readers will need to contend with NULL values here, but |
| 5591 | + // because we locked the dict anyone else will be safe. |
5583 | 5592 | // Don't try this at home, kids: |
5584 | 5593 | dict->ma_keys = NULL; |
5585 | 5594 | dict->ma_values = NULL; |
5586 | 5595 | Py_DECREF(dict); |
5587 | | - return true; |
| 5596 | +exit: |
| 5597 | + Py_END_CRITICAL_SECTION(); |
| 5598 | + return success; |
5588 | 5599 | } |
5589 | 5600 |
|
5590 | 5601 | int |
|