diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 672537bfedc9af..6c8ce475c69842 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -818,6 +818,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bit_offset)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bit_size)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(block)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(blocking)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bound)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(buffer)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(buffer_callback)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index ed767206ee7322..55e48214728153 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -309,6 +309,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(bit_offset) STRUCT_FOR_ID(bit_size) STRUCT_FOR_ID(block) + STRUCT_FOR_ID(blocking) STRUCT_FOR_ID(bound) STRUCT_FOR_ID(buffer) STRUCT_FOR_ID(buffer_callback) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 2f24a138fdbf0c..ea188d4f2bf8ed 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -816,6 +816,7 @@ extern "C" { INIT_ID(bit_offset), \ INIT_ID(bit_size), \ INIT_ID(block), \ + INIT_ID(blocking), \ INIT_ID(bound), \ INIT_ID(buffer), \ INIT_ID(buffer_callback), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 4f8866e729c51e..044fdafef246ed 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1024,6 +1024,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(blocking); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(bound); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index f82ad6870f850f..0a22907375b0dd 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -38,6 +38,20 @@ typedef struct { struct llist_node shutdown_handles; } thread_module_state; +typedef struct { + PyObject_HEAD + PyMutex lock; +} lockobject; + +#define lockobject_CAST(op) ((lockobject *)(op)) + +typedef struct { + PyObject_HEAD + _PyRecursiveMutex lock; +} rlockobject; + +#define rlockobject_CAST(op) ((rlockobject *)(op)) + static inline thread_module_state* get_thread_state(PyObject *module) { @@ -753,13 +767,6 @@ static PyType_Spec ThreadHandle_Type_spec = { /* Lock objects */ -typedef struct { - PyObject_HEAD - PyMutex lock; -} lockobject; - -#define lockobject_CAST(op) ((lockobject *)(op)) - static int lock_traverse(PyObject *self, visitproc visit, void *arg) { @@ -779,16 +786,8 @@ lock_dealloc(PyObject *self) static int -lock_acquire_parse_args(PyObject *args, PyObject *kwds, - PyTime_t *timeout) +lock_acquire_parse_timeout(PyObject *timeout_obj, int blocking, PyTime_t *timeout) { - char *kwlist[] = {"blocking", "timeout", NULL}; - int blocking = 1; - PyObject *timeout_obj = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pO:acquire", kwlist, - &blocking, &timeout_obj)) - return -1; - // XXX Use PyThread_ParseTimeoutArg(). const PyTime_t unset_timeout = _PyTime_FromSeconds(-1); @@ -823,14 +822,29 @@ lock_acquire_parse_args(PyObject *args, PyObject *kwds, } return 0; } +/*[clinic input] +_thread.lock.acquire + blocking: bool = True + timeout as timeoutobj: object(py_default="-1") = NULL + +Lock the lock. + +Without argument, this blocks if the lock is already +locked (even by the same thread), waiting for another thread to release +the lock, and return True once the lock is acquired. +With an argument, this will only block if the argument is true, +and the return value reflects whether the lock is acquired. +The blocking operation is interruptible. +[clinic start generated code]*/ static PyObject * -lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds) +_thread_lock_acquire_impl(lockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=569d6b25d508bf6f input=13e999649bc1c798]*/ { - lockobject *self = lockobject_CAST(op); - PyTime_t timeout; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { + + if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { return NULL; } @@ -848,33 +862,34 @@ lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds) return PyBool_FromLong(r == PY_LOCK_ACQUIRED); } -PyDoc_STRVAR(acquire_doc, -"acquire($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -Lock the lock. Without argument, this blocks if the lock is already\n\ -locked (even by the same thread), waiting for another thread to release\n\ -the lock, and return True once the lock is acquired.\n\ -With an argument, this will only block if the argument is true,\n\ -and the return value reflects whether the lock is acquired.\n\ -The blocking operation is interruptible."); - -PyDoc_STRVAR(acquire_lock_doc, -"acquire_lock($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -An obsolete synonym of acquire()."); +/*[clinic input] +_thread.lock.acquire_lock = _thread.lock.acquire -PyDoc_STRVAR(enter_doc, -"__enter__($self, /)\n\ ---\n\ -\n\ -Lock the lock."); +An obsolete synonym of acquire(). +[clinic start generated code]*/ static PyObject * -lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock_acquire_lock_impl(lockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=ea6c87ea13b56694 input=5e65bd56327ebe85]*/ +{ + return _thread_lock_acquire_impl(self, blocking, timeoutobj); +} + +/*[clinic input] +_thread.lock.release + +Release the lock. + +Allows another thread that is blocked waiting for +the lock to acquire the lock. The lock must be in the locked state, +but it needn't be locked by the same thread that unlocks it. +[clinic start generated code]*/ + +static PyObject * +_thread_lock_release_impl(lockobject *self) +/*[clinic end generated code: output=a4ab0d75d6e9fb73 input=dfe48f962dfe99b4]*/ { - lockobject *self = lockobject_CAST(op); /* Sanity check: the lock must be locked */ if (_PyMutex_TryUnlock(&self->lock) < 0) { PyErr_SetString(ThreadError, "release unlocked lock"); @@ -884,44 +899,76 @@ lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) Py_RETURN_NONE; } -PyDoc_STRVAR(release_doc, -"release($self, /)\n\ ---\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -but it needn't be locked by the same thread that unlocks it."); +/*[clinic input] +_thread.lock.release_lock -PyDoc_STRVAR(release_lock_doc, -"release_lock($self, /)\n\ ---\n\ -\n\ -An obsolete synonym of release()."); +An obsolete synonym of release(). +[clinic start generated code]*/ -PyDoc_STRVAR(lock_exit_doc, -"__exit__($self, /, *exc_info)\n\ ---\n\ -\n\ -Release the lock."); +static PyObject * +_thread_lock_release_lock_impl(lockobject *self) +/*[clinic end generated code: output=43025044d51789bb input=74d91374fc601433]*/ +{ + return _thread_lock_release_impl(self); +} + +/*[clinic input] +_thread.lock.__enter__ + +Lock the lock. +[clinic start generated code]*/ static PyObject * -lock_locked_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock___enter___impl(lockobject *self) +/*[clinic end generated code: output=f27725de751ae064 input=8f982991608d38e7]*/ +{ + return _thread_lock_acquire_impl(self, 1, NULL); +} + +/*[clinic input] +_thread.lock.__exit__ + exc_type: object + exc_value: object + exc_tb: object + / + +Release the lock. +[clinic start generated code]*/ + +static PyObject * +_thread_lock___exit___impl(lockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb) +/*[clinic end generated code: output=c9e8eefa69beed07 input=c74d4abe15a6c037]*/ +{ + return _thread_lock_release_impl(self); +} + + +/*[clinic input] +_thread.lock.locked + +Return whether the lock is in the locked state. +[clinic start generated code]*/ + +static PyObject * +_thread_lock_locked_impl(lockobject *self) +/*[clinic end generated code: output=63bb94e5a9efa382 input=d8e3d64861bbce73]*/ { - lockobject *self = lockobject_CAST(op); return PyBool_FromLong(PyMutex_IsLocked(&self->lock)); } -PyDoc_STRVAR(locked_doc, -"locked($self, /)\n\ ---\n\ -\n\ -Return whether the lock is in the locked state."); +/*[clinic input] +_thread.lock.locked_lock -PyDoc_STRVAR(locked_lock_doc, -"locked_lock($self, /)\n\ ---\n\ -\n\ -An obsolete synonym of locked()."); +An obsolete synonym of locked(). +[clinic start generated code]*/ + +static PyObject * +_thread_lock_locked_lock_impl(lockobject *self) +/*[clinic end generated code: output=f747c8329e905f8e input=500b0c3592f9bf84]*/ +{ + return _thread_lock_locked_impl(self); +} static PyObject * lock_repr(PyObject *op) @@ -932,10 +979,14 @@ lock_repr(PyObject *op) } #ifdef HAVE_FORK +/*[clinic input] +_thread.lock._at_fork_reinit +[clinic start generated code]*/ + static PyObject * -lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock__at_fork_reinit_impl(lockobject *self) +/*[clinic end generated code: output=d8609f2d3bfa1fd5 input=a970cb76e2a0a131]*/ { - lockobject *self = lockobject_CAST(op); _PyMutex_at_fork_reinit(&self->lock); Py_RETURN_NONE; } @@ -960,25 +1011,16 @@ lock_new_impl(PyTypeObject *type) static PyMethodDef lock_methods[] = { - {"acquire_lock", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, acquire_lock_doc}, - {"acquire", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, acquire_doc}, - {"release_lock", lock_PyThread_release_lock, - METH_NOARGS, release_lock_doc}, - {"release", lock_PyThread_release_lock, - METH_NOARGS, release_doc}, - {"locked_lock", lock_locked_lock, - METH_NOARGS, locked_lock_doc}, - {"locked", lock_locked_lock, - METH_NOARGS, locked_doc}, - {"__enter__", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, enter_doc}, - {"__exit__", lock_PyThread_release_lock, - METH_VARARGS, lock_exit_doc}, + _THREAD_LOCK_ACQUIRE_LOCK_METHODDEF + _THREAD_LOCK_ACQUIRE_METHODDEF + _THREAD_LOCK_RELEASE_LOCK_METHODDEF + _THREAD_LOCK_RELEASE_METHODDEF + _THREAD_LOCK_LOCKED_LOCK_METHODDEF + _THREAD_LOCK_LOCKED_METHODDEF + _THREAD_LOCK___ENTER___METHODDEF + _THREAD_LOCK___EXIT___METHODDEF #ifdef HAVE_FORK - {"_at_fork_reinit", lock__at_fork_reinit, - METH_NOARGS, NULL}, + _THREAD_LOCK__AT_FORK_REINIT_METHODDEF #endif {NULL, NULL} /* sentinel */ }; @@ -1018,13 +1060,6 @@ static PyType_Spec lock_type_spec = { /* Recursive lock objects */ -typedef struct { - PyObject_HEAD - _PyRecursiveMutex lock; -} rlockobject; - -#define rlockobject_CAST(op) ((rlockobject *)(op)) - static int rlock_traverse(PyObject *self, visitproc visit, void *arg) { @@ -1048,14 +1083,35 @@ rlock_dealloc(PyObject *self) Py_DECREF(tp); } +/*[clinic input] +_thread.RLock.acquire + blocking: bool = True + timeout as timeoutobj: object(py_default="-1") = NULL + +Lock the lock. + +`blocking` indicates whether we should wait +for the lock to be available or not. If `blocking` is False +and another thread holds the lock, the method will return False +immediately. If `blocking` is True and another thread holds +the lock, the method will wait for the lock to be released, +take it and then return True. +(note: the blocking operation is interruptible.) + +In all other cases, the method will return True immediately. +Precisely, if the current thread already holds the lock, its +internal counter is simply incremented. If nobody holds the lock, +the lock is taken and its internal counter initialized to 1. +[clinic start generated code]*/ static PyObject * -rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) +_thread_RLock_acquire_impl(rlockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=73df5af6f67c1513 input=d55a0f5014522a8d]*/ { - rlockobject *self = rlockobject_CAST(op); PyTime_t timeout; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { + if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { return NULL; } @@ -1073,33 +1129,38 @@ rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) return PyBool_FromLong(r == PY_LOCK_ACQUIRED); } -PyDoc_STRVAR(rlock_acquire_doc, -"acquire($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -Lock the lock. `blocking` indicates whether we should wait\n\ -for the lock to be available or not. If `blocking` is False\n\ -and another thread holds the lock, the method will return False\n\ -immediately. If `blocking` is True and another thread holds\n\ -the lock, the method will wait for the lock to be released,\n\ -take it and then return True.\n\ -(note: the blocking operation is interruptible.)\n\ -\n\ -In all other cases, the method will return True immediately.\n\ -Precisely, if the current thread already holds the lock, its\n\ -internal counter is simply incremented. If nobody holds the lock,\n\ -the lock is taken and its internal counter initialized to 1."); +/*[clinic input] +_thread.RLock.__enter__ -PyDoc_STRVAR(rlock_enter_doc, -"__enter__($self, /)\n\ ---\n\ -\n\ -Lock the lock."); +Lock the lock. +[clinic start generated code]*/ static PyObject * -rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock___enter___impl(rlockobject *self) +/*[clinic end generated code: output=63135898476bf89f input=33be37f459dca390]*/ +{ + return _thread_RLock_acquire_impl(self, 1, NULL); +} + +/*[clinic input] +_thread.RLock.release + +Release the lock. + +Allows another thread that is blocked waiting for +the lock to acquire the lock. The lock must be in the locked state, +and must be locked by the same thread that unlocks it; otherwise a +`RuntimeError` is raised. + +Do note that if the lock was acquire()d several times in a row by the +current thread, release() needs to be called as many times for the lock +to be available for other threads. +[clinic start generated code]*/ + +static PyObject * +_thread_RLock_release_impl(rlockobject *self) +/*[clinic end generated code: output=51f4a013c5fae2c5 input=d425daf1a5782e63]*/ { - rlockobject *self = rlockobject_CAST(op); if (_PyRecursiveMutex_TryUnlock(&self->lock) < 0) { PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); @@ -1108,46 +1169,55 @@ rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy)) Py_RETURN_NONE; } -PyDoc_STRVAR(rlock_release_doc, -"release($self, /)\n\ ---\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -and must be locked by the same thread that unlocks it; otherwise a\n\ -`RuntimeError` is raised.\n\ -\n\ -Do note that if the lock was acquire()d several times in a row by the\n\ -current thread, release() needs to be called as many times for the lock\n\ -to be available for other threads."); +/*[clinic input] +_thread.RLock.__exit__ + exc_type: object + exc_value: object + exc_tb: object + / -PyDoc_STRVAR(rlock_exit_doc, -"__exit__($self, /, *exc_info)\n\ ---\n\ -\n\ -Release the lock."); +Release the lock. + +[clinic start generated code]*/ static PyObject * -rlock_locked(PyObject *op, PyObject *Py_UNUSED(ignored)) +_thread_RLock___exit___impl(rlockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb) +/*[clinic end generated code: output=79bb44d551aedeb5 input=79accf0778d91002]*/ +{ + return _thread_RLock_release_impl(self); +} + +/*[clinic input] +_thread.RLock.locked + +Return a boolean indicating whether this object is locked right now. +[clinic start generated code]*/ + +static PyObject * +_thread_RLock_locked_impl(rlockobject *self) +/*[clinic end generated code: output=e9b6060492b3f94e input=8866d9237ba5391b]*/ { - rlockobject *self = rlockobject_CAST(op); int is_locked = rlock_locked_impl(self); return PyBool_FromLong(is_locked); } -PyDoc_STRVAR(rlock_locked_doc, -"locked()\n\ -\n\ -Return a boolean indicating whether this object is locked right now."); +/*[clinic input] +_thread.RLock._acquire_restore + state: object + / + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_acquire_restore(PyObject *op, PyObject *args) +_thread_RLock__acquire_restore_impl(rlockobject *self, PyObject *state) +/*[clinic end generated code: output=beb8f2713a35e775 input=c8f2094fde059447]*/ { - rlockobject *self = rlockobject_CAST(op); PyThread_ident_t owner; Py_ssize_t count; - if (!PyArg_ParseTuple(args, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", + if (!PyArg_Parse(state, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", &count, &owner)) return NULL; @@ -1157,17 +1227,17 @@ rlock_acquire_restore(PyObject *op, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(rlock_acquire_restore_doc, -"_acquire_restore($self, state, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); + +/*[clinic input] +_thread.RLock._release_save + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__release_save_impl(rlockobject *self) +/*[clinic end generated code: output=d2916487315bea93 input=809d227cfc4a112c]*/ { - rlockobject *self = rlockobject_CAST(op); - if (!_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); @@ -1181,42 +1251,38 @@ rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy)) return Py_BuildValue("n" Py_PARSE_THREAD_IDENT_T, count, owner); } -PyDoc_STRVAR(rlock_release_save_doc, -"_release_save($self, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); + +/*[clinic input] +_thread.RLock._recursion_count + +For internal use by reentrancy checks. +[clinic start generated code]*/ static PyObject * -rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__recursion_count_impl(rlockobject *self) +/*[clinic end generated code: output=7993fb9695ef2c4d input=7fd1834cd7a4b044]*/ { - rlockobject *self = rlockobject_CAST(op); if (_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { return PyLong_FromSize_t(self->lock.level + 1); } return PyLong_FromLong(0); } -PyDoc_STRVAR(rlock_recursion_count_doc, -"_recursion_count($self, /)\n\ ---\n\ -\n\ -For internal use by reentrancy checks."); + +/*[clinic input] +_thread.RLock._is_owned + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__is_owned_impl(rlockobject *self) +/*[clinic end generated code: output=bf14268a3cabbe07 input=fba6535538deb858]*/ { - rlockobject *self = rlockobject_CAST(op); long owned = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock); return PyBool_FromLong(owned); } -PyDoc_STRVAR(rlock_is_owned_doc, -"_is_owned($self, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); - /*[clinic input] @classmethod _thread.RLock.__new__ as rlock_new @@ -1256,10 +1322,14 @@ rlock_repr(PyObject *op) #ifdef HAVE_FORK +/*[clinic input] +_thread.RLock._at_fork_reinit +[clinic start generated code]*/ + static PyObject * -rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__at_fork_reinit_impl(rlockobject *self) +/*[clinic end generated code: output=d77a4ce40351817c input=a3b625b026a8df4f]*/ { - rlockobject *self = rlockobject_CAST(op); self->lock = (_PyRecursiveMutex){0}; Py_RETURN_NONE; } @@ -1267,27 +1337,17 @@ rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) static PyMethodDef rlock_methods[] = { - {"acquire", _PyCFunction_CAST(rlock_acquire), - METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, - {"release", rlock_release, - METH_NOARGS, rlock_release_doc}, - {"locked", rlock_locked, - METH_NOARGS, rlock_locked_doc}, - {"_is_owned", rlock_is_owned, - METH_NOARGS, rlock_is_owned_doc}, - {"_acquire_restore", rlock_acquire_restore, - METH_VARARGS, rlock_acquire_restore_doc}, - {"_release_save", rlock_release_save, - METH_NOARGS, rlock_release_save_doc}, - {"_recursion_count", rlock_recursion_count, - METH_NOARGS, rlock_recursion_count_doc}, - {"__enter__", _PyCFunction_CAST(rlock_acquire), - METH_VARARGS | METH_KEYWORDS, rlock_enter_doc}, - {"__exit__", rlock_release, - METH_VARARGS, rlock_exit_doc}, + _THREAD_RLOCK_ACQUIRE_METHODDEF + _THREAD_RLOCK_RELEASE_METHODDEF + _THREAD_RLOCK_LOCKED_METHODDEF + _THREAD_RLOCK__IS_OWNED_METHODDEF + _THREAD_RLOCK__ACQUIRE_RESTORE_METHODDEF + _THREAD_RLOCK__RELEASE_SAVE_METHODDEF + _THREAD_RLOCK__RECURSION_COUNT_METHODDEF + _THREAD_RLOCK___ENTER___METHODDEF + _THREAD_RLOCK___EXIT___METHODDEF #ifdef HAVE_FORK - {"_at_fork_reinit", rlock__at_fork_reinit, - METH_NOARGS, NULL}, + _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF #endif {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h index fd8e32a2261d38..8455f9929babc1 100644 --- a/Modules/clinic/_threadmodule.c.h +++ b/Modules/clinic/_threadmodule.c.h @@ -6,7 +6,310 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_modsupport.h" // _PyArg_NoKeywords() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_thread_lock_acquire__doc__, +"acquire($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"Lock the lock.\n" +"\n" +"Without argument, this blocks if the lock is already\n" +"locked (even by the same thread), waiting for another thread to release\n" +"the lock, and return True once the lock is acquired.\n" +"With an argument, this will only block if the argument is true,\n" +"and the return value reflects whether the lock is acquired.\n" +"The blocking operation is interruptible."); + +#define _THREAD_LOCK_ACQUIRE_METHODDEF \ + {"acquire", _PyCFunction_CAST(_thread_lock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire__doc__}, + +static PyObject * +_thread_lock_acquire_impl(lockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_lock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_lock_acquire_impl((lockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_acquire_lock__doc__, +"acquire_lock($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"An obsolete synonym of acquire()."); + +#define _THREAD_LOCK_ACQUIRE_LOCK_METHODDEF \ + {"acquire_lock", _PyCFunction_CAST(_thread_lock_acquire_lock), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire_lock__doc__}, + +static PyObject * +_thread_lock_acquire_lock_impl(lockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_lock_acquire_lock(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire_lock", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_lock_acquire_lock_impl((lockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_release__doc__, +"release($self, /)\n" +"--\n" +"\n" +"Release the lock.\n" +"\n" +"Allows another thread that is blocked waiting for\n" +"the lock to acquire the lock. The lock must be in the locked state,\n" +"but it needn\'t be locked by the same thread that unlocks it."); + +#define _THREAD_LOCK_RELEASE_METHODDEF \ + {"release", (PyCFunction)_thread_lock_release, METH_NOARGS, _thread_lock_release__doc__}, + +static PyObject * +_thread_lock_release_impl(lockobject *self); + +static PyObject * +_thread_lock_release(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_release_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock_release_lock__doc__, +"release_lock($self, /)\n" +"--\n" +"\n" +"An obsolete synonym of release()."); + +#define _THREAD_LOCK_RELEASE_LOCK_METHODDEF \ + {"release_lock", (PyCFunction)_thread_lock_release_lock, METH_NOARGS, _thread_lock_release_lock__doc__}, + +static PyObject * +_thread_lock_release_lock_impl(lockobject *self); + +static PyObject * +_thread_lock_release_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_release_lock_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Lock the lock."); + +#define _THREAD_LOCK___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_thread_lock___enter__, METH_NOARGS, _thread_lock___enter____doc__}, + +static PyObject * +_thread_lock___enter___impl(lockobject *self); + +static PyObject * +_thread_lock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock___enter___impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock___exit____doc__, +"__exit__($self, exc_type, exc_value, exc_tb, /)\n" +"--\n" +"\n" +"Release the lock."); + +#define _THREAD_LOCK___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_thread_lock___exit__), METH_FASTCALL, _thread_lock___exit____doc__}, + +static PyObject * +_thread_lock___exit___impl(lockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb); + +static PyObject * +_thread_lock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + exc_tb = args[2]; + return_value = _thread_lock___exit___impl((lockobject *)self, exc_type, exc_value, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_locked__doc__, +"locked($self, /)\n" +"--\n" +"\n" +"Return whether the lock is in the locked state."); + +#define _THREAD_LOCK_LOCKED_METHODDEF \ + {"locked", (PyCFunction)_thread_lock_locked, METH_NOARGS, _thread_lock_locked__doc__}, + +static PyObject * +_thread_lock_locked_impl(lockobject *self); + +static PyObject * +_thread_lock_locked(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_locked_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock_locked_lock__doc__, +"locked_lock($self, /)\n" +"--\n" +"\n" +"An obsolete synonym of locked()."); + +#define _THREAD_LOCK_LOCKED_LOCK_METHODDEF \ + {"locked_lock", (PyCFunction)_thread_lock_locked_lock, METH_NOARGS, _thread_lock_locked_lock__doc__}, + +static PyObject * +_thread_lock_locked_lock_impl(lockobject *self); + +static PyObject * +_thread_lock_locked_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_locked_lock_impl((lockobject *)self); +} + +#if defined(HAVE_FORK) + +PyDoc_STRVAR(_thread_lock__at_fork_reinit__doc__, +"_at_fork_reinit($self, /)\n" +"--\n" +"\n"); + +#define _THREAD_LOCK__AT_FORK_REINIT_METHODDEF \ + {"_at_fork_reinit", (PyCFunction)_thread_lock__at_fork_reinit, METH_NOARGS, _thread_lock__at_fork_reinit__doc__}, + +static PyObject * +_thread_lock__at_fork_reinit_impl(lockobject *self); + +static PyObject * +_thread_lock__at_fork_reinit(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock__at_fork_reinit_impl((lockobject *)self); +} + +#endif /* defined(HAVE_FORK) */ static PyObject * lock_new_impl(PyTypeObject *type); @@ -31,6 +334,265 @@ lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +PyDoc_STRVAR(_thread_RLock_acquire__doc__, +"acquire($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"Lock the lock.\n" +"\n" +"`blocking` indicates whether we should wait\n" +"for the lock to be available or not. If `blocking` is False\n" +"and another thread holds the lock, the method will return False\n" +"immediately. If `blocking` is True and another thread holds\n" +"the lock, the method will wait for the lock to be released,\n" +"take it and then return True.\n" +"(note: the blocking operation is interruptible.)\n" +"\n" +"In all other cases, the method will return True immediately.\n" +"Precisely, if the current thread already holds the lock, its\n" +"internal counter is simply incremented. If nobody holds the lock,\n" +"the lock is taken and its internal counter initialized to 1."); + +#define _THREAD_RLOCK_ACQUIRE_METHODDEF \ + {"acquire", _PyCFunction_CAST(_thread_RLock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_RLock_acquire__doc__}, + +static PyObject * +_thread_RLock_acquire_impl(rlockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_RLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_RLock_acquire_impl((rlockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_RLock___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Lock the lock."); + +#define _THREAD_RLOCK___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_thread_RLock___enter__, METH_NOARGS, _thread_RLock___enter____doc__}, + +static PyObject * +_thread_RLock___enter___impl(rlockobject *self); + +static PyObject * +_thread_RLock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock___enter___impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock_release__doc__, +"release($self, /)\n" +"--\n" +"\n" +"Release the lock.\n" +"\n" +"Allows another thread that is blocked waiting for\n" +"the lock to acquire the lock. The lock must be in the locked state,\n" +"and must be locked by the same thread that unlocks it; otherwise a\n" +"`RuntimeError` is raised.\n" +"\n" +"Do note that if the lock was acquire()d several times in a row by the\n" +"current thread, release() needs to be called as many times for the lock\n" +"to be available for other threads."); + +#define _THREAD_RLOCK_RELEASE_METHODDEF \ + {"release", (PyCFunction)_thread_RLock_release, METH_NOARGS, _thread_RLock_release__doc__}, + +static PyObject * +_thread_RLock_release_impl(rlockobject *self); + +static PyObject * +_thread_RLock_release(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock_release_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock___exit____doc__, +"__exit__($self, exc_type, exc_value, exc_tb, /)\n" +"--\n" +"\n" +"Release the lock."); + +#define _THREAD_RLOCK___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_thread_RLock___exit__), METH_FASTCALL, _thread_RLock___exit____doc__}, + +static PyObject * +_thread_RLock___exit___impl(rlockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb); + +static PyObject * +_thread_RLock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + exc_tb = args[2]; + return_value = _thread_RLock___exit___impl((rlockobject *)self, exc_type, exc_value, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_RLock_locked__doc__, +"locked($self, /)\n" +"--\n" +"\n" +"Return a boolean indicating whether this object is locked right now."); + +#define _THREAD_RLOCK_LOCKED_METHODDEF \ + {"locked", (PyCFunction)_thread_RLock_locked, METH_NOARGS, _thread_RLock_locked__doc__}, + +static PyObject * +_thread_RLock_locked_impl(rlockobject *self); + +static PyObject * +_thread_RLock_locked(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock_locked_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__acquire_restore__doc__, +"_acquire_restore($self, state, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__ACQUIRE_RESTORE_METHODDEF \ + {"_acquire_restore", (PyCFunction)_thread_RLock__acquire_restore, METH_O, _thread_RLock__acquire_restore__doc__}, + +static PyObject * +_thread_RLock__acquire_restore_impl(rlockobject *self, PyObject *state); + +static PyObject * +_thread_RLock__acquire_restore(PyObject *self, PyObject *state) +{ + PyObject *return_value = NULL; + + return_value = _thread_RLock__acquire_restore_impl((rlockobject *)self, state); + + return return_value; +} + +PyDoc_STRVAR(_thread_RLock__release_save__doc__, +"_release_save($self, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__RELEASE_SAVE_METHODDEF \ + {"_release_save", (PyCFunction)_thread_RLock__release_save, METH_NOARGS, _thread_RLock__release_save__doc__}, + +static PyObject * +_thread_RLock__release_save_impl(rlockobject *self); + +static PyObject * +_thread_RLock__release_save(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__release_save_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__recursion_count__doc__, +"_recursion_count($self, /)\n" +"--\n" +"\n" +"For internal use by reentrancy checks."); + +#define _THREAD_RLOCK__RECURSION_COUNT_METHODDEF \ + {"_recursion_count", (PyCFunction)_thread_RLock__recursion_count, METH_NOARGS, _thread_RLock__recursion_count__doc__}, + +static PyObject * +_thread_RLock__recursion_count_impl(rlockobject *self); + +static PyObject * +_thread_RLock__recursion_count(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__recursion_count_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__is_owned__doc__, +"_is_owned($self, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__IS_OWNED_METHODDEF \ + {"_is_owned", (PyCFunction)_thread_RLock__is_owned, METH_NOARGS, _thread_RLock__is_owned__doc__}, + +static PyObject * +_thread_RLock__is_owned_impl(rlockobject *self); + +static PyObject * +_thread_RLock__is_owned(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__is_owned_impl((rlockobject *)self); +} + static PyObject * rlock_new_impl(PyTypeObject *type); @@ -54,6 +616,27 @@ rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +#if defined(HAVE_FORK) + +PyDoc_STRVAR(_thread_RLock__at_fork_reinit__doc__, +"_at_fork_reinit($self, /)\n" +"--\n" +"\n"); + +#define _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF \ + {"_at_fork_reinit", (PyCFunction)_thread_RLock__at_fork_reinit, METH_NOARGS, _thread_RLock__at_fork_reinit__doc__}, + +static PyObject * +_thread_RLock__at_fork_reinit_impl(rlockobject *self); + +static PyObject * +_thread_RLock__at_fork_reinit(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__at_fork_reinit_impl((rlockobject *)self); +} + +#endif /* defined(HAVE_FORK) */ + #if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) || defined(MS_WINDOWS)) PyDoc_STRVAR(_thread__get_name__doc__, @@ -142,6 +725,14 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #endif /* (defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP) || defined(MS_WINDOWS)) */ +#ifndef _THREAD_LOCK__AT_FORK_REINIT_METHODDEF + #define _THREAD_LOCK__AT_FORK_REINIT_METHODDEF +#endif /* !defined(_THREAD_LOCK__AT_FORK_REINIT_METHODDEF) */ + +#ifndef _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF + #define _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF +#endif /* !defined(_THREAD_RLOCK__AT_FORK_REINIT_METHODDEF) */ + #ifndef _THREAD__GET_NAME_METHODDEF #define _THREAD__GET_NAME_METHODDEF #endif /* !defined(_THREAD__GET_NAME_METHODDEF) */ @@ -149,4 +740,4 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _THREAD_SET_NAME_METHODDEF #define _THREAD_SET_NAME_METHODDEF #endif /* !defined(_THREAD_SET_NAME_METHODDEF) */ -/*[clinic end generated code: output=b381ec5e313198e7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1255a1520f43f97a input=a9049054013a1b77]*/