Skip to content

Commit 58c83fc

Browse files
committed
Remove comments and rebase to head.
1 parent 78ffcac commit 58c83fc

2 files changed

Lines changed: 23 additions & 80 deletions

File tree

‎Lib/test/test_asyncio/test_free_threading.py‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,6 @@ def runner():
117117
self.assertIsNone(wr())
118118

119119
def test_lingering_task_different_thread_finalized(self) -> None:
120-
# When a thread exits with pending tasks, PyThreadState_Clear() moves
121-
# them onto interp->asyncio_tasks_head. Unregistering those tasks from
122-
# later threads (which may reuse the exited thread's OS thread ID) or
123-
# during PyThreadState_Clear() while all_tasks() walks the task lists
124-
# must not corrupt the shared list.
125120
tls = threading.local()
126121

127122
async def never_finishes():
@@ -138,14 +133,12 @@ def make_pending_tasks(loop, count):
138133
def worker(batches):
139134
loop = asyncio.new_event_loop()
140135
loop.set_task_factory(self.factory)
141-
# Tasks held in thread-local storage are cleared by PyThreadState_Clear
142-
# after llist_concat moves tstate->asyncio_tasks_head to interp.
136+
143137
tls_tasks = make_pending_tasks(loop, 8)
144138
for task in tls_tasks:
145139
task.get_coro().close()
146140
tls.tasks = tls_tasks
147-
# Tasks returned in batches stay alive until a subsequent worker
148-
# thread (often reusing this thread's OS thread ID) unregisters them.
141+
149142
batches.append((loop, make_pending_tasks(loop, 8)))
150143

151144
stop = threading.Event()

‎Modules/_asynciomodule.c‎

Lines changed: 21 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ typedef struct TaskObj {
7171
// unique id (PyThreadState.id) of the thread state that created this
7272
// task, see unregister_task()
7373
uint64_t task_tstate_id;
74+
7475
#endif
7576
} TaskObj;
7677

@@ -1796,9 +1797,10 @@ FutureIter_dealloc(PyObject *it)
17961797
}
17971798

17981799
static PySendResult
1799-
FutureIter_am_send_lock_held(FutureObj *fut, PyObject **result)
1800+
FutureIter_am_send_lock_held(futureiterobject *it, PyObject **result)
18001801
{
18011802
PyObject *res;
1803+
FutureObj *fut = it->future;
18021804
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut);
18031805

18041806
*result = NULL;
@@ -1829,24 +1831,11 @@ FutureIter_am_send(PyObject *op,
18291831
PyObject **result)
18301832
{
18311833
futureiterobject *it = (futureiterobject*)op;
1832-
1833-
FutureObj *fut;
1834-
Py_BEGIN_CRITICAL_SECTION(op);
1835-
fut = (FutureObj*)Py_XNewRef(it->future);
1836-
Py_END_CRITICAL_SECTION();
1837-
1838-
if (fut == NULL) {
1839-
PyErr_SetNone(PyExc_StopIteration);
1840-
*result = NULL;
1841-
return PYGEN_ERROR;
1842-
}
1843-
18441834
/* arg is unused, see the comment on FutureIter_send for clarification */
18451835
PySendResult res;
1846-
Py_BEGIN_CRITICAL_SECTION(fut);
1847-
res = FutureIter_am_send_lock_held(fut, result);
1836+
Py_BEGIN_CRITICAL_SECTION(it->future);
1837+
res = FutureIter_am_send_lock_held(it, result);
18481838
Py_END_CRITICAL_SECTION();
1849-
Py_DECREF(fut);
18501839
return res;
18511840
}
18521841

@@ -1940,9 +1929,7 @@ FutureIter_throw(PyObject *op, PyObject *const *args, Py_ssize_t nargs)
19401929
goto fail;
19411930
}
19421931

1943-
Py_BEGIN_CRITICAL_SECTION(self);
19441932
Py_CLEAR(self->future);
1945-
Py_END_CRITICAL_SECTION();
19461933

19471934
PyErr_Restore(type, val, tb);
19481935

@@ -1955,16 +1942,6 @@ FutureIter_throw(PyObject *op, PyObject *const *args, Py_ssize_t nargs)
19551942
return NULL;
19561943
}
19571944

1958-
static PyObject *
1959-
FutureIter_close(PyObject *self, PyObject *arg)
1960-
{
1961-
futureiterobject *it = (futureiterobject*)self;
1962-
Py_BEGIN_CRITICAL_SECTION(self);
1963-
Py_CLEAR(it->future);
1964-
Py_END_CRITICAL_SECTION();
1965-
Py_RETURN_NONE;
1966-
}
1967-
19681945
static int
19691946
FutureIter_clear(PyObject *op)
19701947
{
@@ -1973,6 +1950,13 @@ FutureIter_clear(PyObject *op)
19731950
return 0;
19741951
}
19751952

