diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-04-18 12:39:32 +0300 |
---|---|---|
committer | shadchin <shadchin@yandex-team.ru> | 2022-04-18 12:39:32 +0300 |
commit | d4be68e361f4258cf0848fc70018dfe37a2acc24 (patch) | |
tree | 153e294cd97ac8b5d7a989612704a0c1f58e8ad4 /contrib/tools/python3/src/Modules/_threadmodule.c | |
parent | 260c02f5ccf242d9d9b8a873afaf6588c00237d6 (diff) | |
download | ydb-d4be68e361f4258cf0848fc70018dfe37a2acc24.tar.gz |
IGNIETFERRO-1816 Update Python 3 from 3.9.12 to 3.10.4
ref:9f96be6d02ee8044fdd6f124b799b270c20ce641
Diffstat (limited to 'contrib/tools/python3/src/Modules/_threadmodule.c')
-rw-r--r-- | contrib/tools/python3/src/Modules/_threadmodule.c | 782 |
1 files changed, 430 insertions, 352 deletions
diff --git a/contrib/tools/python3/src/Modules/_threadmodule.c b/contrib/tools/python3/src/Modules/_threadmodule.c index a370352238..813d7ec232 100644 --- a/contrib/tools/python3/src/Modules/_threadmodule.c +++ b/contrib/tools/python3/src/Modules/_threadmodule.c @@ -3,17 +3,45 @@ /* Interface to Sjoerd's portable C thread library */ #include "Python.h" -#include "pycore_pylifecycle.h" #include "pycore_interp.h" // _PyInterpreterState.num_threads +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_pylifecycle.h" #include "pycore_pystate.h" // _PyThreadState_Init() #include <stddef.h> // offsetof() +#include "structmember.h" // PyMemberDef + +#ifdef HAVE_SIGNAL_H +# include <signal.h> // SIGINT +#endif -static PyObject *ThreadError; -static PyObject *str_dict; +// ThreadError is just an alias to PyExc_RuntimeError +#define ThreadError PyExc_RuntimeError + +_Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(stderr); _Py_IDENTIFIER(flush); + +// Forward declarations +static struct PyModuleDef thread_module; + + +typedef struct { + PyTypeObject *lock_type; + PyTypeObject *local_type; + PyTypeObject *local_dummy_type; +} thread_module_state; + +static inline thread_module_state* +get_thread_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (thread_module_state *)state; +} + + /* Lock objects */ typedef struct { @@ -23,18 +51,28 @@ typedef struct { char locked; /* for sanity checking */ } lockobject; +static int +lock_traverse(lockobject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + static void lock_dealloc(lockobject *self) { - if (self->in_weakreflist != NULL) + if (self->in_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); + } if (self->lock_lock != NULL) { /* Unlock the lock so it's safe to free it */ if (self->locked) PyThread_release_lock(self->lock_lock); PyThread_free_lock(self->lock_lock); } - PyObject_Del(self); + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free((PyObject*)self); + Py_DECREF(tp); } /* Helper to acquire an interruptible lock with a timeout. If the lock acquire @@ -48,12 +86,13 @@ acquire_timed(PyThread_type_lock lock, _PyTime_t timeout) { PyLockStatus r; _PyTime_t endtime = 0; - _PyTime_t microseconds; - if (timeout > 0) + if (timeout > 0) { endtime = _PyTime_GetMonotonicClock() + timeout; + } do { + _PyTime_t microseconds; microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING); /* first a simple non-blocking try without releasing the GIL */ @@ -138,12 +177,10 @@ static PyObject * lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds) { _PyTime_t timeout; - PyLockStatus r; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) return NULL; - r = acquire_timed(self->lock_lock, timeout); + PyLockStatus r = acquire_timed(self->lock_lock, timeout); if (r == PY_LOCK_INTR) { return NULL; } @@ -245,36 +282,39 @@ static PyMethodDef lock_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject Locktype = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "_thread.lock", /*tp_name*/ - sizeof(lockobject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)lock_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)lock_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - offsetof(lockobject, in_weakreflist), /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - lock_methods, /*tp_methods*/ +PyDoc_STRVAR(lock_doc, +"A lock object is a synchronization primitive. To create a lock,\n\ +call threading.Lock(). Methods are:\n\ +\n\ +acquire() -- lock the lock, possibly blocking until it can be obtained\n\ +release() -- unlock of the lock\n\ +locked() -- test whether the lock is currently locked\n\ +\n\ +A lock is not owned by the thread that locked it; another thread may\n\ +unlock it. A thread attempting to lock a lock that it has already locked\n\ +will block until another thread unlocks it. Deadlocks may ensue."); + +static PyMemberDef lock_type_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(lockobject, in_weakreflist), READONLY}, + {NULL}, +}; + +static PyType_Slot lock_type_slots[] = { + {Py_tp_dealloc, (destructor)lock_dealloc}, + {Py_tp_repr, (reprfunc)lock_repr}, + {Py_tp_doc, (void *)lock_doc}, + {Py_tp_methods, lock_methods}, + {Py_tp_traverse, lock_traverse}, + {Py_tp_members, lock_type_members}, + {0, 0} +}; + +static PyType_Spec lock_type_spec = { + .name = "_thread.lock", + .basicsize = sizeof(lockobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), + .slots = lock_type_slots, }; /* Recursive lock objects */ @@ -287,6 +327,14 @@ typedef struct { PyObject *in_weakreflist; } rlockobject; +static int +rlock_traverse(rlockobject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + + static void rlock_dealloc(rlockobject *self) { @@ -301,7 +349,9 @@ rlock_dealloc(rlockobject *self) PyThread_free_lock(self->rlock_lock); } - Py_TYPE(self)->tp_free(self); + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -525,58 +575,45 @@ static PyMethodDef rlock_methods[] = { }; -static PyTypeObject RLocktype = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "_thread.RLock", /*tp_name*/ - sizeof(rlockobject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)rlock_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)rlock_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - offsetof(rlockobject, in_weakreflist), /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - rlock_methods, /*tp_methods*/ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - rlock_new /* tp_new */ +static PyMemberDef rlock_type_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(rlockobject, in_weakreflist), READONLY}, + {NULL}, +}; + +static PyType_Slot rlock_type_slots[] = { + {Py_tp_dealloc, (destructor)rlock_dealloc}, + {Py_tp_repr, (reprfunc)rlock_repr}, + {Py_tp_methods, rlock_methods}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, rlock_new}, + {Py_tp_members, rlock_type_members}, + {Py_tp_traverse, rlock_traverse}, + {0, 0}, +}; + +static PyType_Spec rlock_type_spec = { + .name = "_thread.RLock", + .basicsize = sizeof(rlockobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), + .slots = rlock_type_slots, }; static lockobject * -newlockobject(void) +newlockobject(PyObject *module) { - lockobject *self; - self = PyObject_New(lockobject, &Locktype); - if (self == NULL) + thread_module_state *state = get_thread_state(module); + + PyTypeObject *type = state->lock_type; + lockobject *self = (lockobject *)type->tp_alloc(type, 0); + if (self == NULL) { return NULL; + } + self->lock_lock = PyThread_allocate_lock(); self->locked = 0; self->in_weakreflist = NULL; + if (self->lock_lock == NULL) { Py_DECREF(self); PyErr_SetString(ThreadError, "can't allocate lock"); @@ -637,35 +674,29 @@ localdummy_dealloc(localdummyobject *self) { if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free((PyObject*)self); + Py_DECREF(tp); } -static PyTypeObject localdummytype = { - PyVarObject_HEAD_INIT(NULL, 0) - /* tp_name */ "_thread._localdummy", - /* tp_basicsize */ sizeof(localdummyobject), - /* tp_itemsize */ 0, - /* tp_dealloc */ (destructor)localdummy_dealloc, - /* tp_vectorcall_offset */ 0, - /* tp_getattr */ 0, - /* tp_setattr */ 0, - /* tp_as_async */ 0, - /* tp_repr */ 0, - /* tp_as_number */ 0, - /* tp_as_sequence */ 0, - /* tp_as_mapping */ 0, - /* tp_hash */ 0, - /* tp_call */ 0, - /* tp_str */ 0, - /* tp_getattro */ 0, - /* tp_setattro */ 0, - /* tp_as_buffer */ 0, - /* tp_flags */ Py_TPFLAGS_DEFAULT, - /* tp_doc */ "Thread-local dummy", - /* tp_traverse */ 0, - /* tp_clear */ 0, - /* tp_richcompare */ 0, - /* tp_weaklistoffset */ offsetof(localdummyobject, weakreflist) +static PyMemberDef local_dummy_type_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(localdummyobject, weakreflist), READONLY}, + {NULL}, +}; + +static PyType_Slot local_dummy_type_slots[] = { + {Py_tp_dealloc, (destructor)localdummy_dealloc}, + {Py_tp_doc, "Thread-local dummy"}, + {Py_tp_members, local_dummy_type_members}, + {0, 0} +}; + +static PyType_Spec local_dummy_type_spec = { + .name = "_thread._localdummy", + .basicsize = sizeof(localdummyobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = local_dummy_type_slots, }; @@ -682,19 +713,19 @@ typedef struct { } localobject; /* Forward declaration */ -static PyObject *_ldict(localobject *self); +static PyObject *_ldict(localobject *self, thread_module_state *state); static PyObject *_localdummy_destroyed(PyObject *meth_self, PyObject *dummyweakref); /* Create and register the dummy for the current thread. Returns a borrowed reference of the corresponding local dict */ static PyObject * -_local_create_dummy(localobject *self) +_local_create_dummy(localobject *self, thread_module_state *state) { - PyObject *tdict, *ldict = NULL, *wr = NULL; + PyObject *ldict = NULL, *wr = NULL; localdummyobject *dummy = NULL; - int r; + PyTypeObject *type = state->local_dummy_type; - tdict = PyThreadState_GetDict(); + PyObject *tdict = PyThreadState_GetDict(); if (tdict == NULL) { PyErr_SetString(PyExc_SystemError, "Couldn't get thread-state dictionary"); @@ -702,25 +733,30 @@ _local_create_dummy(localobject *self) } ldict = PyDict_New(); - if (ldict == NULL) + if (ldict == NULL) { goto err; - dummy = (localdummyobject *) localdummytype.tp_alloc(&localdummytype, 0); - if (dummy == NULL) + } + dummy = (localdummyobject *) type->tp_alloc(type, 0); + if (dummy == NULL) { goto err; + } dummy->localdict = ldict; wr = PyWeakref_NewRef((PyObject *) dummy, self->wr_callback); - if (wr == NULL) + if (wr == NULL) { goto err; + } /* As a side-effect, this will cache the weakref's hash before the dummy gets deleted */ - r = PyDict_SetItem(self->dummies, wr, ldict); - if (r < 0) + int r = PyDict_SetItem(self->dummies, wr, ldict); + if (r < 0) { goto err; + } Py_CLEAR(wr); r = PyDict_SetItem(tdict, self->key, (PyObject *) dummy); - if (r < 0) + if (r < 0) { goto err; + } Py_CLEAR(dummy); Py_DECREF(ldict); @@ -736,8 +772,6 @@ err: static PyObject * local_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - localobject *self; - PyObject *wr; static PyMethodDef wr_callback_def = { "_localdummy_destroyed", (PyCFunction) _localdummy_destroyed, METH_O }; @@ -749,42 +783,48 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (rc == 0 && kw != NULL) rc = PyObject_IsTrue(kw); if (rc != 0) { - if (rc > 0) + if (rc > 0) { PyErr_SetString(PyExc_TypeError, "Initialization arguments are not supported"); + } return NULL; } } - self = (localobject *)type->tp_alloc(type, 0); - if (self == NULL) + PyObject *module = _PyType_GetModuleByDef(type, &thread_module); + thread_module_state *state = get_thread_state(module); + + localobject *self = (localobject *)type->tp_alloc(type, 0); + if (self == NULL) { return NULL; + } - Py_XINCREF(args); - self->args = args; - Py_XINCREF(kw); - self->kw = kw; + self->args = Py_XNewRef(args); + self->kw = Py_XNewRef(kw); self->key = PyUnicode_FromFormat("thread.local.%p", self); - if (self->key == NULL) + if (self->key == NULL) { goto err; + } self->dummies = PyDict_New(); - if (self->dummies == NULL) + if (self->dummies == NULL) { goto err; + } /* We use a weak reference to self in the callback closure in order to avoid spurious reference cycles */ - wr = PyWeakref_NewRef((PyObject *) self, NULL); - if (wr == NULL) + PyObject *wr = PyWeakref_NewRef((PyObject *) self, NULL); + if (wr == NULL) { goto err; + } self->wr_callback = PyCFunction_NewEx(&wr_callback_def, wr, NULL); Py_DECREF(wr); - if (self->wr_callback == NULL) + if (self->wr_callback == NULL) { goto err; - - if (_local_create_dummy(self) == NULL) + } + if (_local_create_dummy(self, state) == NULL) { goto err; - + } return (PyObject *)self; err: @@ -795,6 +835,7 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) static int local_traverse(localobject *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->args); Py_VISIT(self->kw); Py_VISIT(self->dummies); @@ -804,23 +845,26 @@ local_traverse(localobject *self, visitproc visit, void *arg) static int local_clear(localobject *self) { - PyThreadState *tstate; Py_CLEAR(self->args); Py_CLEAR(self->kw); Py_CLEAR(self->dummies); Py_CLEAR(self->wr_callback); /* Remove all strong references to dummies from the thread states */ - if (self->key - && (tstate = PyThreadState_Get()) - && tstate->interp) { - for(tstate = PyInterpreterState_ThreadHead(tstate->interp); - tstate; - tstate = PyThreadState_Next(tstate)) - if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) { - if (PyDict_DelItem(tstate->dict, self->key)) { - PyErr_Clear(); - } + if (self->key) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); + for(; tstate; tstate = PyThreadState_Next(tstate)) { + if (tstate->dict == NULL) { + continue; + } + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v != NULL) { + Py_DECREF(v); + } + else { + PyErr_Clear(); } + } } return 0; } @@ -830,35 +874,38 @@ local_dealloc(localobject *self) { /* Weakrefs must be invalidated right now, otherwise they can be used from code called below, which is very dangerous since Py_REFCNT(self) == 0 */ - if (self->weakreflist != NULL) + if (self->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); + } PyObject_GC_UnTrack(self); local_clear(self); Py_XDECREF(self->key); - Py_TYPE(self)->tp_free((PyObject*)self); + + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free((PyObject*)self); + Py_DECREF(tp); } /* Returns a borrowed reference to the local dict, creating it if necessary */ static PyObject * -_ldict(localobject *self) +_ldict(localobject *self, thread_module_state *state) { - PyObject *tdict, *ldict, *dummy; - - tdict = PyThreadState_GetDict(); + PyObject *tdict = PyThreadState_GetDict(); if (tdict == NULL) { PyErr_SetString(PyExc_SystemError, "Couldn't get thread-state dictionary"); return NULL; } - dummy = PyDict_GetItemWithError(tdict, self->key); + PyObject *ldict; + PyObject *dummy = PyDict_GetItemWithError(tdict, self->key); if (dummy == NULL) { if (PyErr_Occurred()) { return NULL; } - ldict = _local_create_dummy(self); + ldict = _local_create_dummy(self, state); if (ldict == NULL) return NULL; @@ -873,7 +920,7 @@ _ldict(localobject *self) } } else { - assert(Py_IS_TYPE(dummy, &localdummytype)); + assert(Py_IS_TYPE(dummy, state->local_dummy_type)); ldict = ((localdummyobject *) dummy)->localdict; } @@ -883,104 +930,98 @@ _ldict(localobject *self) static int local_setattro(localobject *self, PyObject *name, PyObject *v) { - PyObject *ldict; - int r; + PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module); + thread_module_state *state = get_thread_state(module); - ldict = _ldict(self); - if (ldict == NULL) + PyObject *ldict = _ldict(self, state); + if (ldict == NULL) { return -1; + } - r = PyObject_RichCompareBool(name, str_dict, Py_EQ); + PyObject *str_dict = _PyUnicode_FromId(&PyId___dict__); // borrowed ref + if (str_dict == NULL) { + return -1; + } + + int r = PyObject_RichCompareBool(name, str_dict, Py_EQ); + if (r == -1) { + return -1; + } if (r == 1) { PyErr_Format(PyExc_AttributeError, "'%.50s' object attribute '%U' is read-only", Py_TYPE(self)->tp_name, name); return -1; } - if (r == -1) - return -1; return _PyObject_GenericSetAttrWithDict((PyObject *)self, name, v, ldict); } static PyObject *local_getattro(localobject *, PyObject *); -static PyTypeObject localtype = { - PyVarObject_HEAD_INIT(NULL, 0) - /* tp_name */ "_thread._local", - /* tp_basicsize */ sizeof(localobject), - /* tp_itemsize */ 0, - /* tp_dealloc */ (destructor)local_dealloc, - /* tp_vectorcall_offset */ 0, - /* tp_getattr */ 0, - /* tp_setattr */ 0, - /* tp_as_async */ 0, - /* tp_repr */ 0, - /* tp_as_number */ 0, - /* tp_as_sequence */ 0, - /* tp_as_mapping */ 0, - /* tp_hash */ 0, - /* tp_call */ 0, - /* tp_str */ 0, - /* tp_getattro */ (getattrofunc)local_getattro, - /* tp_setattro */ (setattrofunc)local_setattro, - /* tp_as_buffer */ 0, - /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, - /* tp_doc */ "Thread-local data", - /* tp_traverse */ (traverseproc)local_traverse, - /* tp_clear */ (inquiry)local_clear, - /* tp_richcompare */ 0, - /* tp_weaklistoffset */ offsetof(localobject, weakreflist), - /* tp_iter */ 0, - /* tp_iternext */ 0, - /* tp_methods */ 0, - /* tp_members */ 0, - /* tp_getset */ 0, - /* tp_base */ 0, - /* tp_dict */ 0, /* internal use */ - /* tp_descr_get */ 0, - /* tp_descr_set */ 0, - /* tp_dictoffset */ 0, - /* tp_init */ 0, - /* tp_alloc */ 0, - /* tp_new */ local_new, - /* tp_free */ 0, /* Low-level free-mem routine */ - /* tp_is_gc */ 0, /* For PyObject_IS_GC */ +static PyMemberDef local_type_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(localobject, weakreflist), READONLY}, + {NULL}, +}; + +static PyType_Slot local_type_slots[] = { + {Py_tp_dealloc, (destructor)local_dealloc}, + {Py_tp_getattro, (getattrofunc)local_getattro}, + {Py_tp_setattro, (setattrofunc)local_setattro}, + {Py_tp_doc, "Thread-local data"}, + {Py_tp_traverse, (traverseproc)local_traverse}, + {Py_tp_clear, (inquiry)local_clear}, + {Py_tp_new, local_new}, + {Py_tp_members, local_type_members}, + {0, 0} +}; + +static PyType_Spec local_type_spec = { + .name = "_thread._local", + .basicsize = sizeof(localobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = local_type_slots, }; static PyObject * local_getattro(localobject *self, PyObject *name) { - PyObject *ldict, *value; - int r; + PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module); + thread_module_state *state = get_thread_state(module); - ldict = _ldict(self); + PyObject *ldict = _ldict(self, state); if (ldict == NULL) return NULL; - r = PyObject_RichCompareBool(name, str_dict, Py_EQ); + PyObject *str_dict = _PyUnicode_FromId(&PyId___dict__); // borrowed ref + if (str_dict == NULL) { + return NULL; + } + + int r = PyObject_RichCompareBool(name, str_dict, Py_EQ); if (r == 1) { - Py_INCREF(ldict); - return ldict; + return Py_NewRef(ldict); } - if (r == -1) + if (r == -1) { return NULL; + } - if (!Py_IS_TYPE(self, &localtype)) + if (!Py_IS_TYPE(self, state->local_type)) { /* use generic lookup for subtypes */ - return _PyObject_GenericGetAttrWithDict( - (PyObject *)self, name, ldict, 0); + return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, + ldict, 0); + } /* Optimization: just look in dict ourselves */ - value = PyDict_GetItemWithError(ldict, name); + PyObject *value = PyDict_GetItemWithError(ldict, name); if (value != NULL) { - Py_INCREF(value); - return value; + return Py_NewRef(value); } - else if (PyErr_Occurred()) { + if (PyErr_Occurred()) { return NULL; } + /* Fall back on generic to get __class__ and __dict__ */ return _PyObject_GenericGetAttrWithDict( (PyObject *)self, name, ldict, 0); @@ -990,17 +1031,15 @@ local_getattro(localobject *self, PyObject *name) static PyObject * _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref) { - PyObject *obj; - localobject *self; assert(PyWeakref_CheckRef(localweakref)); - obj = PyWeakref_GET_OBJECT(localweakref); - if (obj == Py_None) + PyObject *obj = PyWeakref_GET_OBJECT(localweakref); + if (obj == Py_None) { Py_RETURN_NONE; - Py_INCREF(obj); - assert(PyObject_TypeCheck(obj, &localtype)); + } + /* If the thread-local object is still alive and not being cleared, remove the corresponding local dict */ - self = (localobject *) obj; + localobject *self = (localobject *)Py_NewRef(obj); if (self->dummies != NULL) { PyObject *ldict; ldict = PyDict_GetItemWithError(self->dummies, dummyweakref); @@ -1020,24 +1059,35 @@ struct bootstate { PyInterpreterState *interp; PyObject *func; PyObject *args; - PyObject *keyw; + PyObject *kwargs; PyThreadState *tstate; _PyRuntimeState *runtime; }; + +static void +thread_bootstate_free(struct bootstate *boot) +{ + Py_DECREF(boot->func); + Py_DECREF(boot->args); + Py_XDECREF(boot->kwargs); + PyMem_Free(boot); +} + + static void -t_bootstrap(void *boot_raw) +thread_run(void *boot_raw) { struct bootstate *boot = (struct bootstate *) boot_raw; PyThreadState *tstate; - PyObject *res; tstate = boot->tstate; tstate->thread_id = PyThread_get_thread_ident(); _PyThreadState_Init(tstate); PyEval_AcquireThread(tstate); tstate->interp->num_threads++; - res = PyObject_Call(boot->func, boot->args, boot->keyw); + + PyObject *res = PyObject_Call(boot->func, boot->args, boot->kwargs); if (res == NULL) { if (PyErr_ExceptionMatches(PyExc_SystemExit)) /* SystemExit is ignored silently */ @@ -1049,10 +1099,8 @@ t_bootstrap(void *boot_raw) else { Py_DECREF(res); } - Py_DECREF(boot->func); - Py_DECREF(boot->args); - Py_XDECREF(boot->keyw); - PyMem_DEL(boot_raw); + + thread_bootstate_free(boot); tstate->interp->num_threads--; PyThreadState_Clear(tstate); _PyThreadState_DeleteCurrent(tstate); @@ -1066,12 +1114,10 @@ static PyObject * thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) { _PyRuntimeState *runtime = &_PyRuntime; - PyObject *func, *args, *keyw = NULL; - struct bootstate *boot; - unsigned long ident; + PyObject *func, *args, *kwargs = NULL; if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, - &func, &args, &keyw)) + &func, &args, &kwargs)) return NULL; if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, @@ -1083,7 +1129,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) "2nd arg must be a tuple"); return NULL; } - if (keyw != NULL && !PyDict_Check(keyw)) { + if (kwargs != NULL && !PyDict_Check(kwargs)) { PyErr_SetString(PyExc_TypeError, "optional 3rd arg must be a dictionary"); return NULL; @@ -1096,31 +1142,26 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) return NULL; } - boot = PyMem_NEW(struct bootstate, 1); - if (boot == NULL) + struct bootstate *boot = PyMem_NEW(struct bootstate, 1); + if (boot == NULL) { return PyErr_NoMemory(); + } boot->interp = _PyInterpreterState_GET(); - boot->func = func; - boot->args = args; - boot->keyw = keyw; boot->tstate = _PyThreadState_Prealloc(boot->interp); - boot->runtime = runtime; if (boot->tstate == NULL) { - PyMem_DEL(boot); + PyMem_Free(boot); return PyErr_NoMemory(); } - Py_INCREF(func); - Py_INCREF(args); - Py_XINCREF(keyw); + boot->runtime = runtime; + boot->func = Py_NewRef(func); + boot->args = Py_NewRef(args); + boot->kwargs = Py_XNewRef(kwargs); - ident = PyThread_start_new_thread(t_bootstrap, (void*) boot); + unsigned long ident = PyThread_start_new_thread(thread_run, (void*) boot); if (ident == PYTHREAD_INVALID_THREAD_ID) { PyErr_SetString(ThreadError, "can't start new thread"); - Py_DECREF(func); - Py_DECREF(args); - Py_XDECREF(keyw); PyThreadState_Clear(boot->tstate); - PyMem_DEL(boot); + thread_bootstate_free(boot); return NULL; } return PyLong_FromUnsignedLong(ident); @@ -1152,25 +1193,37 @@ This is synonymous to ``raise SystemExit''. It will cause the current\n\ thread to exit silently unless the exception is caught."); static PyObject * -thread_PyThread_interrupt_main(PyObject * self, PyObject *Py_UNUSED(ignored)) +thread_PyThread_interrupt_main(PyObject *self, PyObject *args) { - PyErr_SetInterrupt(); + int signum = SIGINT; + if (!PyArg_ParseTuple(args, "|i:signum", &signum)) { + return NULL; + } + + if (PyErr_SetInterruptEx(signum)) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } Py_RETURN_NONE; } PyDoc_STRVAR(interrupt_doc, -"interrupt_main()\n\ +"interrupt_main(signum=signal.SIGINT, /)\n\ \n\ -Raise a KeyboardInterrupt in the main thread.\n\ -A subthread can use this function to interrupt the main thread." +Simulate the arrival of the given signal in the main thread,\n\ +where the corresponding signal handler will be executed.\n\ +If *signum* is omitted, SIGINT is assumed.\n\ +A subthread can use this function to interrupt the main thread.\n\ +\n\ +Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``." ); -static lockobject *newlockobject(void); +static lockobject *newlockobject(PyObject *module); static PyObject * -thread_PyThread_allocate_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored)) { - return (PyObject *) newlockobject(); + return (PyObject *) newlockobject(module); } PyDoc_STRVAR(allocate_doc, @@ -1247,7 +1300,6 @@ release_sentinel(void *wr_raw) PyObject *obj = PyWeakref_GET_OBJECT(wr); lockobject *lock; if (obj != Py_None) { - assert(Py_IS_TYPE(obj, &Locktype)); lock = (lockobject *) obj; if (lock->locked) { PyThread_release_lock(lock->lock_lock); @@ -1260,7 +1312,7 @@ release_sentinel(void *wr_raw) } static PyObject * -thread__set_sentinel(PyObject *self, PyObject *Py_UNUSED(ignored)) +thread__set_sentinel(PyObject *module, PyObject *Py_UNUSED(ignored)) { PyObject *wr; PyThreadState *tstate = PyThreadState_Get(); @@ -1275,7 +1327,7 @@ thread__set_sentinel(PyObject *self, PyObject *Py_UNUSED(ignored)) tstate->on_delete_data = NULL; Py_DECREF(wr); } - lock = newlockobject(); + lock = newlockobject(module); if (lock == NULL) return NULL; /* The lock is owned by whoever called _set_sentinel(), but the weakref @@ -1428,7 +1480,7 @@ static PyStructSequence_Field ExceptHookArgs_fields[] = { }; static PyStructSequence_Desc ExceptHookArgs_desc = { - .name = "_thread.ExceptHookArgs", + .name = "_thread._ExceptHookArgs", .doc = ExceptHookArgs__doc__, .fields = ExceptHookArgs_fields, .n_in_sequence = 4 @@ -1507,8 +1559,8 @@ static PyMethodDef thread_methods[] = { METH_NOARGS, exit_doc}, {"exit", thread_PyThread_exit_thread, METH_NOARGS, exit_doc}, - {"interrupt_main", thread_PyThread_interrupt_main, - METH_NOARGS, interrupt_doc}, + {"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main, + METH_VARARGS, interrupt_doc}, {"get_ident", thread_get_ident, METH_NOARGS, get_ident_doc}, #ifdef PY_HAVE_THREAD_NATIVE_ID @@ -1529,106 +1581,132 @@ static PyMethodDef thread_methods[] = { /* Initialization function */ -PyDoc_STRVAR(thread_doc, -"This module provides primitive operations to write multi-threaded programs.\n\ -The 'threading' module provides a more convenient interface."); +static int +thread_module_exec(PyObject *module) +{ + thread_module_state *state = get_thread_state(module); + PyObject *d = PyModule_GetDict(module); -PyDoc_STRVAR(lock_doc, -"A lock object is a synchronization primitive. To create a lock,\n\ -call threading.Lock(). Methods are:\n\ -\n\ -acquire() -- lock the lock, possibly blocking until it can be obtained\n\ -release() -- unlock of the lock\n\ -locked() -- test whether the lock is currently locked\n\ -\n\ -A lock is not owned by the thread that locked it; another thread may\n\ -unlock it. A thread attempting to lock a lock that it has already locked\n\ -will block until another thread unlocks it. Deadlocks may ensue."); + // Initialize the C thread library + PyThread_init_thread(); -static struct PyModuleDef threadmodule = { - PyModuleDef_HEAD_INIT, - "_thread", - thread_doc, - -1, - thread_methods, - NULL, - NULL, - NULL, - NULL -}; + // Lock + state->lock_type = (PyTypeObject *)PyType_FromSpec(&lock_type_spec); + if (state->lock_type == NULL) { + return -1; + } + if (PyDict_SetItemString(d, "LockType", (PyObject *)state->lock_type) < 0) { + return -1; + } + // RLock + PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec); + if (rlock_type == NULL) { + return -1; + } + if (PyModule_AddType(module, rlock_type) < 0) { + Py_DECREF(rlock_type); + return -1; + } + Py_DECREF(rlock_type); -PyMODINIT_FUNC -PyInit__thread(void) -{ - PyObject *m, *d, *v; - double time_max; - double timeout_max; - PyInterpreterState *interp = _PyInterpreterState_GET(); + // Local dummy + state->local_dummy_type = (PyTypeObject *)PyType_FromSpec(&local_dummy_type_spec); + if (state->local_dummy_type == NULL) { + return -1; + } + + // Local + state->local_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &local_type_spec, NULL); + if (state->local_type == NULL) { + return -1; + } + if (PyModule_AddType(module, state->local_type) < 0) { + return -1; + } - /* Initialize types: */ - if (PyType_Ready(&localdummytype) < 0) - return NULL; - if (PyType_Ready(&localtype) < 0) - return NULL; - if (PyType_Ready(&Locktype) < 0) - return NULL; - if (PyType_Ready(&RLocktype) < 0) - return NULL; if (ExceptHookArgsType.tp_name == NULL) { if (PyStructSequence_InitType2(&ExceptHookArgsType, &ExceptHookArgs_desc) < 0) { - return NULL; + return -1; } } - /* Create the module and add the functions */ - m = PyModule_Create(&threadmodule); - if (m == NULL) - return NULL; + // Add module attributes + if (PyDict_SetItemString(d, "error", ThreadError) < 0) { + return -1; + } + if (PyModule_AddType(module, &ExceptHookArgsType) < 0) { + return -1; + } - timeout_max = (_PyTime_t)PY_TIMEOUT_MAX * 1e-6; - time_max = _PyTime_AsSecondsDouble(_PyTime_MAX); + // TIMEOUT_MAX + double timeout_max = (_PyTime_t)PY_TIMEOUT_MAX * 1e-6; + double time_max = _PyTime_AsSecondsDouble(_PyTime_MAX); timeout_max = Py_MIN(timeout_max, time_max); - /* Round towards minus infinity */ + // Round towards minus infinity timeout_max = floor(timeout_max); - v = PyFloat_FromDouble(timeout_max); - if (!v) - return NULL; - if (PyModule_AddObject(m, "TIMEOUT_MAX", v) < 0) - return NULL; + if (PyModule_AddObject(module, "TIMEOUT_MAX", + PyFloat_FromDouble(timeout_max)) < 0) { + return -1; + } - /* Add a symbolic constant */ - d = PyModule_GetDict(m); - ThreadError = PyExc_RuntimeError; - Py_INCREF(ThreadError); + return 0; +} - PyDict_SetItemString(d, "error", ThreadError); - Locktype.tp_doc = lock_doc; - Py_INCREF(&Locktype); - PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); - Py_INCREF(&RLocktype); - if (PyModule_AddObject(m, "RLock", (PyObject *)&RLocktype) < 0) - return NULL; +static int +thread_module_traverse(PyObject *module, visitproc visit, void *arg) +{ + thread_module_state *state = get_thread_state(module); + Py_VISIT(state->lock_type); + Py_VISIT(state->local_type); + Py_VISIT(state->local_dummy_type); + return 0; +} - Py_INCREF(&localtype); - if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) - return NULL; +static int +thread_module_clear(PyObject *module) +{ + thread_module_state *state = get_thread_state(module); + Py_CLEAR(state->lock_type); + Py_CLEAR(state->local_type); + Py_CLEAR(state->local_dummy_type); + return 0; +} - Py_INCREF(&ExceptHookArgsType); - if (PyModule_AddObject(m, "_ExceptHookArgs", - (PyObject *)&ExceptHookArgsType) < 0) - return NULL; +static void +thread_module_free(void *module) +{ + thread_module_clear((PyObject *)module); +} - interp->num_threads = 0; - str_dict = PyUnicode_InternFromString("__dict__"); - if (str_dict == NULL) - return NULL; - /* Initialize the C thread library */ - PyThread_init_thread(); - return m; +PyDoc_STRVAR(thread_doc, +"This module provides primitive operations to write multi-threaded programs.\n\ +The 'threading' module provides a more convenient interface."); + +static PyModuleDef_Slot thread_module_slots[] = { + {Py_mod_exec, thread_module_exec}, + {0, NULL} +}; + +static struct PyModuleDef thread_module = { + PyModuleDef_HEAD_INIT, + .m_name = "_thread", + .m_doc = thread_doc, + .m_size = sizeof(thread_module_state), + .m_methods = thread_methods, + .m_traverse = thread_module_traverse, + .m_clear = thread_module_clear, + .m_free = thread_module_free, + .m_slots = thread_module_slots, +}; + +PyMODINIT_FUNC +PyInit__thread(void) +{ + return PyModuleDef_Init(&thread_module); } |