Skip to content
Closed
Prev Previous commit
Next Next commit
cleanup
  • Loading branch information
eendebakpt committed May 11, 2025
commit 5ced0a988243a25a91b0cbfda41bc27de6c684f5
1 change: 1 addition & 0 deletions Lib/test/test_free_threading/test_threading_iter_locked.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def work(it):

data = tuple(range(400))
for it in range(number_of_iterations):
print(f'test_iter_locked {it=}')
iter_locked_iterator = iter_locked(non_atomic_iterator(data,))
worker_threads = []
for ii in range(number_of_threads):
Expand Down
19 changes: 7 additions & 12 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -2418,18 +2418,13 @@ def run_last():

class LockedIterTests(unittest.TestCase):

def test_locked_iter(self):
for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)):
for g in (G, I, Ig, S, L, R):
seq = list(g(s))
expected = seq
actual = list(serialize(g(s)))
self.assertEqual(actual, expected)
self.assertRaises(TypeError, serialize, X(s))
self.assertRaises(TypeError, serialize, N(s))
self.assertRaises(ZeroDivisionError, list, serialize(E(s)))
for arg in [1, True, sys]:
self.assertRaises(TypeError, serialize, arg)
def test_iter_locked(self):
for s in ("123", [], [1, 2, 3], tuple(), (1, 2, 3)):
expected = list(s)
actual = list(threading.iter_locked(s))
self.assertEqual(actual, expected)
for arg in [1, None, True, sys]:
self.assertRaises(TypeError, threading.iter_locked, arg)

if __name__ == "__main__":
unittest.main()
3 changes: 2 additions & 1 deletion Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
'Barrier', 'BrokenBarrierError', 'Timer', 'ThreadError',
'setprofile', 'settrace', 'local', 'stack_size',
'excepthook', 'ExceptHookArgs', 'gettrace', 'getprofile',
'setprofile_all_threads','settrace_all_threads']
'setprofile_all_threads','settrace_all_threads', 'iter_locked']

# Rename some stuff so "from threading import *" is safe
_start_joinable_thread = _thread.start_joinable_thread
Expand All @@ -42,6 +42,7 @@
get_ident = _thread.get_ident
_get_main_thread_ident = _thread._get_main_thread_ident
_is_main_interpreter = _thread._is_main_interpreter
iter_locked = _thread.iter_locked
try:
get_native_id = _thread.get_native_id
_HAVE_THREAD_NATIVE_ID = True
Expand Down
42 changes: 20 additions & 22 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ find_state_by_type(PyTypeObject *tp)
return get_thread_state(mod);
}

#define clinic_state() (find_state_by_type(type))
#include "clinic/_threadmodule.c.h"
#undef clinic_state

#ifdef MS_WINDOWS
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
Expand All @@ -70,10 +68,10 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL;

/*[clinic input]
module _thread
class _thread.iter_locked "iter_locked_object *" "clinic_state()->iter_locked_type"
class _thread.iter_locked "iter_locked_object *" "find_state_by_type(type)->iter_locked_type"

[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6c78d729dec7bf7e]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cc495aee1743488d]*/


// _ThreadHandle type
Expand Down Expand Up @@ -770,20 +768,24 @@ _thread_iter_locked_impl(PyTypeObject *type, PyObject *iterable)
if (it == NULL)
return NULL;

iter_locked_object *lz = (iter_locked_object *)type->tp_alloc(type, 0);
lz->it = it;
iter_locked_object *il = (iter_locked_object *)type->tp_alloc(type, 0);
if (il == NULL) {
Py_DECREF(it);
return NULL;
}
il->it = it;

return (PyObject *)lz;
return (PyObject *)il;
}

static void
iter_locked_dealloc(PyObject *op)
{
iter_locked_object *lz = iter_locked_object_CAST(op);
PyTypeObject *tp = Py_TYPE(lz);
PyObject_GC_UnTrack(lz);
Py_XDECREF(lz->it);
tp->tp_free(lz);
iter_locked_object *il = iter_locked_object_CAST(op);
PyTypeObject *tp = Py_TYPE(il);
PyObject_GC_UnTrack(il);
Py_DECREF(il->it);
tp->tp_free(il);
Py_DECREF(tp);
}

Expand All @@ -802,16 +804,12 @@ iter_locked_next(PyObject *op)
iter_locked_object *lz = iter_locked_object_CAST(op);
PyObject *result = NULL;

Py_BEGIN_CRITICAL_SECTION(lz->it); // or lock on op?
Py_BEGIN_CRITICAL_SECTION(op); // lock on op or lz->it?
PyObject *it = lz->it;
if (it != NULL) {
result = PyIter_Next(lz->it);
if (result == NULL) {
/* Note: StopIteration is already cleared by PyIter_Next() */
if (PyErr_Occurred())
return NULL;
Py_CLEAR(lz->it);
}
result = PyIter_Next(it);
if (result == NULL) {
/* Note: StopIteration is already cleared by PyIter_Next() */
/* If PyErr_Occurred() we will also return NULL*/
}
Py_END_CRITICAL_SECTION();
return result;
Expand Down Expand Up @@ -2854,7 +2852,7 @@ thread_module_traverse(PyObject *module, visitproc visit, void *arg)
{
thread_module_state *state = get_thread_state(module);
Py_VISIT(state->excepthook_type);
Py_CLEAR(state->iter_locked_type);
Py_VISIT(state->iter_locked_type);
Py_VISIT(state->lock_type);
Py_VISIT(state->local_type);
Py_VISIT(state->local_dummy_type);
Expand Down
4 changes: 2 additions & 2 deletions Modules/clinic/_threadmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.