8000 Fixes · python/cpython@7815db2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7815db2

Browse files
committed
Fixes
1 parent ed0feaa commit 7815db2

File tree

4 files changed

+61
-63
lines changed

4 files changed

+61
-63
lines changed

‎< 8000 !-- -->Lib/test/test_free_threading/test_dict.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class MyDict(dict): pass
152152
THREAD_COUNT = 10
153153

154154
def writer_func(l):
155-
for i in range(100000):
155+
for i in range(1000):
156156
if cyclic:
157157
other_d = {}
158158
d = MyDict({"foo": 100})

Objects/dictobject.c

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7113,7 +7113,8 @@ try_set_dict_inline_only_or_other_dict(PyObject *obj, PyObject *new_dict, PyDict
71137113
(PyDictObject *)Py_XNewRef(new_dict));
71147114
replaced = true;
71157115
goto exit_lock;
7116-
} else {
711 8000 6+
}
7117+
else {
71177118
// We have inline values, we need to lock the dict and the object
71187119
// at the same time to safely dematerialize them. To do that while releasing
71197120
// the object lock we need a strong reference to the current dictionary.
@@ -7129,39 +7130,38 @@ try_set_dict_inline_only_or_other_dict(PyObject *obj, PyObject *new_dict, PyDict
71297130
// and replaced it with another dictionary though.
71307131
static int
71317132
replace_dict_probably_inline_materialized(PyObject *obj, PyDictObject *inline_dict,
7132-
PyObject *new_dict, bool clear,
7133-
PyDictObject **replaced_dict)
7133+
PyDictObject *cur_dict, PyObject *new_dict)
71347134
{
7135-
// But we could have had another thread race in after we released
7136-
// the object lock
7137-
int err = 0;
7138-
*replaced_dict = _PyObject_GetManagedDict(obj);
7139-
assert(FT_ATOMIC_LOAD_PTR_RELAXED(inline_dict->ma_values) == _PyObject_InlineValues(obj));
7135+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
7136+
7137+
if (cur_dict == inline_dict) {
7138+
assert(FT_ATOMIC_LOAD_PTR_RELAXED(inline_dict->ma_values) == _PyObject_InlineValues(obj));
71407139

7141-
if (*replaced_dict == inline_dict) {
7142-
err = _PyDict_DetachFromObject(inline_dict, obj);
7140+
int err = _PyDict_DetachFromObject(inline_dict, obj);
71437141
if (err != 0) {
71447142
assert(new_dict == NULL);
71457143
return err;
71467144
}
7147-
// We incref'd the inline dict and the object owns a ref.
7148-
// Clear the object's reference, we'll clear the local
7149-
// reference after releasing the lock.
7150-
if (clear) {
7151-
Py_XDECREF((PyObject *)*replaced_dict);
7152-
} else {
7153-
_PyObject_XDecRefDelayed((PyObject *)*replaced_dict);
7154-
}
7155-
*replaced_dict = NULL;
71567145
}
71577146

71587147
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
7159-
(PyDictObject *)Py_XNewRef(new_dict));
7160-
return err;
7148+
(PyDictObject *)Py_XNewRef(new_dict));
7149+
return 0;
71617150
}
71627151

71637152
#endif
71647153

7154+
static void
7155+
decref_maybe_delay(PyObject *obj, bool delay)
7156+
{
7157+
if (delay) {
7158+
_PyObject_XDecRefDelayed(obj);
7159+
}
7160+
else {
7161+
Py_XDECREF(obj);
7162+
}
7163+
}
7164+
71657165
static int
71667166
set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
71677167
{
@@ -7177,32 +7177,37 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
71777177
// values. We need to lock both the object and the dict at the
71787178
// same time to safely replace it. We can't merely lock the dictionary
71797179
// while the object is locked because it could suspend the object lock.
7180-
PyDictObject *replaced_dict;
7180+
PyDictObject *cur_dict;
71817181

71827182
assert(prev_dict != NULL);
71837183
Py_BEGIN_CRITICAL_SECTION2(obj, prev_dict);
71847184

7185-
err = replace_dict_probably_inline_materialized(obj, prev_dict, new_dict,
7186-
clear, &replaced_dict);
7185+
// We could have had another thread race in between the call to
7186+
// try_set_dict_inline_only_or_other_dict where we locked the object
7187+
// and when we unlocked and re-locked the dictionary.
7188+
cur_dict = _PyObject_GetManagedDict(obj);
7189+
7190+
err = replace_dict_probably_inline_materialized(obj, prev_dict,
7191+
cur_dict, new_dict);
71877192

71887193
Py_END_CRITICAL_SECTION2();
71897194

7190-
Py_DECREF(prev_dict);
7195+
// Decref for the dictionary we incref'd in try_set_dict_inline_only_or_other_dict
7196+
// while the object was locked
7197+
decref_maybe_delay((PyObject *)prev_dict,
7198+
!clear && prev_dict != cur_dict);
71917199
if (err != 0) {
71927200
return err;
71937201
}
7194-
prev_dict = replaced_dict;
7202+
7203+
prev_dict = cur_dict;
71957204
}
71967205

