GeezerVison is showing github.com — gh-120974: Make _asyncio._leave_task atomic in the free-threaded buil… · python/cpython@a15fede
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_dict.h +6-2Lines changed: 6 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -14,8 +14,12 @@ extern "C" { | 14 | 14 | // Unsafe flavor of PyDict_GetItemWithError(): no error checking | 15 | 15 | extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key); | 16 | 16 |
| 17 | | -extern int _PyDict_DelItemIf(PyObject *mp, PyObject *key, | 18 | | - int (*predicate)(PyObject *value)); | | 17 | +// Delete an item from a dict if a predicate is true | | 18 | +// Returns -1 on error, 1 if the item was deleted, 0 otherwise | | 19 | +// Export for '_asyncio' shared extension | | 20 | +PyAPI_FUNC(int) _PyDict_DelItemIf(PyObject *mp, PyObject *key, | | 21 | + int (*predicate)(PyObject *value, void *arg), | | 22 | + void *arg); | 19 | 23 |
| 20 | 24 | // "KnownHash" variants | 21 | 25 | // Export for '_asyncio' shared extension | |
Collapse file Copy file name to clipboardExpand all lines: Modules/_asynciomodule.c +24-18Lines changed: 24 additions & 18 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1994,30 +1994,36 @@ enter_task(asyncio_state *state, PyObject *loop, PyObject *task) | 1994 | 1994 | return 0; | 1995 | 1995 | } | 1996 | 1996 |
| | 1997 | +static int | | 1998 | +err_leave_task(PyObject *item, PyObject *task) | | 1999 | +{ | | 2000 | + PyErr_Format( | | 2001 | + PyExc_RuntimeError, | | 2002 | + "Leaving task %R does not match the current task %R.", | | 2003 | + task, item); | | 2004 | + return -1; | | 2005 | +} | | 2006 | + | | 2007 | +static int | | 2008 | +leave_task_predicate(PyObject *item, void *task) | | 2009 | +{ | | 2010 | + if (item != task) { | | 2011 | + return err_leave_task(item, (PyObject *)task); | | 2012 | + } | | 2013 | + return 1; | | 2014 | +} | 1997 | 2015 |
| 1998 | 2016 | static int | 1999 | 2017 | leave_task(asyncio_state *state, PyObject *loop, PyObject *task) | 2000 | 2018 | /*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/ | 2001 | 2019 | { | 2002 | | - PyObject *item; | 2003 | | - Py_hash_t hash; | 2004 | | - hash = PyObject_Hash(loop); | 2005 | | - if (hash == -1) { | 2006 | | - return -1; | 2007 | | - } | 2008 | | - item = _PyDict_GetItem_KnownHash(state->current_tasks, loop, hash); | 2009 | | - if (item != task) { | 2010 | | - if (item == NULL) { | 2011 | | - /* Not entered, replace with None */ | 2012 | | - item = Py_None; | 2013 | | - } | 2014 | | - PyErr_Format( | 2015 | | - PyExc_RuntimeError, | 2016 | | - "Leaving task %R does not match the current task %R.", | 2017 | | - task, item, NULL); | 2018 | | - return -1; | | 2020 | + int res = _PyDict_DelItemIf(state->current_tasks, loop, | | 2021 | + leave_task_predicate, task); | | 2022 | + if (res == 0) { | | 2023 | + // task was not found | | 2024 | + return err_leave_task(Py_None, task); | 2019 | 2025 | } | 2020 | | - return _PyDict_DelItem_KnownHash(state->current_tasks, loop, hash); | | 2026 | + return res; | 2021 | 2027 | } | 2022 | 2028 |
| 2023 | 2029 | static PyObject * | |
Collapse file Copy file name to clipboardExpand all lines: Modules/_weakref.c +3-10Lines changed: 3 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -31,7 +31,7 @@ _weakref_getweakrefcount_impl(PyObject *module, PyObject *object) | 31 | 31 |
| 32 | 32 |
| 33 | 33 | static int | 34 | | -is_dead_weakref(PyObject *value) | | 34 | +is_dead_weakref(PyObject *value, void *unused) | 35 | 35 | { | 36 | 36 | if (!PyWeakref_Check(value)) { | 37 | 37 | PyErr_SetString(PyExc_TypeError, "not a weakref"); | @@ -56,15 +56,8 @@ _weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct, | 56 | 56 | PyObject *key) | 57 | 57 | /*[clinic end generated code: output=d9ff53061fcb875c input=19fc91f257f96a1d]*/ | 58 | 58 | { | 59 | | - if (_PyDict_DelItemIf(dct, key, is_dead_weakref) < 0) { | 60 | | - if (PyErr_ExceptionMatches(PyExc_KeyError)) | 61 | | - /* This function is meant to allow safe weak-value dicts | 62 | | - with GC in another thread (see issue #28427), so it's | 63 | | - ok if the key doesn't exist anymore. | 64 | | - */ | 65 | | - PyErr_Clear(); | 66 | | - else | 67 | | - return NULL; | | 59 | + if (_PyDict_DelItemIf(dct, key, is_dead_weakref, NULL) < 0) { | | 60 | + return NULL; | 68 | 61 | } | 69 | 62 | Py_RETURN_NONE; | 70 | 63 | } | |
Collapse file Copy file name to clipboardExpand all lines: Objects/dictobject.c +15-15Lines changed: 15 additions & 15 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -2508,7 +2508,7 @@ delete_index_from_values(PyDictValues *values, Py_ssize_t ix) | 2508 | 2508 | values->size = size; | 2509 | 2509 | } | 2510 | 2510 |
| 2511 | | -static int | | 2511 | +static void | 2512 | 2512 | delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, | 2513 | 2513 | PyObject *old_value, uint64_t new_version) | 2514 | 2514 | { | @@ -2550,7 +2550,6 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, | 2550 | 2550 | Py_DECREF(old_value); | 2551 | 2551 |
| 2552 | 2552 | ASSERT_CONSISTENT(mp); | 2553 | | - return 0; | 2554 | 2553 | } | 2555 | 2554 |
| 2556 | 2555 | int | @@ -2593,7 +2592,8 @@ delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash) | 2593 | 2592 | PyInterpreterState *interp = _PyInterpreterState_GET(); | 2594 | 2593 | uint64_t new_version = _PyDict_NotifyEvent( | 2595 | 2594 | interp, PyDict_EVENT_DELETED, mp, key, NULL); | 2596 | | - return delitem_common(mp, hash, ix, old_value, new_version); | | 2595 | + delitem_common(mp, hash, ix, old_value, new_version); | | 2596 | + return 0; | 2597 | 2597 | } | 2598 | 2598 |
| 2599 | 2599 | int | @@ -2608,7 +2608,8 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) | 2608 | 2608 |
| 2609 | 2609 | static int | 2610 | 2610 | delitemif_lock_held(PyObject *op, PyObject *key, | 2611 | | - int (*predicate)(PyObject *value)) | | 2611 | + int (*predicate)(PyObject *value, void *arg), | | 2612 | + void *arg) | 2612 | 2613 | { | 2613 | 2614 | Py_ssize_t ix; | 2614 | 2615 | PyDictObject *mp; | @@ -2618,32 +2619,29 @@ delitemif_lock_held(PyObject *op, PyObject *key, | 2618 | 2619 |
| 2619 | 2620 | ASSERT_DICT_LOCKED(op); | 2620 | 2621 |
| 2621 | | - if (!PyDict_Check(op)) { | 2622 | | - PyErr_BadInternalCall(); | 2623 | | - return -1; | 2624 | | - } | 2625 | 2622 | assert(key); | 2626 | 2623 | hash = PyObject_Hash(key); | 2627 | 2624 | if (hash == -1) | 2628 | 2625 | return -1; | 2629 | 2626 | mp = (PyDictObject *)op; | 2630 | 2627 | ix = _Py_dict_lookup(mp, key, hash, &old_value); | 2631 | | - if (ix == DKIX_ERROR) | | 2628 | + if (ix == DKIX_ERROR) { | 2632 | 2629 | return -1; | | 2630 | + } | 2633 | 2631 | if (ix == DKIX_EMPTY || old_value == NULL) { | 2634 | | - _PyErr_SetKeyError(key); | 2635 | | - return -1; | | 2632 | + return 0; | 2636 | 2633 | } | 2637 | 2634 |
| 2638 | | - res = predicate(old_value); | | 2635 | + res = predicate(old_value, arg); | 2639 | 2636 | if (res == -1) | 2640 | 2637 | return -1; | 2641 | 2638 |
| 2642 | 2639 | if (res > 0) { | 2643 | 2640 | PyInterpreterState *interp = _PyInterpreterState_GET(); | 2644 | 2641 | uint64_t new_version = _PyDict_NotifyEvent( | 2645 | 2642 | interp, PyDict_EVENT_DELETED, mp, key, NULL); | 2646 | | - return delitem_common(mp, hash, ix, old_value, new_version); | | 2643 | + delitem_common(mp, hash, ix, old_value, new_version); | | 2644 | + return 1; | 2647 | 2645 | } else { | 2648 | 2646 | return 0; | 2649 | 2647 | } | @@ -2655,11 +2653,13 @@ delitemif_lock_held(PyObject *op, PyObject *key, | 2655 | 2653 | */ | 2656 | 2654 | int | 2657 | 2655 | _PyDict_DelItemIf(PyObject *op, PyObject *key, | 2658 | | - int (*predicate)(PyObject *value)) | | 2656 | + int (*predicate)(PyObject *value, void *arg), | | 2657 | + void *arg) | 2659 | 2658 | { | | 2659 | + assert(PyDict_Check(op)); | 2660 | 2660 | int res; | 2661 | 2661 | Py_BEGIN_CRITICAL_SECTION(op); | 2662 | | - res = delitemif_lock_held(op, key, predicate); | | 2662 | + res = delitemif_lock_held(op, key, predicate, arg); | 2663 | 2663 | Py_END_CRITICAL_SECTION(); | 2664 | 2664 | return res; | 2665 | 2665 | } | |
|