1953+
static PyObject *
1954+
FutureIter_close(PyObject *self, PyObject *arg)
1955+
{
1956+
(void)FutureIter_clear(self);
1957+
Py_RETURN_NONE;
1958+
}
1959+
19761960
static int
19771961
FutureIter_traverse(PyObject *op, visitproc visit, void *arg)
19781962
{
@@ -1989,22 +1973,6 @@ static PyMethodDef FutureIter_methods[] = {
19891973
{NULL, NULL} /* Sentinel */
19901974
};
19911975

1992-
static PyObject *
1993-
FutureIter_get_fi_future(PyObject *op, void *Py_UNUSED(closure))
1994-
{
1995-
futureiterobject *it = (futureiterobject*)op;
1996-
PyObject *fut;
1997-
Py_BEGIN_CRITICAL_SECTION(op);
1998-
fut = it->future ? Py_NewRef(it->future) : Py_None;
1999-
Py_END_CRITICAL_SECTION();
2000-
return fut;
2001-
}
2002-
2003-
static PyGetSetDef FutureIter_getsetlist[] = {
2004-
{"fi_future", FutureIter_get_fi_future, NULL, NULL},
2005-
{NULL} /* Sentinel */
2006-
};
2007-
20081976
static PyType_Slot FutureIter_slots[] = {
20091977
{Py_tp_dealloc, FutureIter_dealloc},
20101978
{Py_tp_getattro, PyObject_GenericGetAttr},
@@ -2013,7 +1981,6 @@ static PyType_Slot FutureIter_slots[] = {
20131981
{Py_tp_iter, PyObject_SelfIter},
20141982
{Py_tp_iternext, FutureIter_iternext},
20151983
{Py_tp_methods, FutureIter_methods},
2016-
{Py_tp_getset, FutureIter_getsetlist},
20171984

20181985
// async methods
20191986
{Py_am_send, FutureIter_am_send},
@@ -2186,9 +2153,6 @@ register_task(_PyThreadStateImpl *ts, TaskObj *task)
21862153
assert(task->task_node.prev != NULL);
21872154
return;
21882155
}
2189-
#ifdef Py_GIL_DISABLED
2190-
task->task_tstate_id = ts->base.id;
2191-
#endif
21922156
struct llist_node *head = &ts->asyncio_tasks_head;
21932157
llist_insert_tail(head, &task->task_node);
21942158
}
@@ -2208,19 +2172,15 @@ static void
22082172
unregister_task(TaskObj *task)
22092173
{
22102174
#ifdef Py_GIL_DISABLED
2211-
PyThreadState *tstate = _PyThreadState_GET();
2212-
// The lock-free path is only safe while the task is still linked into the
2213-
// current thread's own list. That holds if this thread state created the
2214-
// task and has not started PyThreadState_Clear(), which moves lingering
2215-
// tasks onto the shared interp->asyncio_tasks_head list. Compare thread
2216-
// state ids rather than _Py_ThreadId(), which is reused by threads that
2217-
// start after the creating thread has exited.
2218-
if (task->task_tstate_id == tstate->id && !tstate->_status.finalizing) {
2175+
// check if we are in the same thread
2176+
// if so, we can avoid locking
2177+
if (task->task_tid == _Py_ThreadId()) {
22192178
unregister_task_safe(task);
22202179
}
22212180
else {
2222-
// the task may be linked into another thread's list or into the
2223-
// interpreter's list: stop the world then check and remove the task
2181+
// we are in a different thread
2182+
// stop the world then check and remove the task
2183+
PyThreadState *tstate = _PyThreadState_GET();
22242184
_PyEval_StopTheWorld(tstate->interp);
22252185
unregister_task_safe(task);
22262186
_PyEval_StartTheWorld(tstate->interp);
@@ -2349,9 +2309,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop,
23492309

23502310
Py_CLEAR(self->task_fut_waiter);
23512311
#ifdef Py_GIL_DISABLED
2352-
if (self->task_node.next == NULL) {
2353-
self->task_tstate_id = _PyThreadState_GET()->id;
2354-
}
2312+
self->task_tid = _Py_ThreadId();
23552313
#endif
23562314
self->task_must_cancel = 0;
23572315
self->task_log_destroy_pending = 1;
@@ -2813,12 +2771,7 @@ _asyncio_Task_get_name_impl(TaskObj *self)
28132771
{
28142772
if (self->task_name) {
28152773
if (PyLong_CheckExact(self->task_name)) {
2816-
// Formatting can run arbitrary code (e.g. a GC finalizer that logs
2817-
// and re-enters get_name()), which may replace self->task_name.
2818-
// Hold a strong reference to the counter while formatting it.
2819-
PyObject *counter = Py_NewRef(self->task_name);
2820-
PyObject *name = PyUnicode_FromFormat("Task-%S", counter);
2821-
Py_DECREF(counter);
2774+
PyObject *name = PyUnicode_FromFormat("Task-%S", self->task_name);
28222775
if (name == NULL) {
28232776
return NULL;
28242777
}
@@ -3997,10 +3950,7 @@ add_tasks_llist(struct llist_node *head, PyListObject *tasks)
39973950
struct llist_node *node;
39983951
llist_for_each_safe(node, head) {
39993952
TaskObj *task = llist_data(node, TaskObj, task_node);
4000-
// The task may already be done but not yet unlinked: its state is set
4001-
// before unregister_task() removes it, and that removal may be
4002-
// waiting for a stop-the-world pause. add_one_task() filters out
4003-
// tasks that are done.
3953+
assert(task->task_state == STATE_PENDING);
40043954
// The linked list holds borrowed references to task
40053955
// as such it is possible that the task is concurrently
40063956
// deallocated while added to this list.

0 commit comments

Comments
 (0)