diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 28bf04651bd1737..fd6be6a9b67a031 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -160,7 +160,7 @@ There are three ways strings and buffers can be converted to C: ``w*`` (read-write :term:`bytes-like object`) [Py_buffer] This format accepts any object which implements the read-write buffer interface. It fills a :c:type:`Py_buffer` structure provided by the caller. - The buffer may contain embedded null bytes. The caller have to call + The buffer may contain embedded null bytes. The caller has to call :c:func:`PyBuffer_Release` when it is done with the buffer. ``es`` (:class:`str`) [const char \*encoding, char \*\*buffer] diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 29d5cd8d5391a39..6626f628fcc47f5 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -102,6 +102,10 @@ Module Objects Return a pointer to the :c:type:`PyModuleDef` struct from which the module was created, or ``NULL`` if the module wasn't created from a definition. + On error, return ``NULL`` with an exception set. + Use :c:func:`PyErr_Occurred` to tell this case apart from a mising + :c:type:`!PyModuleDef`. + .. c:function:: PyObject* PyModule_GetFilenameObject(PyObject *module) diff --git a/Doc/library/xmlrpc.client.rst b/Doc/library/xmlrpc.client.rst index 547cb50be78a782..a21c7d3e4e3ad52 100644 --- a/Doc/library/xmlrpc.client.rst +++ b/Doc/library/xmlrpc.client.rst @@ -524,7 +524,7 @@ Convenience Functions .. function:: dumps(params, methodname=None, methodresponse=None, encoding=None, allow_none=False) - Convert *params* into an XML-RPC request. or into a response if *methodresponse* + Convert *params* into an XML-RPC request, or into a response if *methodresponse* is true. *params* can be either a tuple of arguments or an instance of the :exc:`Fault` exception class. If *methodresponse* is true, only a single value can be returned, meaning that *params* must be of length 1. *encoding*, if diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 034cd5f7f9e89f3..fe3e391a7f5ba14 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1385,6 +1385,22 @@ def test_map_strict(self): self.assertRaises(ValueError, tuple, map(pack, (1, 2), (1, 2), 'abc', strict=True)) + # gh-140517: Testing refleaks with mortal objects. + t1 = (None, object()) + t2 = (object(), object()) + t3 = (object(),) + + self.assertRaises(ValueError, tuple, + map(pack, t1, 'a', strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, t1, t2, 'a', strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, t1, t2, t3, strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, 'a', t1, strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, 'a', t2, t3, strict=True)) + def test_map_strict_iterators(self): x = iter(range(5)) y = [0] diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 99408e60721c602..1f58b1fb3506c6c 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -4079,30 +4079,44 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) return NULL; } - PyInterpreterState *interp = PyInterpreterState_Get(); - // Stop the world and traverse the per-thread linked list - // of asyncio tasks for every thread, as well as the - // interpreter's linked list, and add them to `tasks`. - // The interpreter linked list is used for any lingering tasks - // whose thread state has been deallocated while the task was - // still alive. This can happen if a task is referenced by - // a different thread, in which case the task is moved to - // the interpreter's linked list from the thread's linked - // list before deallocation. See PyThreadState_Clear. - // - // The stop-the-world pause is required so that no thread - // modifies its linked list while being iterated here - // in parallel. This design allows for lock-free - // register_task/unregister_task for loops running in parallel - // in different threads (the general case). - _PyEval_StopTheWorld(interp); - int ret = add_tasks_interp(interp, (PyListObject *)tasks); - _PyEval_StartTheWorld(interp); - if (ret < 0) { - // call any escaping calls after starting the world to avoid any deadlocks. - Py_DECREF(tasks); - Py_DECREF(loop); - return NULL; + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + if (ts->asyncio_running_loop == loop) { + // Fast path for the current running loop of current thread + // no locking or stop the world pause is required + struct llist_node *head = &ts->asyncio_tasks_head; + if (add_tasks_llist(head, (PyListObject *)tasks) < 0) { + Py_DECREF(tasks); + Py_DECREF(loop); + return NULL; + } + } + else { + // Slow path for loop running in different thread + PyInterpreterState *interp = ts->base.interp; + // Stop the world and traverse the per-thread linked list + // of asyncio tasks for every thread, as well as the + // interpreter's linked list, and add them to `tasks`. + // The interpreter linked list is used for any lingering tasks + // whose thread state has been deallocated while the task was + // still alive. This can happen if a task is referenced by + // a different thread, in which case the task is moved to + // the interpreter's linked list from the thread's linked + // list before deallocation. See PyThreadState_Clear. + // + // The stop-the-world pause is required so that no thread + // modifies its linked list while being iterated here + // in parallel. This design allows for lock-free + // register_task/unregister_task for loops running in parallel + // in different threads (the general case). + _PyEval_StopTheWorld(interp); + int ret = add_tasks_interp(interp, (PyListObject *)tasks); + _PyEval_StartTheWorld(interp); + if (ret < 0) { + // call any escaping calls after starting the world to avoid any deadlocks. + Py_DECREF(tasks); + Py_DECREF(loop); + return NULL; + } } // All the tasks are now in the list, now filter the tasks which are done diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 64249177eec5f20..f6fadd936bb8ff4 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1501,34 +1501,27 @@ map_next(PyObject *self) } Py_ssize_t nargs = 0; - for (i=0; i < niters; i++) { + for (i = 0; i < niters; i++) { PyObject *it = PyTuple_GET_ITEM(lz->iters, i); PyObject *val = Py_TYPE(it)->tp_iternext(it); if (val == NULL) { if (lz->strict) { goto check; } - goto exit; + goto exit_no_result; } stack[i] = val; nargs++; } result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL); + goto exit; -exit: - for (i=0; i < nargs; i++) { - Py_DECREF(stack[i]); - } - if (stack != small_stack) { - PyMem_Free(stack); - } - return result; check: if (PyErr_Occurred()) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { // next() on argument i raised an exception (not StopIteration) - return NULL; + goto exit_no_result; } PyErr_Clear(); } @@ -1536,9 +1529,10 @@ map_next(PyObject *self) // ValueError: map() argument 2 is shorter than argument 1 // ValueError: map() argument 3 is shorter than arguments 1-2 const char* plural = i == 1 ? " " : "s 1-"; - return PyErr_Format(PyExc_ValueError, - "map() argument %d is shorter than argument%s%d", - i + 1, plural, i); + PyErr_Format(PyExc_ValueError, + "map() argument %d is shorter than argument%s%d", + i + 1, plural, i); + goto exit_no_result; } for (i = 1; i < niters; i++) { PyObject *it = PyTuple_GET_ITEM(lz->iters, i); @@ -1546,21 +1540,33 @@ map_next(PyObject *self) if (val) { Py_DECREF(val); const char* plural = i == 1 ? " " : "s 1-"; - return PyErr_Format(PyExc_ValueError, - "map() argument %d is longer than argument%s%d", - i + 1, plural, i); + PyErr_Format(PyExc_ValueError, + "map() argument %d is longer than argument%s%d", + i + 1, plural, i); + goto exit_no_result; } if (PyErr_Occurred()) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { // next() on argument i raised an exception (not StopIteration) - return NULL; + goto exit_no_result; } PyErr_Clear(); } // Argument i is exhausted. So far so good... } // All arguments are exhausted. Success! - goto exit; + +exit_no_result: + assert(result == NULL); + +exit: + for (i = 0; i < nargs; i++) { + Py_DECREF(stack[i]); + } + if (stack != small_stack) { + PyMem_Free(stack); + } + return result; } static PyObject * diff --git a/Python/pystate.c b/Python/pystate.c index 955457cdcf5c289..2f76adf50260125 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -67,48 +67,34 @@ to avoid the expense of doing their own locking). For each of these functions, the GIL must be held by the current thread. */ +#ifndef HAVE_THREAD_LOCAL +# error "no supported thread-local variable storage classifier" +#endif -#ifdef HAVE_THREAD_LOCAL /* The attached thread state for the current thread. */ _Py_thread_local PyThreadState *_Py_tss_tstate = NULL; /* The "bound" thread state used by PyGILState_Ensure(), also known as a "gilstate." */ _Py_thread_local PyThreadState *_Py_tss_gilstate = NULL; -#endif static inline PyThreadState * current_fast_get(void) { -#ifdef HAVE_THREAD_LOCAL return _Py_tss_tstate; -#else - // XXX Fall back to the PyThread_tss_*() API. -# error "no supported thread-local variable storage classifier" -#endif } static inline void current_fast_set(_PyRuntimeState *Py_UNUSED(runtime), PyThreadState *tstate) { assert(tstate != NULL); -#ifdef HAVE_THREAD_LOCAL _Py_tss_tstate = tstate; -#else - // XXX Fall back to the PyThread_tss_*() API. -# error "no supported thread-local variable storage classifier" -#endif } static inline void current_fast_clear(_PyRuntimeState *Py_UNUSED(runtime)) { -#ifdef HAVE_THREAD_LOCAL _Py_tss_tstate = NULL; -#else - // XXX Fall back to the PyThread_tss_*() API. -# error "no supported thread-local variable storage classifier" -#endif } #define tstate_verify_not_active(tstate) \ @@ -2494,14 +2480,10 @@ _PyThreadState_Bind(PyThreadState *tstate) uintptr_t _Py_GetThreadLocal_Addr(void) { -#ifdef HAVE_THREAD_LOCAL // gh-112535: Use the address of the thread-local PyThreadState variable as // a unique identifier for the current thread. Each thread has a unique // _Py_tss_tstate variable with a unique address. return (uintptr_t)&_Py_tss_tstate; -#else -# error "no supported thread-local variable storage classifier" -#endif } #endif