71977206
if (prev_dict != NULL) {
7198-
// Readers from the old dictionary use a borrowed reference. We need
7199-
// to set the decref the dict at the next safe point.
7200-
if (clear) {
7201-
Py_XDECREF((PyObject *)prev_dict);
7202-
} else {
7203-
_PyObject_XDecRefDelayed((PyObject *)prev_dict);
7204-
}
7207+
// decref for the dictionary that we replaced
7208+
decref_maybe_delay((PyObject *)prev_dict, !clear);
72057209
}
7210+
72067211
return 0;
72077212
#else
72087213
PyDictObject *dict = _PyObject_GetManagedDict(obj);
@@ -7230,11 +7235,7 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
72307235
(PyDictObject *)Py_XNewRef(new_dict));
72317236

72327237
Py_END_CRITICAL_SECTION();
7233-
if (clear) {
7234-
Py_XDECREF((PyObject *)dict);
7235-
} else {
7236-
_PyObject_XDecRefDelayed((PyObject *)dict);
7237-
}
7238+
decref_maybe_delay((PyObject *)dict, !clear);
72387239
}
72397240
assert(_PyObject_InlineValuesConsistencyCheck(obj));
72407241
return err;

Objects/obmalloc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ static void
10961096
free_work_item(uintptr_t ptr, delayed_dealloc_cb cb, void *state)
10971097
{
10981098
if (ptr & 0x01) {
1099-
PyObject *obj = (PyObject*)(char *)(ptr - 1);
1099+
PyObject *obj = (PyObject *)(ptr - 1);
11001100
#ifdef Py_GIL_DISABLED
11011101
if (cb == NULL) {
11021102
assert(!_PyInterpreterState_GET()->stoptheworld.world_stopped);

Python/gc_free_threading.c

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,23 @@ gc_visit_thread_stacks(PyInterpreterState *interp)
393393
HEAD_UNLOCK(&_PyRuntime);
394394
}
395395

396+
static void
397+
queue_untracked_obj_decref(PyObject *op, struct collection_state *state)
398+
{
399+
if (!_PyObject_GC_IS_TRACKED(op)) {
400+
// GC objects with zero refcount are handled subsequently by the
401+
// GC as if they were cyclic trash, but we have to handle dead
402+
// non-GC objects here. Add one to the refcount so that we can
403+
// decref and deallocate the object once we start the world again.
404+
op->ob_ref_shared += (1 << _Py_REF_SHARED_SHIFT);
405+
#ifdef Py_REF_DEBUG
406+
_Py_IncRefTotal(_PyThreadState_GET());
407+
#endif
408+
worklist_push(&state->objs_to_decref, op);
409+
}
410+
411+
}
412+
396413
static void
397414
merge_queued_objects(_PyThreadStateImpl *tstate, struct collection_state *state)
398415
{
@@ -404,36 +421,16 @@ merge_queued_objects(_PyThreadStateImpl *tstate, struct collection_state *state)
404421
// Subtract one when merging because the queue had a reference.
405422
Py_ssize_t refcount = merge_refcount(op, -1);
406423

407-
if (!_PyObject_GC_IS_TRACKED(op) && refcount == 0) {
408-
// GC objects with zero refcount are handled subsequently by the
409-
// GC as if they were cyclic trash, but we have to handle dead
410-
// non-GC objects here. Add one to the refcount so that we can
411-
// decref and deallocate the object once we start the world again.
412-
op->ob_ref_shared += (1 << _Py_REF_SHARED_SHIFT);
413-
#ifdef Py_REF_DEBUG
414-
_Py_IncRefTotal(_PyThreadState_GET());
415-
#endif
416-
worklist_push(&state->objs_to_decref, op);
424+
if (refcount == 0) {
425+
queue_untracked_obj_decref(op, state);
417426
}
418427
}
419428
}
420429

421430
static void
422431
queue_freed_object(PyObject *obj, void *arg)
423432
{
424-
struct collection_state *state = (struct collection_state *)arg;
425-
426-
// GC objects with zero refcount are handled subsequently by the
427-
// GC as if they were cyclic trash, but we have to handle dead
428-
// non-GC objects here. Add one to the refcount so that we can
429-
// decref and deallocate the object once we start the world again.
430-
if (!_PyObject_GC_IS_TRACKED(obj)) {
431-
obj->ob_ref_shared += (1 << _Py_REF_SHARED_SHIFT);
432-
#ifdef Py_REF_DEBUG
433-
_Py_IncRefTotal(_PyThreadState_GET());
434-
#endif
435-
worklist_push(&state->objs_to_decref, obj);
436-
}
433+
queue_untracked_obj_decref(obj, arg);
437434
}
438435

439436
static void

0 commit comments

Comments
 (0)
0