@@ -48,6 +48,28 @@ find_thread_state(struct _brc_bucket *bucket, uintptr_t thread_id)
4848 return NULL ;
4949}
5050
51+ // Merge the refcounts of all objects in `stack`, keeping the queue's reference.
52+ static void
53+ merge_queued_refcounts (_PyObjectStack * stack )
54+ {
55+ for (_PyObjectStackChunk * buf = stack -> head ; buf != NULL ; buf = buf -> prev ) {
56+ for (Py_ssize_t i = 0 ; i < buf -> n ; i ++ ) {
57+ _Py_ExplicitMergeRefcount (buf -> objs [i ], 0 );
58+ }
59+ }
60+ }
61+
62+ // Release the queue's reference to each merged object. This may run
63+ // destructors, so the bucket mutex must not be held.
64+ static void
65+ decref_merged_objects (_PyObjectStack * stack )
66+ {
67+ PyObject * ob ;
68+ while ((ob = _PyObjectStack_Pop (stack )) != NULL ) {
69+ Py_DECREF (ob );
70+ }
71+ }
72+
5173// Enqueue an object to be merged by the owning thread. This steals a
5274// reference to the object.
5375void
@@ -93,6 +115,22 @@ _Py_brc_queue_object(PyObject *ob)
93115 return ;
94116 }
95117
118+ if (_PyThreadState_TrySuspendDetached (& tstate -> base )) {
119+ // The owning thread is detached (e.g. blocked on a lock or in a
120+ // system call) and may not run Python code again for a long time,
121+ // so merge its queue on its behalf instead of waiting for it. While
122+ // it is held in the "suspended" state it cannot attach and therefore
123+ // cannot touch ob_ref_local or ob_tid.
124+ _PyObjectStack merged = {0 };
125+ _PyObjectStack_Merge (& merged , & tstate -> brc .objects_to_merge );
126+ merge_queued_refcounts (& merged );
127+ _PyThreadState_ResumeDetached (& tstate -> base );
128+ PyMutex_Unlock (& bucket -> mutex );
129+
130+ decref_merged_objects (& merged );
131+ return ;
132+ }
133+
96134 // Notify owning thread
97135 _Py_set_eval_breaker_bit (& tstate -> base , _PY_EVAL_EXPLICIT_MERGE_BIT );
98136
0 commit comments