LCOV - code coverage report
Current view: | top level - Modules - _threadmodule.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 264 | 630 | 41.9 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 30 | 51 | 58.8 %
|  |  | Branches: | 116 | 362 | 32.0 %

               Branch data     Line data    Source code
           1                 :            :
           2                 :            : /* Thread module */
           3                 :            : /* Interface to Sjoerd's portable C thread library */
           4                 :            :
           5                 :            : #include "Python.h"
           6                 :            : #include "pycore_interp.h"        // _PyInterpreterState.threads.count
           7                 :            : #include "pycore_moduleobject.h"  // _PyModule_GetState()
           8                 :            : #include "pycore_pylifecycle.h"
           9                 :            : #include "pycore_pystate.h"       // _PyThreadState_SetCurrent()
          10                 :            : #include <stddef.h>               // offsetof()
          11                 :            : #include "structmember.h"         // PyMemberDef
          12                 :            :
          13                 :            : #ifdef HAVE_SIGNAL_H
          14                 :            : #  include <signal.h>             // SIGINT
          15                 :            : #endif
          16                 :            :
          17                 :            : // ThreadError is just an alias to PyExc_RuntimeError
          18                 :            : #define ThreadError PyExc_RuntimeError
          19                 :            :
          20                 :            :
          21                 :            : // Forward declarations
          22                 :            : static struct PyModuleDef thread_module;
          23                 :            :
          24                 :            :
          25                 :            : typedef struct {
          26                 :            :     PyTypeObject *excepthook_type;
          27                 :            :     PyTypeObject *lock_type;
          28                 :            :     PyTypeObject *local_type;
          29                 :            :     PyTypeObject *local_dummy_type;
          30                 :            : } thread_module_state;
          31                 :            :
          32                 :            : static inline thread_module_state*
          33                 :       1383 : get_thread_state(PyObject *module)
          34                 :            : {
          35                 :       1383 :     void *state = _PyModule_GetState(module);
          36                 :            :     assert(state != NULL);
          37                 :       1383 :     return (thread_module_state *)state;
          38                 :            : }
          39                 :            :
          40                 :            :
          41                 :            : /* Lock objects */
          42                 :            :
          43                 :            : typedef struct {
          44                 :            :     PyObject_HEAD
          45                 :            :     PyThread_type_lock lock_lock;
          46                 :            :     PyObject *in_weakreflist;
          47                 :            :     char locked; /* for sanity checking */
          48                 :            : } lockobject;
          49                 :            :
          50                 :            : static int
          51                 :        756 : lock_traverse(lockobject *self, visitproc visit, void *arg)
          52                 :            : {
          53   [ +  -  -  + ]:        756 :     Py_VISIT(Py_TYPE(self));
          54                 :        756 :     return 0;
          55                 :            : }
          56                 :            :
          57                 :            : static void
          58                 :       1061 : lock_dealloc(lockobject *self)
          59                 :            : {
          60                 :       1061 :     PyObject_GC_UnTrack(self);
          61         [ +  + ]:       1061 :     if (self->in_weakreflist != NULL) {
          62                 :          4 :         PyObject_ClearWeakRefs((PyObject *) self);
          63                 :            :     }
          64         [ +  - ]:       1061 :     if (self->lock_lock != NULL) {
          65                 :            :         /* Unlock the lock so it's safe to free it */
          66         [ -  + ]:       1061 :         if (self->locked)
          67                 :          0 :             PyThread_release_lock(self->lock_lock);
          68                 :       1061 :         PyThread_free_lock(self->lock_lock);
          69                 :            :     }
          70                 :       1061 :     PyTypeObject *tp = Py_TYPE(self);
          71                 :       1061 :     tp->tp_free((PyObject*)self);
          72                 :       1061 :     Py_DECREF(tp);
          73                 :       1061 : }
          74                 :            :
          75                 :            : /* Helper to acquire an interruptible lock with a timeout.  If the lock acquire
          76                 :            :  * is interrupted, signal handlers are run, and if they raise an exception,
          77                 :            :  * PY_LOCK_INTR is returned.  Otherwise, PY_LOCK_ACQUIRED or PY_LOCK_FAILURE
          78                 :            :  * are returned, depending on whether the lock can be acquired within the
          79                 :            :  * timeout.
          80                 :            :  */
          81                 :            : static PyLockStatus
          82                 :       2605 : acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
          83                 :            : {
          84                 :       2605 :     _PyTime_t endtime = 0;
          85         [ -  + ]:       2605 :     if (timeout > 0) {
          86                 :          0 :         endtime = _PyDeadline_Init(timeout);
          87                 :            :     }
          88                 :            :
          89                 :            :     PyLockStatus r;
          90                 :            :     do {
          91                 :            :         _PyTime_t microseconds;
          92                 :       2605 :         microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
          93                 :            :
          94                 :            :         /* first a simple non-blocking try without releasing the GIL */
          95                 :       2605 :         r = PyThread_acquire_lock_timed(lock, 0, 0);
          96   [ +  +  -  + ]:       2605 :         if (r == PY_LOCK_FAILURE && microseconds != 0) {
          97                 :          0 :             Py_BEGIN_ALLOW_THREADS
          98                 :          0 :             r = PyThread_acquire_lock_timed(lock, microseconds, 1);
          99                 :          0 :             Py_END_ALLOW_THREADS
         100                 :            :         }
         101                 :            :
         102         [ -  + ]:       2605 :         if (r == PY_LOCK_INTR) {
         103                 :            :             /* Run signal handlers if we were interrupted.  Propagate
         104                 :            :              * exceptions from signal handlers, such as KeyboardInterrupt, by
         105                 :            :              * passing up PY_LOCK_INTR.  */
         106         [ #  # ]:          0 :             if (Py_MakePendingCalls() < 0) {
         107                 :          0 :                 return PY_LOCK_INTR;
         108                 :            :             }
         109                 :            :
         110                 :            :             /* If we're using a timeout, recompute the timeout after processing
         111                 :            :              * signals, since those can take time.  */
         112         [ #  # ]:          0 :             if (timeout > 0) {
         113                 :          0 :                 timeout = _PyDeadline_Get(endtime);
         114                 :            :
         115                 :            :                 /* Check for negative values, since those mean block forever.
         116                 :            :                  */
         117         [ #  # ]:          0 :                 if (timeout < 0) {
         118                 :          0 :                     r = PY_LOCK_FAILURE;
         119                 :            :                 }
         120                 :            :             }
         121                 :            :         }
         122         [ -  + ]:       2605 :     } while (r == PY_LOCK_INTR);  /* Retry if we were interrupted. */
         123                 :            :
         124                 :       2605 :     return r;
         125                 :            : }
         126                 :            :
         127                 :            : static int
         128                 :       2611 : lock_acquire_parse_args(PyObject *args, PyObject *kwds,
         129                 :            :                         _PyTime_t *timeout)
         130                 :            : {
         131                 :       2611 :     char *kwlist[] = {"blocking", "timeout", NULL};
         132                 :       2611 :     int blocking = 1;
         133                 :       2611 :     PyObject *timeout_obj = NULL;
         134                 :       2611 :     const _PyTime_t unset_timeout = _PyTime_FromSeconds(-1);
         135                 :            :
         136                 :       2611 :     *timeout = unset_timeout ;
         137                 :            :
         138         [ -  + ]:       2611 :     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pO:acquire", kwlist,
         139                 :            :                                      &blocking, &timeout_obj))
         140                 :          0 :         return -1;
         141                 :            :
         142         [ -  + ]:       2611 :     if (timeout_obj
         143         [ #  # ]:          0 :         && _PyTime_FromSecondsObject(timeout,
         144                 :            :                                      timeout_obj, _PyTime_ROUND_TIMEOUT) < 0)
         145                 :          0 :         return -1;
         146                 :            :
         147   [ +  +  -  + ]:       2611 :     if (!blocking && *timeout != unset_timeout ) {
         148                 :          0 :         PyErr_SetString(PyExc_ValueError,
         149                 :            :                         "can't specify a timeout for a non-blocking call");
         150                 :          0 :         return -1;
         151                 :            :     }
         152   [ +  -  -  + ]:       2611 :     if (*timeout < 0 && *timeout != unset_timeout) {
         153                 :          0 :         PyErr_SetString(PyExc_ValueError,
         154                 :            :                         "timeout value must be positive");
         155                 :          0 :         return -1;
         156                 :            :     }
         157         [ +  + ]:       2611 :     if (!blocking)
         158                 :          4 :         *timeout = 0;
         159         [ -  + ]:       2607 :     else if (*timeout != unset_timeout) {
         160                 :            :         _PyTime_t microseconds;
         161                 :            :
         162                 :          0 :         microseconds = _PyTime_AsMicroseconds(*timeout, _PyTime_ROUND_TIMEOUT);
         163         [ #  # ]:          0 :         if (microseconds > PY_TIMEOUT_MAX) {
         164                 :          0 :             PyErr_SetString(PyExc_OverflowError,
         165                 :            :                             "timeout value is too large");
         166                 :          0 :             return -1;
         167                 :            :         }
         168                 :            :     }
         169                 :       2611 :     return 0;
         170                 :            : }
         171                 :            :
         172                 :            : static PyObject *
         173                 :         24 : lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
         174                 :            : {
         175                 :            :     _PyTime_t timeout;
         176         [ -  + ]:         24 :     if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
         177                 :          0 :         return NULL;
         178                 :            :
         179                 :         24 :     PyLockStatus r = acquire_timed(self->lock_lock, timeout);
         180         [ -  + ]:         24 :     if (r == PY_LOCK_INTR) {
         181                 :          0 :         return NULL;
         182                 :            :     }
         183                 :            :
         184         [ +  + ]:         24 :     if (r == PY_LOCK_ACQUIRED)
         185                 :         20 :         self->locked = 1;
         186                 :         24 :     return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
         187                 :            : }
         188                 :            :
         189                 :            : PyDoc_STRVAR(acquire_doc,
         190                 :            : "acquire(blocking=True, timeout=-1) -> bool\n\
         191                 :            : (acquire_lock() is an obsolete synonym)\n\
         192                 :            : \n\
         193                 :            : Lock the lock.  Without argument, this blocks if the lock is already\n\
         194                 :            : locked (even by the same thread), waiting for another thread to release\n\
         195                 :            : the lock, and return True once the lock is acquired.\n\
         196                 :            : With an argument, this will only block if the argument is true,\n\
         197                 :            : and the return value reflects whether the lock is acquired.\n\
         198                 :            : The blocking operation is interruptible.");
         199                 :            :
         200                 :            : static PyObject *
         201                 :         20 : lock_PyThread_release_lock(lockobject *self, PyObject *Py_UNUSED(ignored))
         202                 :            : {
         203                 :            :     /* Sanity check: the lock must be locked */
         204         [ -  + ]:         20 :     if (!self->locked) {
         205                 :          0 :         PyErr_SetString(ThreadError, "release unlocked lock");
         206                 :          0 :         return NULL;
         207                 :            :     }
         208                 :            :
         209                 :         20 :     PyThread_release_lock(self->lock_lock);
         210                 :         20 :     self->locked = 0;
         211                 :         20 :     Py_RETURN_NONE;
         212                 :            : }
         213                 :            :
         214                 :            : PyDoc_STRVAR(release_doc,
         215                 :            : "release()\n\
         216                 :            : (release_lock() is an obsolete synonym)\n\
         217                 :            : \n\
         218                 :            : Release the lock, allowing another thread that is blocked waiting for\n\
         219                 :            : the lock to acquire the lock.  The lock must be in the locked state,\n\
         220                 :            : but it needn't be locked by the same thread that unlocks it.");
         221                 :            :
         222                 :            : static PyObject *
         223                 :         12 : lock_locked_lock(lockobject *self, PyObject *Py_UNUSED(ignored))
         224                 :            : {
         225                 :         12 :     return PyBool_FromLong((long)self->locked);
         226                 :            : }
         227                 :            :
         228                 :            : PyDoc_STRVAR(locked_doc,
         229                 :            : "locked() -> bool\n\
         230                 :            : (locked_lock() is an obsolete synonym)\n\
         231                 :            : \n\
         232                 :            : Return whether the lock is in the locked state.");
         233                 :            :
         234                 :            : static PyObject *
         235                 :          0 : lock_repr(lockobject *self)
         236                 :            : {
         237                 :          0 :     return PyUnicode_FromFormat("<%s %s object at %p>",
         238         [ #  # ]:          0 :         self->locked ? "locked" : "unlocked", Py_TYPE(self)->tp_name, self);
         239                 :            : }
         240                 :            :
         241                 :            : #ifdef HAVE_FORK
         242                 :            : static PyObject *
         243                 :          0 : lock__at_fork_reinit(lockobject *self, PyObject *Py_UNUSED(args))
         244                 :            : {
         245         [ #  # ]:          0 :     if (_PyThread_at_fork_reinit(&self->lock_lock) < 0) {
         246                 :          0 :         PyErr_SetString(ThreadError, "failed to reinitialize lock at fork");
         247                 :          0 :         return NULL;
         248                 :            :     }
         249                 :            :
         250                 :          0 :     self->locked = 0;
         251                 :            :
         252                 :          0 :     Py_RETURN_NONE;
         253                 :            : }
         254                 :            : #endif  /* HAVE_FORK */
         255                 :            :
         256                 :            :
         257                 :            : static PyMethodDef lock_methods[] = {
         258                 :            :     {"acquire_lock", _PyCFunction_CAST(lock_PyThread_acquire_lock),
         259                 :            :      METH_VARARGS | METH_KEYWORDS, acquire_doc},
         260                 :            :     {"acquire",      _PyCFunction_CAST(lock_PyThread_acquire_lock),
         261                 :            :      METH_VARARGS | METH_KEYWORDS, acquire_doc},
         262                 :            :     {"release_lock", (PyCFunction)lock_PyThread_release_lock,
         263                 :            :      METH_NOARGS, release_doc},
         264                 :            :     {"release",      (PyCFunction)lock_PyThread_release_lock,
         265                 :            :      METH_NOARGS, release_doc},
         266                 :            :     {"locked_lock",  (PyCFunction)lock_locked_lock,
         267                 :            :      METH_NOARGS, locked_doc},
         268                 :            :     {"locked",       (PyCFunction)lock_locked_lock,
         269                 :            :      METH_NOARGS, locked_doc},
         270                 :            :     {"__enter__",    _PyCFunction_CAST(lock_PyThread_acquire_lock),
         271                 :            :      METH_VARARGS | METH_KEYWORDS, acquire_doc},
         272                 :            :     {"__exit__",    (PyCFunction)lock_PyThread_release_lock,
         273                 :            :      METH_VARARGS, release_doc},
         274                 :            : #ifdef HAVE_FORK
         275                 :            :     {"_at_fork_reinit",    (PyCFunction)lock__at_fork_reinit,
         276                 :            :      METH_NOARGS, NULL},
         277                 :            : #endif
         278                 :            :     {NULL,           NULL}              /* sentinel */
         279                 :            : };
         280                 :            :
         281                 :            : PyDoc_STRVAR(lock_doc,
         282                 :            : "A lock object is a synchronization primitive.  To create a lock,\n\
         283                 :            : call threading.Lock().  Methods are:\n\
         284                 :            : \n\
         285                 :            : acquire() -- lock the lock, possibly blocking until it can be obtained\n\
         286                 :            : release() -- unlock of the lock\n\
         287                 :            : locked() -- test whether the lock is currently locked\n\
         288                 :            : \n\
         289                 :            : A lock is not owned by the thread that locked it; another thread may\n\
         290                 :            : unlock it.  A thread attempting to lock a lock that it has already locked\n\
         291                 :            : will block until another thread unlocks it.  Deadlocks may ensue.");
         292                 :            :
         293                 :            : static PyMemberDef lock_type_members[] = {
         294                 :            :     {"__weaklistoffset__", T_PYSSIZET, offsetof(lockobject, in_weakreflist), READONLY},
         295                 :            :     {NULL},
         296                 :            : };
         297                 :            :
         298                 :            : static PyType_Slot lock_type_slots[] = {
         299                 :            :     {Py_tp_dealloc, (destructor)lock_dealloc},
         300                 :            :     {Py_tp_repr, (reprfunc)lock_repr},
         301                 :            :     {Py_tp_doc, (void *)lock_doc},
         302                 :            :     {Py_tp_methods, lock_methods},
         303                 :            :     {Py_tp_traverse, lock_traverse},
         304                 :            :     {Py_tp_members, lock_type_members},
         305                 :            :     {0, 0}
         306                 :            : };
         307                 :            :
         308                 :            : static PyType_Spec lock_type_spec = {
         309                 :            :     .name = "_thread.lock",
         310                 :            :     .basicsize = sizeof(lockobject),
         311                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
         312                 :            :               Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
         313                 :            :     .slots = lock_type_slots,
         314                 :            : };
         315                 :            :
         316                 :            : /* Recursive lock objects */
         317                 :            :
         318                 :            : typedef struct {
         319                 :            :     PyObject_HEAD
         320                 :            :     PyThread_type_lock rlock_lock;
         321                 :            :     unsigned long rlock_owner;
         322                 :            :     unsigned long rlock_count;
         323                 :            :     PyObject *in_weakreflist;
         324                 :            : } rlockobject;
         325                 :            :
         326                 :            : static int
         327                 :        700 : rlock_traverse(rlockobject *self, visitproc visit, void *arg)
         328                 :            : {
         329   [ +  -  -  + ]:        700 :     Py_VISIT(Py_TYPE(self));
         330                 :        700 :     return 0;
         331                 :            : }
         332                 :            :
         333                 :            :
         334                 :            : static void
         335                 :       1057 : rlock_dealloc(rlockobject *self)
         336                 :            : {
         337                 :       1057 :     PyObject_GC_UnTrack(self);
         338         [ -  + ]:       1057 :     if (self->in_weakreflist != NULL)
         339                 :          0 :         PyObject_ClearWeakRefs((PyObject *) self);
         340                 :            :     /* self->rlock_lock can be NULL if PyThread_allocate_lock() failed
         341                 :            :        in rlock_new() */
         342         [ +  - ]:       1057 :     if (self->rlock_lock != NULL) {
         343                 :            :         /* Unlock the lock so it's safe to free it */
         344         [ -  + ]:       1057 :         if (self->rlock_count > 0)
         345                 :          0 :             PyThread_release_lock(self->rlock_lock);
         346                 :            :
         347                 :       1057 :         PyThread_free_lock(self->rlock_lock);
         348                 :            :     }
         349                 :       1057 :     PyTypeObject *tp = Py_TYPE(self);
         350                 :       1057 :     tp->tp_free(self);
         351                 :       1057 :     Py_DECREF(tp);
         352                 :       1057 : }
         353                 :            :
         354                 :            : static PyObject *
         355                 :       2587 : rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
         356                 :            : {
         357                 :            :     _PyTime_t timeout;
         358                 :            :     unsigned long tid;
         359                 :       2587 :     PyLockStatus r = PY_LOCK_ACQUIRED;
         360                 :            :
         361         [ -  + ]:       2587 :     if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
         362                 :          0 :         return NULL;
         363                 :            :
         364                 :       2587 :     tid = PyThread_get_thread_ident();
         365   [ +  +  +  - ]:       2587 :     if (self->rlock_count > 0 && tid == self->rlock_owner) {
         366                 :          6 :         unsigned long count = self->rlock_count + 1;
         367         [ -  + ]:          6 :         if (count <= self->rlock_count) {
         368                 :          0 :             PyErr_SetString(PyExc_OverflowError,
         369                 :            :                             "Internal lock count overflowed");
         370                 :          0 :             return NULL;
         371                 :            :         }
         372                 :          6 :         self->rlock_count = count;
         373                 :          6 :         Py_RETURN_TRUE;
         374                 :            :     }
         375                 :       2581 :     r = acquire_timed(self->rlock_lock, timeout);
         376         [ +  - ]:       2581 :     if (r == PY_LOCK_ACQUIRED) {
         377                 :            :         assert(self->rlock_count == 0);
         378                 :       2581 :         self->rlock_owner = tid;
         379                 :       2581 :         self->rlock_count = 1;
         380                 :            :     }
         381         [ #  # ]:          0 :     else if (r == PY_LOCK_INTR) {
         382                 :          0 :         return NULL;
         383                 :            :     }
         384                 :            :
         385                 :       2581 :     return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
         386                 :            : }
         387                 :            :
         388                 :            : PyDoc_STRVAR(rlock_acquire_doc,
         389                 :            : "acquire(blocking=True) -> bool\n\
         390                 :            : \n\
         391                 :            : Lock the lock.  `blocking` indicates whether we should wait\n\
         392                 :            : for the lock to be available or not.  If `blocking` is False\n\
         393                 :            : and another thread holds the lock, the method will return False\n\
         394                 :            : immediately.  If `blocking` is True and another thread holds\n\
         395                 :            : the lock, the method will wait for the lock to be released,\n\
         396                 :            : take it and then return True.\n\
         397                 :            : (note: the blocking operation is interruptible.)\n\
         398                 :            : \n\
         399                 :            : In all other cases, the method will return True immediately.\n\
         400                 :            : Precisely, if the current thread already holds the lock, its\n\
         401                 :            : internal counter is simply incremented. If nobody holds the lock,\n\
         402                 :            : the lock is taken and its internal counter initialized to 1.");
         403                 :            :
         404                 :            : static PyObject *
         405                 :       2587 : rlock_release(rlockobject *self, PyObject *Py_UNUSED(ignored))
         406                 :            : {
         407                 :       2587 :     unsigned long tid = PyThread_get_thread_ident();
         408                 :            :
         409   [ +  -  -  + ]:       2587 :     if (self->rlock_count == 0 || self->rlock_owner != tid) {
         410                 :          0 :         PyErr_SetString(PyExc_RuntimeError,
         411                 :            :                         "cannot release un-acquired lock");
         412                 :          0 :         return NULL;
         413                 :            :     }
         414         [ +  + ]:       2587 :     if (--self->rlock_count == 0) {
         415                 :       2581 :         self->rlock_owner = 0;
         416                 :       2581 :         PyThread_release_lock(self->rlock_lock);
         417                 :            :     }
         418                 :       2587 :     Py_RETURN_NONE;
         419                 :            : }
         420                 :            :
         421                 :            : PyDoc_STRVAR(rlock_release_doc,
         422                 :            : "release()\n\
         423                 :            : \n\
         424                 :            : Release the lock, allowing another thread that is blocked waiting for\n\
         425                 :            : the lock to acquire the lock.  The lock must be in the locked state,\n\
         426                 :            : and must be locked by the same thread that unlocks it; otherwise a\n\
         427                 :            : `RuntimeError` is raised.\n\
         428                 :            : \n\
         429                 :            : Do note that if the lock was acquire()d several times in a row by the\n\
         430                 :            : current thread, release() needs to be called as many times for the lock\n\
         431                 :            : to be available for other threads.");
         432                 :            :
         433                 :            : static PyObject *
         434                 :          0 : rlock_acquire_restore(rlockobject *self, PyObject *args)
         435                 :            : {
         436                 :            :     unsigned long owner;
         437                 :            :     unsigned long count;
         438                 :          0 :     int r = 1;
         439                 :            :
         440         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "(kk):_acquire_restore", &count, &owner))
         441                 :          0 :         return NULL;
         442                 :            :
         443         [ #  # ]:          0 :     if (!PyThread_acquire_lock(self->rlock_lock, 0)) {
         444                 :          0 :         Py_BEGIN_ALLOW_THREADS
         445                 :          0 :         r = PyThread_acquire_lock(self->rlock_lock, 1);
         446                 :          0 :         Py_END_ALLOW_THREADS
         447                 :            :     }
         448         [ #  # ]:          0 :     if (!r) {
         449                 :          0 :         PyErr_SetString(ThreadError, "couldn't acquire lock");
         450                 :          0 :         return NULL;
         451                 :            :     }
         452                 :            :     assert(self->rlock_count == 0);
         453                 :          0 :     self->rlock_owner = owner;
         454                 :          0 :     self->rlock_count = count;
         455                 :          0 :     Py_RETURN_NONE;
         456                 :            : }
         457                 :            :
         458                 :            : PyDoc_STRVAR(rlock_acquire_restore_doc,
         459                 :            : "_acquire_restore(state) -> None\n\
         460                 :            : \n\
         461                 :            : For internal use by `threading.Condition`.");
         462                 :            :
         463                 :            : static PyObject *
         464                 :          0 : rlock_release_save(rlockobject *self, PyObject *Py_UNUSED(ignored))
         465                 :            : {
         466                 :            :     unsigned long owner;
         467                 :            :     unsigned long count;
         468                 :            :
         469         [ #  # ]:          0 :     if (self->rlock_count == 0) {
         470                 :          0 :         PyErr_SetString(PyExc_RuntimeError,
         471                 :            :                         "cannot release un-acquired lock");
         472                 :          0 :         return NULL;
         473                 :            :     }
         474                 :            :
         475                 :          0 :     owner = self->rlock_owner;
         476                 :          0 :     count = self->rlock_count;
         477                 :          0 :     self->rlock_count = 0;
         478                 :          0 :     self->rlock_owner = 0;
         479                 :          0 :     PyThread_release_lock(self->rlock_lock);
         480                 :          0 :     return Py_BuildValue("kk", count, owner);
         481                 :            : }
         482                 :            :
         483                 :            : PyDoc_STRVAR(rlock_release_save_doc,
         484                 :            : "_release_save() -> tuple\n\
         485                 :            : \n\
         486                 :            : For internal use by `threading.Condition`.");
         487                 :            :
         488                 :            :
         489                 :            : static PyObject *
         490                 :          0 : rlock_is_owned(rlockobject *self, PyObject *Py_UNUSED(ignored))
         491                 :            : {
         492                 :          0 :     unsigned long tid = PyThread_get_thread_ident();
         493                 :            :
         494   [ #  #  #  # ]:          0 :     if (self->rlock_count > 0 && self->rlock_owner == tid) {
         495                 :          0 :         Py_RETURN_TRUE;
         496                 :            :     }
         497                 :          0 :     Py_RETURN_FALSE;
         498                 :            : }
         499                 :            :
         500                 :            : PyDoc_STRVAR(rlock_is_owned_doc,
         501                 :            : "_is_owned() -> bool\n\
         502                 :            : \n\
         503                 :            : For internal use by `threading.Condition`.");
         504                 :            :
         505                 :            : static PyObject *
         506                 :       1057 : rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
         507                 :            : {
         508                 :       1057 :     rlockobject *self = (rlockobject *) type->tp_alloc(type, 0);
         509         [ -  + ]:       1057 :     if (self == NULL) {
         510                 :          0 :         return NULL;
         511                 :            :     }
         512                 :       1057 :     self->in_weakreflist = NULL;
         513                 :       1057 :     self->rlock_owner = 0;
         514                 :       1057 :     self->rlock_count = 0;
         515                 :            :
         516                 :       1057 :     self->rlock_lock = PyThread_allocate_lock();
         517         [ -  + ]:       1057 :     if (self->rlock_lock == NULL) {
         518                 :          0 :         Py_DECREF(self);
         519                 :          0 :         PyErr_SetString(ThreadError, "can't allocate lock");
         520                 :          0 :         return NULL;
         521                 :            :     }
         522                 :       1057 :     return (PyObject *) self;
         523                 :            : }
         524                 :            :
         525                 :            : static PyObject *
         526                 :          0 : rlock_repr(rlockobject *self)
         527                 :            : {
         528                 :          0 :     return PyUnicode_FromFormat("<%s %s object owner=%ld count=%lu at %p>",
         529         [ #  # ]:          0 :         self->rlock_count ? "locked" : "unlocked",
         530                 :          0 :         Py_TYPE(self)->tp_name, self->rlock_owner,
         531                 :            :         self->rlock_count, self);
         532                 :            : }
         533                 :            :
         534                 :            :
         535                 :            : #ifdef HAVE_FORK
         536                 :            : static PyObject *
         537                 :          0 : rlock__at_fork_reinit(rlockobject *self, PyObject *Py_UNUSED(args))
         538                 :            : {
         539         [ #  # ]:          0 :     if (_PyThread_at_fork_reinit(&self->rlock_lock) < 0) {
         540                 :          0 :         PyErr_SetString(ThreadError, "failed to reinitialize lock at fork");
         541                 :          0 :         return NULL;
         542                 :            :     }
         543                 :            :
         544                 :          0 :     self->rlock_owner = 0;
         545                 :          0 :     self->rlock_count = 0;
         546                 :            :
         547                 :          0 :     Py_RETURN_NONE;
         548                 :            : }
         549                 :            : #endif  /* HAVE_FORK */
         550                 :            :
         551                 :            :
         552                 :            : static PyMethodDef rlock_methods[] = {
         553                 :            :     {"acquire",      _PyCFunction_CAST(rlock_acquire),
         554                 :            :      METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc},
         555                 :            :     {"release",      (PyCFunction)rlock_release,
         556                 :            :      METH_NOARGS, rlock_release_doc},
         557                 :            :     {"_is_owned",     (PyCFunction)rlock_is_owned,
         558                 :            :      METH_NOARGS, rlock_is_owned_doc},
         559                 :            :     {"_acquire_restore", (PyCFunction)rlock_acquire_restore,
         560                 :            :      METH_VARARGS, rlock_acquire_restore_doc},
         561                 :            :     {"_release_save", (PyCFunction)rlock_release_save,
         562                 :            :      METH_NOARGS, rlock_release_save_doc},
         563                 :            :     {"__enter__",    _PyCFunction_CAST(rlock_acquire),
         564                 :            :      METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc},
         565                 :            :     {"__exit__",    (PyCFunction)rlock_release,
         566                 :            :      METH_VARARGS, rlock_release_doc},
         567                 :            : #ifdef HAVE_FORK
         568                 :            :     {"_at_fork_reinit",    (PyCFunction)rlock__at_fork_reinit,
         569                 :            :      METH_NOARGS, NULL},
         570                 :            : #endif
         571                 :            :     {NULL,           NULL}              /* sentinel */
         572                 :            : };
         573                 :            :
         574                 :            :
         575                 :            : static PyMemberDef rlock_type_members[] = {
         576                 :            :     {"__weaklistoffset__", T_PYSSIZET, offsetof(rlockobject, in_weakreflist), READONLY},
         577                 :            :     {NULL},
         578                 :            : };
         579                 :            :
         580                 :            : static PyType_Slot rlock_type_slots[] = {
         581                 :            :     {Py_tp_dealloc, (destructor)rlock_dealloc},
         582                 :            :     {Py_tp_repr, (reprfunc)rlock_repr},
         583                 :            :     {Py_tp_methods, rlock_methods},
         584                 :            :     {Py_tp_alloc, PyType_GenericAlloc},
         585                 :            :     {Py_tp_new, rlock_new},
         586                 :            :     {Py_tp_members, rlock_type_members},
         587                 :            :     {Py_tp_traverse, rlock_traverse},
         588                 :            :     {0, 0},
         589                 :            : };
         590                 :            :
         591                 :            : static PyType_Spec rlock_type_spec = {
         592                 :            :     .name = "_thread.RLock",
         593                 :            :     .basicsize = sizeof(rlockobject),
         594                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
         595                 :            :               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
         596                 :            :     .slots = rlock_type_slots,
         597                 :            : };
         598                 :            :
         599                 :            : static lockobject *
         600                 :       1061 : newlockobject(PyObject *module)
         601                 :            : {
         602                 :       1061 :     thread_module_state *state = get_thread_state(module);
         603                 :            :
         604                 :       1061 :     PyTypeObject *type = state->lock_type;
         605                 :       1061 :     lockobject *self = (lockobject *)type->tp_alloc(type, 0);
         606         [ -  + ]:       1061 :     if (self == NULL) {
         607                 :          0 :         return NULL;
         608                 :            :     }
         609                 :            :
         610                 :       1061 :     self->lock_lock = PyThread_allocate_lock();
         611                 :       1061 :     self->locked = 0;
         612                 :       1061 :     self->in_weakreflist = NULL;
         613                 :            :
         614         [ -  + ]:       1061 :     if (self->lock_lock == NULL) {
         615                 :          0 :         Py_DECREF(self);
         616                 :          0 :         PyErr_SetString(ThreadError, "can't allocate lock");
         617                 :          0 :         return NULL;
         618                 :            :     }
         619                 :       1061 :     return self;
         620                 :            : }
         621                 :            :
         622                 :            : /* Thread-local objects */
         623                 :            :
         624                 :            : /* Quick overview:
         625                 :            :
         626                 :            :    We need to be able to reclaim reference cycles as soon as possible
         627                 :            :    (both when a thread is being terminated, or a thread-local object
         628                 :            :     becomes unreachable from user data).  Constraints:
         629                 :            :    - it must not be possible for thread-state dicts to be involved in
         630                 :            :      reference cycles (otherwise the cyclic GC will refuse to consider
         631                 :            :      objects referenced from a reachable thread-state dict, even though
         632                 :            :      local_dealloc would clear them)
         633                 :            :    - the death of a thread-state dict must still imply destruction of the
         634                 :            :      corresponding local dicts in all thread-local objects.
         635                 :            :
         636                 :            :    Our implementation uses small "localdummy" objects in order to break
         637                 :            :    the reference chain. These trivial objects are hashable (using the
         638                 :            :    default scheme of identity hashing) and weakrefable.
         639                 :            :    Each thread-state holds a separate localdummy for each local object
         640                 :            :    (as a /strong reference/),
         641                 :            :    and each thread-local object holds a dict mapping /weak references/
         642                 :            :    of localdummies to local dicts.
         643                 :            :
         644                 :            :    Therefore:
         645                 :            :    - only the thread-state dict holds a strong reference to the dummies
         646                 :            :    - only the thread-local object holds a strong reference to the local dicts
         647                 :            :    - only outside objects (application- or library-level) hold strong
         648                 :            :      references to the thread-local objects
         649                 :            :    - as soon as a thread-state dict is destroyed, the weakref callbacks of all
         650                 :            :      dummies attached to that thread are called, and destroy the corresponding
         651                 :            :      local dicts from thread-local objects
         652                 :            :    - as soon as a thread-local object is destroyed, its local dicts are
         653                 :            :      destroyed and its dummies are manually removed from all thread states
         654                 :            :    - the GC can do its work correctly when a thread-local object is dangling,
         655                 :            :      without any interference from the thread-state dicts
         656                 :            :
         657                 :            :    As an additional optimization, each localdummy holds a borrowed reference
         658                 :            :    to the corresponding localdict.  This borrowed reference is only used
         659                 :            :    by the thread-local object which has created the localdummy, which should
         660                 :            :    guarantee that the localdict still exists when accessed.
         661                 :            : */
         662                 :            :
         663                 :            : typedef struct {
         664                 :            :     PyObject_HEAD
         665                 :            :     PyObject *localdict;        /* Borrowed reference! */
         666                 :            :     PyObject *weakreflist;      /* List of weak references to self */
         667                 :            : } localdummyobject;
         668                 :            :
         669                 :            : static void
         670                 :          1 : localdummy_dealloc(localdummyobject *self)
         671                 :            : {
         672         [ -  + ]:          1 :     if (self->weakreflist != NULL)
         673                 :          0 :         PyObject_ClearWeakRefs((PyObject *) self);
         674                 :          1 :     PyTypeObject *tp = Py_TYPE(self);
         675                 :          1 :     tp->tp_free((PyObject*)self);
         676                 :          1 :     Py_DECREF(tp);
         677                 :          1 : }
         678                 :            :
         679                 :            : static PyMemberDef local_dummy_type_members[] = {
         680                 :            :     {"__weaklistoffset__", T_PYSSIZET, offsetof(localdummyobject, weakreflist), READONLY},
         681                 :            :     {NULL},
         682                 :            : };
         683                 :            :
         684                 :            : static PyType_Slot local_dummy_type_slots[] = {
         685                 :            :     {Py_tp_dealloc, (destructor)localdummy_dealloc},
         686                 :            :     {Py_tp_doc, "Thread-local dummy"},
         687                 :            :     {Py_tp_members, local_dummy_type_members},
         688                 :            :     {0, 0}
         689                 :            : };
         690                 :            :
         691                 :            : static PyType_Spec local_dummy_type_spec = {
         692                 :            :     .name = "_thread._localdummy",
         693                 :            :     .basicsize = sizeof(localdummyobject),
         694                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
         695                 :            :               Py_TPFLAGS_IMMUTABLETYPE),
         696                 :            :     .slots = local_dummy_type_slots,
         697                 :            : };
         698                 :            :
         699                 :            :
         700                 :            : typedef struct {
         701                 :            :     PyObject_HEAD
         702                 :            :     PyObject *key;
         703                 :            :     PyObject *args;
         704                 :            :     PyObject *kw;
         705                 :            :     PyObject *weakreflist;      /* List of weak references to self */
         706                 :            :     /* A {localdummy weakref -> localdict} dict */
         707                 :            :     PyObject *dummies;
         708                 :            :     /* The callback for weakrefs to localdummies */
         709                 :            :     PyObject *wr_callback;
         710                 :            : } localobject;
         711                 :            :
         712                 :            : /* Forward declaration */
         713                 :            : static PyObject *_ldict(localobject *self, thread_module_state *state);
         714                 :            : static PyObject *_localdummy_destroyed(PyObject *meth_self, PyObject *dummyweakref);
         715                 :            :
         716                 :            : /* Create and register the dummy for the current thread.
         717                 :            :    Returns a borrowed reference of the corresponding local dict */
         718                 :            : static PyObject *
         719                 :          1 : _local_create_dummy(localobject *self, thread_module_state *state)
         720                 :            : {
         721                 :          1 :     PyObject *ldict = NULL, *wr = NULL;
         722                 :          1 :     localdummyobject *dummy = NULL;
         723                 :          1 :     PyTypeObject *type = state->local_dummy_type;
         724                 :            :
         725                 :          1 :     PyObject *tdict = PyThreadState_GetDict();
         726         [ -  + ]:          1 :     if (tdict == NULL) {
         727                 :          0 :         PyErr_SetString(PyExc_SystemError,
         728                 :            :                         "Couldn't get thread-state dictionary");
         729                 :          0 :         goto err;
         730                 :            :     }
         731                 :            :
         732                 :          1 :     ldict = PyDict_New();
         733         [ -  + ]:          1 :     if (ldict == NULL) {
         734                 :          0 :         goto err;
         735                 :            :     }
         736                 :          1 :     dummy = (localdummyobject *) type->tp_alloc(type, 0);
         737         [ -  + ]:          1 :     if (dummy == NULL) {
         738                 :          0 :         goto err;
         739                 :            :     }
         740                 :          1 :     dummy->localdict = ldict;
         741                 :          1 :     wr = PyWeakref_NewRef((PyObject *) dummy, self->wr_callback);
         742         [ -  + ]:          1 :     if (wr == NULL) {
         743                 :          0 :         goto err;
         744                 :            :     }
         745                 :            :
         746                 :            :     /* As a side-effect, this will cache the weakref's hash before the
         747                 :            :        dummy gets deleted */
         748                 :          1 :     int r = PyDict_SetItem(self->dummies, wr, ldict);
         749         [ -  + ]:          1 :     if (r < 0) {
         750                 :          0 :         goto err;
         751                 :            :     }
         752         [ +  - ]:          1 :     Py_CLEAR(wr);
         753                 :          1 :     r = PyDict_SetItem(tdict, self->key, (PyObject *) dummy);
         754         [ -  + ]:          1 :     if (r < 0) {
         755                 :          0 :         goto err;
         756                 :            :     }
         757         [ +  - ]:          1 :     Py_CLEAR(dummy);
         758                 :            :
         759                 :          1 :     Py_DECREF(ldict);
         760                 :          1 :     return ldict;
         761                 :            :
         762                 :          0 : err:
         763                 :          0 :     Py_XDECREF(ldict);
         764                 :          0 :     Py_XDECREF(wr);
         765                 :          0 :     Py_XDECREF(dummy);
         766                 :          0 :     return NULL;
         767                 :            : }
         768                 :            :
         769                 :            : static PyObject *
         770                 :          1 : local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
         771                 :            : {
         772                 :            :     static PyMethodDef wr_callback_def = {
         773                 :            :         "_localdummy_destroyed", (PyCFunction) _localdummy_destroyed, METH_O
         774                 :            :     };
         775                 :            :
         776         [ +  - ]:          1 :     if (type->tp_init == PyBaseObject_Type.tp_init) {
         777                 :          1 :         int rc = 0;
         778         [ +  - ]:          1 :         if (args != NULL)
         779                 :          1 :             rc = PyObject_IsTrue(args);
         780   [ +  -  -  + ]:          1 :         if (rc == 0 && kw != NULL)
         781                 :          0 :             rc = PyObject_IsTrue(kw);
         782         [ -  + ]:          1 :         if (rc != 0) {
         783         [ #  # ]:          0 :             if (rc > 0) {
         784                 :          0 :                 PyErr_SetString(PyExc_TypeError,
         785                 :            :                           "Initialization arguments are not supported");
         786                 :            :             }
         787                 :          0 :             return NULL;
         788                 :            :         }
         789                 :            :     }
         790                 :            :
         791                 :          1 :     PyObject *module = PyType_GetModuleByDef(type, &thread_module);
         792                 :          1 :     thread_module_state *state = get_thread_state(module);
         793                 :            :
         794                 :          1 :     localobject *self = (localobject *)type->tp_alloc(type, 0);
         795         [ -  + ]:          1 :     if (self == NULL) {
         796                 :          0 :         return NULL;
         797                 :            :     }
         798                 :            :
         799                 :          1 :     self->args = Py_XNewRef(args);
         800                 :          1 :     self->kw = Py_XNewRef(kw);
         801                 :          1 :     self->key = PyUnicode_FromFormat("thread.local.%p", self);
         802         [ -  + ]:          1 :     if (self->key == NULL) {
         803                 :          0 :         goto err;
         804                 :            :     }
         805                 :            :
         806                 :          1 :     self->dummies = PyDict_New();
         807         [ -  + ]:          1 :     if (self->dummies == NULL) {
         808                 :          0 :         goto err;
         809                 :            :     }
         810                 :            :
         811                 :            :     /* We use a weak reference to self in the callback closure
         812                 :            :        in order to avoid spurious reference cycles */
         813                 :          1 :     PyObject *wr = PyWeakref_NewRef((PyObject *) self, NULL);
         814         [ -  + ]:          1 :     if (wr == NULL) {
         815                 :          0 :         goto err;
         816                 :            :     }
         817                 :          1 :     self->wr_callback = PyCFunction_NewEx(&wr_callback_def, wr, NULL);
         818                 :          1 :     Py_DECREF(wr);
         819         [ -  + ]:          1 :     if (self->wr_callback == NULL) {
         820                 :          0 :         goto err;
         821                 :            :     }
         822         [ -  + ]:          1 :     if (_local_create_dummy(self, state) == NULL) {
         823                 :          0 :         goto err;
         824                 :            :     }
         825                 :          1 :     return (PyObject *)self;
         826                 :            :
         827                 :          0 :   err:
         828                 :          0 :     Py_DECREF(self);
         829                 :          0 :     return NULL;
         830                 :            : }
         831                 :            :
         832                 :            : static int
         833                 :          8 : local_traverse(localobject *self, visitproc visit, void *arg)
         834                 :            : {
         835   [ +  -  -  + ]:          8 :     Py_VISIT(Py_TYPE(self));
         836   [ +  -  -  + ]:          8 :     Py_VISIT(self->args);
         837   [ -  +  -  - ]:          8 :     Py_VISIT(self->kw);
         838   [ +  -  -  + ]:          8 :     Py_VISIT(self->dummies);
         839                 :          8 :     return 0;
         840                 :            : }
         841                 :            :
         842                 :            : static int
         843                 :          1 : local_clear(localobject *self)
         844                 :            : {
         845         [ +  - ]:          1 :     Py_CLEAR(self->args);
         846         [ -  + ]:          1 :     Py_CLEAR(self->kw);
         847         [ +  - ]:          1 :     Py_CLEAR(self->dummies);
         848         [ +  - ]:          1 :     Py_CLEAR(self->wr_callback);
         849                 :            :     /* Remove all strong references to dummies from the thread states */
         850         [ +  - ]:          1 :     if (self->key) {
         851                 :          1 :         PyInterpreterState *interp = _PyInterpreterState_GET();
         852                 :          1 :         _PyRuntimeState *runtime = &_PyRuntime;
         853                 :          1 :         HEAD_LOCK(runtime);
         854                 :          1 :         PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
         855                 :          1 :         HEAD_UNLOCK(runtime);
         856         [ +  + ]:          2 :         while (tstate) {
         857         [ +  - ]:          1 :             if (tstate->dict) {
         858                 :          1 :                 PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None);
         859         [ +  - ]:          1 :                 if (v != NULL) {
         860                 :          1 :                     Py_DECREF(v);
         861                 :            :                 }
         862                 :            :                 else {
         863                 :          0 :                     PyErr_Clear();
         864                 :            :                 }
         865                 :            :             }
         866                 :          1 :             HEAD_LOCK(runtime);
         867                 :          1 :             tstate = PyThreadState_Next(tstate);
         868                 :          1 :             HEAD_UNLOCK(runtime);
         869                 :            :         }
         870                 :            :     }
         871                 :          1 :     return 0;
         872                 :            : }
         873                 :            :
         874                 :            : static void
         875                 :          1 : local_dealloc(localobject *self)
         876                 :            : {
         877                 :            :     /* Weakrefs must be invalidated right now, otherwise they can be used
         878                 :            :        from code called below, which is very dangerous since Py_REFCNT(self) == 0 */
         879         [ +  - ]:          1 :     if (self->weakreflist != NULL) {
         880                 :          1 :         PyObject_ClearWeakRefs((PyObject *) self);
         881                 :            :     }
         882                 :            :
         883                 :          1 :     PyObject_GC_UnTrack(self);
         884                 :            :
         885                 :          1 :     local_clear(self);
         886                 :          1 :     Py_XDECREF(self->key);
         887                 :            :
         888                 :          1 :     PyTypeObject *tp = Py_TYPE(self);
         889                 :          1 :     tp->tp_free((PyObject*)self);
         890                 :          1 :     Py_DECREF(tp);
         891                 :          1 : }
         892                 :            :
         893                 :            : /* Returns a borrowed reference to the local dict, creating it if necessary */
         894                 :            : static PyObject *
         895                 :          0 : _ldict(localobject *self, thread_module_state *state)
         896                 :            : {
         897                 :          0 :     PyObject *tdict = PyThreadState_GetDict();
         898         [ #  # ]:          0 :     if (tdict == NULL) {
         899                 :          0 :         PyErr_SetString(PyExc_SystemError,
         900                 :            :                         "Couldn't get thread-state dictionary");
         901                 :          0 :         return NULL;
         902                 :            :     }
         903                 :            :
         904                 :            :     PyObject *ldict;
         905                 :          0 :     PyObject *dummy = PyDict_GetItemWithError(tdict, self->key);
         906         [ #  # ]:          0 :     if (dummy == NULL) {
         907         [ #  # ]:          0 :         if (PyErr_Occurred()) {
         908                 :          0 :             return NULL;
         909                 :            :         }
         910                 :          0 :         ldict = _local_create_dummy(self, state);
         911         [ #  # ]:          0 :         if (ldict == NULL)
         912                 :          0 :             return NULL;
         913                 :            :
         914   [ #  #  #  # ]:          0 :         if (Py_TYPE(self)->tp_init != PyBaseObject_Type.tp_init &&
         915                 :          0 :             Py_TYPE(self)->tp_init((PyObject*)self,
         916                 :            :                                    self->args, self->kw) < 0) {
         917                 :            :             /* we need to get rid of ldict from thread so
         918                 :            :                we create a new one the next time we do an attr
         919                 :            :                access */
         920                 :          0 :             PyDict_DelItem(tdict, self->key);
         921                 :          0 :             return NULL;
         922                 :            :         }
         923                 :            :     }
         924                 :            :     else {
         925                 :            :         assert(Py_IS_TYPE(dummy, state->local_dummy_type));
         926                 :          0 :         ldict = ((localdummyobject *) dummy)->localdict;
         927                 :            :     }
         928                 :            :
         929                 :          0 :     return ldict;
         930                 :            : }
         931                 :            :
         932                 :            : static int
         933                 :          0 : local_setattro(localobject *self, PyObject *name, PyObject *v)
         934                 :            : {
         935                 :          0 :     PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
         936                 :          0 :     thread_module_state *state = get_thread_state(module);
         937                 :            :
         938                 :          0 :     PyObject *ldict = _ldict(self, state);
         939         [ #  # ]:          0 :     if (ldict == NULL) {
         940                 :          0 :         return -1;
         941                 :            :     }
         942                 :            :
         943                 :          0 :     int r = PyObject_RichCompareBool(name, &_Py_ID(__dict__), Py_EQ);
         944         [ #  # ]:          0 :     if (r == -1) {
         945                 :          0 :         return -1;
         946                 :            :     }
         947         [ #  # ]:          0 :     if (r == 1) {
         948                 :          0 :         PyErr_Format(PyExc_AttributeError,
         949                 :            :                      "'%.50s' object attribute '%U' is read-only",
         950                 :          0 :                      Py_TYPE(self)->tp_name, name);
         951                 :          0 :         return -1;
         952                 :            :     }
         953                 :            :
         954                 :          0 :     return _PyObject_GenericSetAttrWithDict((PyObject *)self, name, v, ldict);
         955                 :            : }
         956                 :            :
         957                 :            : static PyObject *local_getattro(localobject *, PyObject *);
         958                 :            :
         959                 :            : static PyMemberDef local_type_members[] = {
         960                 :            :     {"__weaklistoffset__", T_PYSSIZET, offsetof(localobject, weakreflist), READONLY},
         961                 :            :     {NULL},
         962                 :            : };
         963                 :            :
         964                 :            : static PyType_Slot local_type_slots[] = {
         965                 :            :     {Py_tp_dealloc, (destructor)local_dealloc},
         966                 :            :     {Py_tp_getattro, (getattrofunc)local_getattro},
         967                 :            :     {Py_tp_setattro, (setattrofunc)local_setattro},
         968                 :            :     {Py_tp_doc, "Thread-local data"},
         969                 :            :     {Py_tp_traverse, (traverseproc)local_traverse},
         970                 :            :     {Py_tp_clear, (inquiry)local_clear},
         971                 :            :     {Py_tp_new, local_new},
         972                 :            :     {Py_tp_members, local_type_members},
         973                 :            :     {0, 0}
         974                 :            : };
         975                 :            :
         976                 :            : static PyType_Spec local_type_spec = {
         977                 :            :     .name = "_thread._local",
         978                 :            :     .basicsize = sizeof(localobject),
         979                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
         980                 :            :               Py_TPFLAGS_IMMUTABLETYPE),
         981                 :            :     .slots = local_type_slots,
         982                 :            : };
         983                 :            :
         984                 :            : static PyObject *
         985                 :          0 : local_getattro(localobject *self, PyObject *name)
         986                 :            : {
         987                 :          0 :     PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
         988                 :          0 :     thread_module_state *state = get_thread_state(module);
         989                 :            :
         990                 :          0 :     PyObject *ldict = _ldict(self, state);
         991         [ #  # ]:          0 :     if (ldict == NULL)
         992                 :          0 :         return NULL;
         993                 :            :
         994                 :          0 :     int r = PyObject_RichCompareBool(name, &_Py_ID(__dict__), Py_EQ);
         995         [ #  # ]:          0 :     if (r == 1) {
         996                 :          0 :         return Py_NewRef(ldict);
         997                 :            :     }
         998         [ #  # ]:          0 :     if (r == -1) {
         999                 :          0 :         return NULL;
        1000                 :            :     }
        1001                 :            :
        1002         [ #  # ]:          0 :     if (!Py_IS_TYPE(self, state->local_type)) {
        1003                 :            :         /* use generic lookup for subtypes */
        1004                 :          0 :         return _PyObject_GenericGetAttrWithDict((PyObject *)self, name,
        1005                 :            :                                                 ldict, 0);
        1006                 :            :     }
        1007                 :            :
        1008                 :            :     /* Optimization: just look in dict ourselves */
        1009                 :          0 :     PyObject *value = PyDict_GetItemWithError(ldict, name);
        1010         [ #  # ]:          0 :     if (value != NULL) {
        1011                 :          0 :         return Py_NewRef(value);
        1012                 :            :     }
        1013         [ #  # ]:          0 :     if (PyErr_Occurred()) {
        1014                 :          0 :         return NULL;
        1015                 :            :     }
        1016                 :            :
        1017                 :            :     /* Fall back on generic to get __class__ and __dict__ */
        1018                 :          0 :     return _PyObject_GenericGetAttrWithDict(
        1019                 :            :         (PyObject *)self, name, ldict, 0);
        1020                 :            : }
        1021                 :            :
        1022                 :            : /* Called when a dummy is destroyed. */
        1023                 :            : static PyObject *
        1024                 :          0 : _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref)
        1025                 :            : {
        1026                 :            :     assert(PyWeakref_CheckRef(localweakref));
        1027                 :          0 :     PyObject *obj = PyWeakref_GET_OBJECT(localweakref);
        1028         [ #  # ]:          0 :     if (obj == Py_None) {
        1029                 :          0 :         Py_RETURN_NONE;
        1030                 :            :     }
        1031                 :            :
        1032                 :            :     /* If the thread-local object is still alive and not being cleared,
        1033                 :            :        remove the corresponding local dict */
        1034                 :          0 :     localobject *self = (localobject *)Py_NewRef(obj);
        1035         [ #  # ]:          0 :     if (self->dummies != NULL) {
        1036                 :            :         PyObject *ldict;
        1037                 :          0 :         ldict = PyDict_GetItemWithError(self->dummies, dummyweakref);
        1038         [ #  # ]:          0 :         if (ldict != NULL) {
        1039                 :          0 :             PyDict_DelItem(self->dummies, dummyweakref);
        1040                 :            :         }
        1041         [ #  # ]:          0 :         if (PyErr_Occurred())
        1042                 :          0 :             PyErr_WriteUnraisable(obj);
        1043                 :            :     }
        1044                 :          0 :     Py_DECREF(obj);
        1045                 :          0 :     Py_RETURN_NONE;
        1046                 :            : }
        1047                 :            :
        1048                 :            : /* Module functions */
        1049                 :            :
        1050                 :            : struct bootstate {
        1051                 :            :     PyInterpreterState *interp;
        1052                 :            :     PyObject *func;
        1053                 :            :     PyObject *args;
        1054                 :            :     PyObject *kwargs;
        1055                 :            :     PyThreadState *tstate;
        1056                 :            :     _PyRuntimeState *runtime;
        1057                 :            : };
        1058                 :            :
        1059                 :            :
        1060                 :            : static void
        1061                 :          0 : thread_bootstate_free(struct bootstate *boot)
        1062                 :            : {
        1063                 :          0 :     Py_DECREF(boot->func);
        1064                 :          0 :     Py_DECREF(boot->args);
        1065                 :          0 :     Py_XDECREF(boot->kwargs);
        1066                 :          0 :     PyMem_Free(boot);
        1067                 :          0 : }
        1068                 :            :
        1069                 :            :
        1070                 :            : static void
        1071                 :          0 : thread_run(void *boot_raw)
        1072                 :            : {
        1073                 :          0 :     struct bootstate *boot = (struct bootstate *) boot_raw;
        1074                 :            :     PyThreadState *tstate;
        1075                 :            :
        1076                 :          0 :     tstate = boot->tstate;
        1077                 :          0 :     _PyThreadState_Bind(tstate);
        1078                 :          0 :     PyEval_AcquireThread(tstate);
        1079                 :          0 :     tstate->interp->threads.count++;
        1080                 :            :
        1081                 :          0 :     PyObject *res = PyObject_Call(boot->func, boot->args, boot->kwargs);
        1082         [ #  # ]:          0 :     if (res == NULL) {
        1083         [ #  # ]:          0 :         if (PyErr_ExceptionMatches(PyExc_SystemExit))
        1084                 :            :             /* SystemExit is ignored silently */
        1085                 :          0 :             PyErr_Clear();
        1086                 :            :         else {
        1087                 :          0 :             _PyErr_WriteUnraisableMsg("in thread started by", boot->func);
        1088                 :            :         }
        1089                 :            :     }
        1090                 :            :     else {
        1091                 :          0 :         Py_DECREF(res);
        1092                 :            :     }
        1093                 :            :
        1094                 :          0 :     thread_bootstate_free(boot);
        1095                 :          0 :     tstate->interp->threads.count--;
        1096                 :          0 :     PyThreadState_Clear(tstate);
        1097                 :          0 :     _PyThreadState_DeleteCurrent(tstate);
        1098                 :            :
        1099                 :            :     // bpo-44434: Don't call explicitly PyThread_exit_thread(). On Linux with
        1100                 :            :     // the glibc, pthread_exit() can abort the whole process if dlopen() fails
        1101                 :            :     // to open the libgcc_s.so library (ex: EMFILE error).
        1102                 :          0 : }
        1103                 :            :
        1104                 :            : static PyObject *
        1105                 :          0 : thread_daemon_threads_allowed(PyObject *module, PyObject *Py_UNUSED(ignored))
        1106                 :            : {
        1107                 :          0 :     PyInterpreterState *interp = _PyInterpreterState_Get();
        1108         [ #  # ]:          0 :     if (interp->feature_flags & Py_RTFLAGS_DAEMON_THREADS) {
        1109                 :          0 :         Py_RETURN_TRUE;
        1110                 :            :     }
        1111                 :            :     else {
        1112                 :          0 :         Py_RETURN_FALSE;
        1113                 :            :     }
        1114                 :            : }
        1115                 :            :
        1116                 :            : PyDoc_STRVAR(daemon_threads_allowed_doc,
        1117                 :            : "daemon_threads_allowed()\n\
        1118                 :            : \n\
        1119                 :            : Return True if daemon threads are allowed in the current interpreter,\n\
        1120                 :            : and False otherwise.\n");
        1121                 :            :
        1122                 :            : static PyObject *
        1123                 :          0 : thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
        1124                 :            : {
        1125                 :          0 :     _PyRuntimeState *runtime = &_PyRuntime;
        1126                 :          0 :     PyObject *func, *args, *kwargs = NULL;
        1127                 :            :
        1128         [ #  # ]:          0 :     if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3,
        1129                 :            :                            &func, &args, &kwargs))
        1130                 :          0 :         return NULL;
        1131         [ #  # ]:          0 :     if (!PyCallable_Check(func)) {
        1132                 :          0 :         PyErr_SetString(PyExc_TypeError,
        1133                 :            :                         "first arg must be callable");
        1134                 :          0 :         return NULL;
        1135                 :            :     }
        1136         [ #  # ]:          0 :     if (!PyTuple_Check(args)) {
        1137                 :          0 :         PyErr_SetString(PyExc_TypeError,
        1138                 :            :                         "2nd arg must be a tuple");
        1139                 :          0 :         return NULL;
        1140                 :            :     }
        1141   [ #  #  #  # ]:          0 :     if (kwargs != NULL && !PyDict_Check(kwargs)) {
        1142                 :          0 :         PyErr_SetString(PyExc_TypeError,
        1143                 :            :                         "optional 3rd arg must be a dictionary");
        1144                 :          0 :         return NULL;
        1145                 :            :     }
        1146                 :            :
        1147         [ #  # ]:          0 :     if (PySys_Audit("_thread.start_new_thread", "OOO",
        1148         [ #  # ]:          0 :                     func, args, kwargs ? kwargs : Py_None) < 0) {
        1149                 :          0 :         return NULL;
        1150                 :            :     }
        1151                 :            :
        1152                 :          0 :     PyInterpreterState *interp = _PyInterpreterState_GET();
        1153         [ #  # ]:          0 :     if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_THREADS)) {
        1154                 :          0 :         PyErr_SetString(PyExc_RuntimeError,
        1155                 :            :                         "thread is not supported for isolated subinterpreters");
        1156                 :          0 :         return NULL;
        1157                 :            :     }
        1158                 :            :
        1159                 :          0 :     struct bootstate *boot = PyMem_NEW(struct bootstate, 1);
        1160         [ #  # ]:          0 :     if (boot == NULL) {
        1161                 :          0 :         return PyErr_NoMemory();
        1162                 :            :     }
        1163                 :          0 :     boot->interp = _PyInterpreterState_GET();
        1164                 :          0 :     boot->tstate = _PyThreadState_New(boot->interp);
        1165         [ #  # ]:          0 :     if (boot->tstate == NULL) {
        1166                 :          0 :         PyMem_Free(boot);
        1167         [ #  # ]:          0 :         if (!PyErr_Occurred()) {
        1168                 :          0 :             return PyErr_NoMemory();
        1169                 :            :         }
        1170                 :          0 :         return NULL;
        1171                 :            :     }
        1172                 :          0 :     boot->runtime = runtime;
        1173                 :          0 :     boot->func = Py_NewRef(func);
        1174                 :          0 :     boot->args = Py_NewRef(args);
        1175                 :          0 :     boot->kwargs = Py_XNewRef(kwargs);
        1176                 :            :
        1177                 :          0 :     unsigned long ident = PyThread_start_new_thread(thread_run, (void*) boot);
        1178         [ #  # ]:          0 :     if (ident == PYTHREAD_INVALID_THREAD_ID) {
        1179                 :          0 :         PyErr_SetString(ThreadError, "can't start new thread");
        1180                 :          0 :         PyThreadState_Clear(boot->tstate);
        1181                 :          0 :         thread_bootstate_free(boot);
        1182                 :          0 :         return NULL;
        1183                 :            :     }
        1184                 :          0 :     return PyLong_FromUnsignedLong(ident);
        1185                 :            : }
        1186                 :            :
        1187                 :            : PyDoc_STRVAR(start_new_doc,
        1188                 :            : "start_new_thread(function, args[, kwargs])\n\
        1189                 :            : (start_new() is an obsolete synonym)\n\
        1190                 :            : \n\
        1191                 :            : Start a new thread and return its identifier.  The thread will call the\n\
        1192                 :            : function with positional arguments from the tuple args and keyword arguments\n\
        1193                 :            : taken from the optional dictionary kwargs.  The thread exits when the\n\
        1194                 :            : function returns; the return value is ignored.  The thread will also exit\n\
        1195                 :            : when the function raises an unhandled exception; a stack trace will be\n\
        1196                 :            : printed unless the exception is SystemExit.\n");
        1197                 :            :
        1198                 :            : static PyObject *
        1199                 :          0 : thread_PyThread_exit_thread(PyObject *self, PyObject *Py_UNUSED(ignored))
        1200                 :            : {
        1201                 :          0 :     PyErr_SetNone(PyExc_SystemExit);
        1202                 :          0 :     return NULL;
        1203                 :            : }
        1204                 :            :
        1205                 :            : PyDoc_STRVAR(exit_doc,
        1206                 :            : "exit()\n\
        1207                 :            : (exit_thread() is an obsolete synonym)\n\
        1208                 :            : \n\
        1209                 :            : This is synonymous to ``raise SystemExit''.  It will cause the current\n\
        1210                 :            : thread to exit silently unless the exception is caught.");
        1211                 :            :
        1212                 :            : static PyObject *
        1213                 :          0 : thread_PyThread_interrupt_main(PyObject *self, PyObject *args)
        1214                 :            : {
        1215                 :          0 :     int signum = SIGINT;
        1216         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "|i:signum", &signum)) {
        1217                 :          0 :         return NULL;
        1218                 :            :     }
        1219                 :            :
        1220         [ #  # ]:          0 :     if (PyErr_SetInterruptEx(signum)) {
        1221                 :          0 :         PyErr_SetString(PyExc_ValueError, "signal number out of range");
        1222                 :          0 :         return NULL;
        1223                 :            :     }
        1224                 :          0 :     Py_RETURN_NONE;
        1225                 :            : }
        1226                 :            :
        1227                 :            : PyDoc_STRVAR(interrupt_doc,
        1228                 :            : "interrupt_main(signum=signal.SIGINT, /)\n\
        1229                 :            : \n\
        1230                 :            : Simulate the arrival of the given signal in the main thread,\n\
        1231                 :            : where the corresponding signal handler will be executed.\n\
        1232                 :            : If *signum* is omitted, SIGINT is assumed.\n\
        1233                 :            : A subthread can use this function to interrupt the main thread.\n\
        1234                 :            : \n\
        1235                 :            : Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``."
        1236                 :            : );
        1237                 :            :
        1238                 :            : static lockobject *newlockobject(PyObject *module);
        1239                 :            :
        1240                 :            : static PyObject *
        1241                 :       1057 : thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
        1242                 :            : {
        1243                 :       1057 :     return (PyObject *) newlockobject(module);
        1244                 :            : }
        1245                 :            :
        1246                 :            : PyDoc_STRVAR(allocate_doc,
        1247                 :            : "allocate_lock() -> lock object\n\
        1248                 :            : (allocate() is an obsolete synonym)\n\
        1249                 :            : \n\
        1250                 :            : Create a new lock object. See help(type(threading.Lock())) for\n\
        1251                 :            : information about locks.");
        1252                 :            :
        1253                 :            : static PyObject *
        1254                 :       2572 : thread_get_ident(PyObject *self, PyObject *Py_UNUSED(ignored))
        1255                 :            : {
        1256                 :       2572 :     unsigned long ident = PyThread_get_thread_ident();
        1257         [ -  + ]:       2572 :     if (ident == PYTHREAD_INVALID_THREAD_ID) {
        1258                 :          0 :         PyErr_SetString(ThreadError, "no current thread ident");
        1259                 :          0 :         return NULL;
        1260                 :            :     }
        1261                 :       2572 :     return PyLong_FromUnsignedLong(ident);
        1262                 :            : }
        1263                 :            :
        1264                 :            : PyDoc_STRVAR(get_ident_doc,
        1265                 :            : "get_ident() -> integer\n\
        1266                 :            : \n\
        1267                 :            : Return a non-zero integer that uniquely identifies the current thread\n\
        1268                 :            : amongst other threads that exist simultaneously.\n\
        1269                 :            : This may be used to identify per-thread resources.\n\
        1270                 :            : Even though on some platforms threads identities may appear to be\n\
        1271                 :            : allocated consecutive numbers starting at 1, this behavior should not\n\
        1272                 :            : be relied upon, and the number should be seen purely as a magic cookie.\n\
        1273                 :            : A thread's identity may be reused for another thread after it exits.");
        1274                 :            :
        1275                 :            : #ifdef PY_HAVE_THREAD_NATIVE_ID
        1276                 :            : static PyObject *
        1277                 :          4 : thread_get_native_id(PyObject *self, PyObject *Py_UNUSED(ignored))
        1278                 :            : {
        1279                 :          4 :     unsigned long native_id = PyThread_get_thread_native_id();
        1280                 :          4 :     return PyLong_FromUnsignedLong(native_id);
        1281                 :            : }
        1282                 :            :
        1283                 :            : PyDoc_STRVAR(get_native_id_doc,
        1284                 :            : "get_native_id() -> integer\n\
        1285                 :            : \n\
        1286                 :            : Return a non-negative integer identifying the thread as reported\n\
        1287                 :            : by the OS (kernel). This may be used to uniquely identify a\n\
        1288                 :            : particular thread within a system.");
        1289                 :            : #endif
        1290                 :            :
        1291                 :            : static PyObject *
        1292                 :          0 : thread__count(PyObject *self, PyObject *Py_UNUSED(ignored))
        1293                 :            : {
        1294                 :          0 :     PyInterpreterState *interp = _PyInterpreterState_GET();
        1295                 :          0 :     return PyLong_FromLong(interp->threads.count);
        1296                 :            : }
        1297                 :            :
        1298                 :            : PyDoc_STRVAR(_count_doc,
        1299                 :            : "_count() -> integer\n\
        1300                 :            : \n\
        1301                 :            : \
        1302                 :            : Return the number of currently running Python threads, excluding\n\
        1303                 :            : the main thread. The returned number comprises all threads created\n\
        1304                 :            : through `start_new_thread()` as well as `threading.Thread`, and not\n\
        1305                 :            : yet finished.\n\
        1306                 :            : \n\
        1307                 :            : This function is meant for internal and specialized purposes only.\n\
        1308                 :            : In most applications `threading.enumerate()` should be used instead.");
        1309                 :            :
        1310                 :            : static void
        1311                 :          4 : release_sentinel(void *wr_raw)
        1312                 :            : {
        1313                 :          4 :     PyObject *wr = _PyObject_CAST(wr_raw);
        1314                 :            :     /* Tricky: this function is called when the current thread state
        1315                 :            :        is being deleted.  Therefore, only simple C code can safely
        1316                 :            :        execute here. */
        1317                 :          4 :     PyObject *obj = PyWeakref_GET_OBJECT(wr);
        1318                 :            :     lockobject *lock;
        1319         [ -  + ]:          4 :     if (obj != Py_None) {
        1320                 :          0 :         lock = (lockobject *) obj;
        1321         [ #  # ]:          0 :         if (lock->locked) {
        1322                 :          0 :             PyThread_release_lock(lock->lock_lock);
        1323                 :          0 :             lock->locked = 0;
        1324                 :            :         }
        1325                 :            :     }
        1326                 :            :     /* Deallocating a weakref with a NULL callback only calls
        1327                 :            :        PyObject_GC_Del(), which can't call any Python code. */
        1328                 :          4 :     Py_DECREF(wr);
        1329                 :          4 : }
        1330                 :            :
        1331                 :            : static PyObject *
        1332                 :          4 : thread__set_sentinel(PyObject *module, PyObject *Py_UNUSED(ignored))
        1333                 :            : {
        1334                 :            :     PyObject *wr;
        1335                 :          4 :     PyThreadState *tstate = _PyThreadState_GET();
        1336                 :            :     lockobject *lock;
        1337                 :            :
        1338         [ -  + ]:          4 :     if (tstate->on_delete_data != NULL) {
        1339                 :            :         /* We must support the re-creation of the lock from a
        1340                 :            :            fork()ed child. */
        1341                 :            :         assert(tstate->on_delete == &release_sentinel);
        1342                 :          0 :         wr = (PyObject *) tstate->on_delete_data;
        1343                 :          0 :         tstate->on_delete = NULL;
        1344                 :          0 :         tstate->on_delete_data = NULL;
        1345                 :          0 :         Py_DECREF(wr);
        1346                 :            :     }
        1347                 :          4 :     lock = newlockobject(module);
        1348         [ -  + ]:          4 :     if (lock == NULL)
        1349                 :          0 :         return NULL;
        1350                 :            :     /* The lock is owned by whoever called _set_sentinel(), but the weakref
        1351                 :            :        hangs to the thread state. */
        1352                 :          4 :     wr = PyWeakref_NewRef((PyObject *) lock, NULL);
        1353         [ -  + ]:          4 :     if (wr == NULL) {
        1354                 :          0 :         Py_DECREF(lock);
        1355                 :          0 :         return NULL;
        1356                 :            :     }
        1357                 :          4 :     tstate->on_delete_data = (void *) wr;
        1358                 :          4 :     tstate->on_delete = &release_sentinel;
        1359                 :          4 :     return (PyObject *) lock;
        1360                 :            : }
        1361                 :            :
        1362                 :            : PyDoc_STRVAR(_set_sentinel_doc,
        1363                 :            : "_set_sentinel() -> lock\n\
        1364                 :            : \n\
        1365                 :            : Set a sentinel lock that will be released when the current thread\n\
        1366                 :            : state is finalized (after it is untied from the interpreter).\n\
        1367                 :            : \n\
        1368                 :            : This is a private API for the threading module.");
        1369                 :            :
        1370                 :            : static PyObject *
        1371                 :          0 : thread_stack_size(PyObject *self, PyObject *args)
        1372                 :            : {
        1373                 :            :     size_t old_size;
        1374                 :          0 :     Py_ssize_t new_size = 0;
        1375                 :            :     int rc;
        1376                 :            :
        1377         [ #  # ]:          0 :     if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size))
        1378                 :          0 :         return NULL;
        1379                 :            :
        1380         [ #  # ]:          0 :     if (new_size < 0) {
        1381                 :          0 :         PyErr_SetString(PyExc_ValueError,
        1382                 :            :                         "size must be 0 or a positive value");
        1383                 :          0 :         return NULL;
        1384                 :            :     }
        1385                 :            :
        1386                 :          0 :     old_size = PyThread_get_stacksize();
        1387                 :            :
        1388                 :          0 :     rc = PyThread_set_stacksize((size_t) new_size);
        1389         [ #  # ]:          0 :     if (rc == -1) {
        1390                 :          0 :         PyErr_Format(PyExc_ValueError,
        1391                 :            :                      "size not valid: %zd bytes",
        1392                 :            :                      new_size);
        1393                 :          0 :         return NULL;
        1394                 :            :     }
        1395         [ #  # ]:          0 :     if (rc == -2) {
        1396                 :          0 :         PyErr_SetString(ThreadError,
        1397                 :            :                         "setting stack size not supported");
        1398                 :          0 :         return NULL;
        1399                 :            :     }
        1400                 :            :
        1401                 :          0 :     return PyLong_FromSsize_t((Py_ssize_t) old_size);
        1402                 :            : }
        1403                 :            :
        1404                 :            : PyDoc_STRVAR(stack_size_doc,
        1405                 :            : "stack_size([size]) -> size\n\
        1406                 :            : \n\
        1407                 :            : Return the thread stack size used when creating new threads.  The\n\
        1408                 :            : optional size argument specifies the stack size (in bytes) to be used\n\
        1409                 :            : for subsequently created threads, and must be 0 (use platform or\n\
        1410                 :            : configured default) or a positive integer value of at least 32,768 (32k).\n\
        1411                 :            : If changing the thread stack size is unsupported, a ThreadError\n\
        1412                 :            : exception is raised.  If the specified size is invalid, a ValueError\n\
        1413                 :            : exception is raised, and the stack size is unmodified.  32k bytes\n\
        1414                 :            :  currently the minimum supported stack size value to guarantee\n\
        1415                 :            : sufficient stack space for the interpreter itself.\n\
        1416                 :            : \n\
        1417                 :            : Note that some platforms may have particular restrictions on values for\n\
        1418                 :            : the stack size, such as requiring a minimum stack size larger than 32 KiB or\n\
        1419                 :            : requiring allocation in multiples of the system memory page size\n\
        1420                 :            : - platform documentation should be referred to for more information\n\
        1421                 :            : (4 KiB pages are common; using multiples of 4096 for the stack size is\n\
        1422                 :            : the suggested approach in the absence of more specific information).");
        1423                 :            :
        1424                 :            : static int
        1425                 :          0 : thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value,
        1426                 :            :                        PyObject *exc_traceback, PyObject *thread)
        1427                 :            : {
        1428                 :            :     /* print(f"Exception in thread {thread.name}:", file=file) */
        1429         [ #  # ]:          0 :     if (PyFile_WriteString("Exception in thread ", file) < 0) {
        1430                 :          0 :         return -1;
        1431                 :            :     }
        1432                 :            :
        1433                 :          0 :     PyObject *name = NULL;
        1434         [ #  # ]:          0 :     if (thread != Py_None) {
        1435         [ #  # ]:          0 :         if (_PyObject_LookupAttr(thread, &_Py_ID(name), &name) < 0) {
        1436                 :          0 :             return -1;
        1437                 :            :         }
        1438                 :            :     }
        1439         [ #  # ]:          0 :     if (name != NULL) {
        1440         [ #  # ]:          0 :         if (PyFile_WriteObject(name, file, Py_PRINT_RAW) < 0) {
        1441                 :          0 :             Py_DECREF(name);
        1442                 :          0 :             return -1;
        1443                 :            :         }
        1444                 :          0 :         Py_DECREF(name);
        1445                 :            :     }
        1446                 :            :     else {
        1447                 :          0 :         unsigned long ident = PyThread_get_thread_ident();
        1448                 :          0 :         PyObject *str = PyUnicode_FromFormat("%lu", ident);
        1449         [ #  # ]:          0 :         if (str != NULL) {
        1450         [ #  # ]:          0 :             if (PyFile_WriteObject(str, file, Py_PRINT_RAW) < 0) {
        1451                 :          0 :                 Py_DECREF(str);
        1452                 :          0 :                 return -1;
        1453                 :            :             }
        1454                 :          0 :             Py_DECREF(str);
        1455                 :            :         }
        1456                 :            :         else {
        1457                 :          0 :             PyErr_Clear();
        1458                 :            :
        1459         [ #  # ]:          0 :             if (PyFile_WriteString("<failed to get thread name>", file) < 0) {
        1460                 :          0 :                 return -1;
        1461                 :            :             }
        1462                 :            :         }
        1463                 :            :     }
        1464                 :            :
        1465         [ #  # ]:          0 :     if (PyFile_WriteString(":\n", file) < 0) {
        1466                 :          0 :         return -1;
        1467                 :            :     }
        1468                 :            :
        1469                 :            :     /* Display the traceback */
        1470                 :          0 :     _PyErr_Display(file, exc_type, exc_value, exc_traceback);
        1471                 :            :
        1472                 :            :     /* Call file.flush() */
        1473                 :          0 :     PyObject *res = PyObject_CallMethodNoArgs(file, &_Py_ID(flush));
        1474         [ #  # ]:          0 :     if (!res) {
        1475                 :          0 :         return -1;
        1476                 :            :     }
        1477                 :          0 :     Py_DECREF(res);
        1478                 :            :
        1479                 :          0 :     return 0;
        1480                 :            : }
        1481                 :            :
        1482                 :            :
        1483                 :            : PyDoc_STRVAR(ExceptHookArgs__doc__,
        1484                 :            : "ExceptHookArgs\n\
        1485                 :            : \n\
        1486                 :            : Type used to pass arguments to threading.excepthook.");
        1487                 :            :
        1488                 :            : static PyStructSequence_Field ExceptHookArgs_fields[] = {
        1489                 :            :     {"exc_type", "Exception type"},
        1490                 :            :     {"exc_value", "Exception value"},
        1491                 :            :     {"exc_traceback", "Exception traceback"},
        1492                 :            :     {"thread", "Thread"},
        1493                 :            :     {0}
        1494                 :            : };
        1495                 :            :
        1496                 :            : static PyStructSequence_Desc ExceptHookArgs_desc = {
        1497                 :            :     .name = "_thread._ExceptHookArgs",
        1498                 :            :     .doc = ExceptHookArgs__doc__,
        1499                 :            :     .fields = ExceptHookArgs_fields,
        1500                 :            :     .n_in_sequence = 4
        1501                 :            : };
        1502                 :            :
        1503                 :            :
        1504                 :            : static PyObject *
        1505                 :          0 : thread_excepthook(PyObject *module, PyObject *args)
        1506                 :            : {
        1507                 :          0 :     thread_module_state *state = get_thread_state(module);
        1508                 :            :
        1509         [ #  # ]:          0 :     if (!Py_IS_TYPE(args, state->excepthook_type)) {
        1510                 :          0 :         PyErr_SetString(PyExc_TypeError,
        1511                 :            :                         "_thread.excepthook argument type "
        1512                 :            :                         "must be ExceptHookArgs");
        1513                 :          0 :         return NULL;
        1514                 :            :     }
        1515                 :            :
        1516                 :            :     /* Borrowed reference */
        1517                 :          0 :     PyObject *exc_type = PyStructSequence_GET_ITEM(args, 0);
        1518         [ #  # ]:          0 :     if (exc_type == PyExc_SystemExit) {
        1519                 :            :         /* silently ignore SystemExit */
        1520                 :          0 :         Py_RETURN_NONE;
        1521                 :            :     }
        1522                 :            :
        1523                 :            :     /* Borrowed references */
        1524                 :          0 :     PyObject *exc_value = PyStructSequence_GET_ITEM(args, 1);
        1525                 :          0 :     PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2);
        1526                 :          0 :     PyObject *thread = PyStructSequence_GET_ITEM(args, 3);
        1527                 :            :
        1528                 :          0 :     PyThreadState *tstate = _PyThreadState_GET();
        1529                 :          0 :     PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr));
        1530   [ #  #  #  # ]:          0 :     if (file == NULL || file == Py_None) {
        1531         [ #  # ]:          0 :         if (thread == Py_None) {
        1532                 :            :             /* do nothing if sys.stderr is None and thread is None */
        1533                 :          0 :             Py_RETURN_NONE;
        1534                 :            :         }
        1535                 :            :
        1536                 :          0 :         file = PyObject_GetAttrString(thread, "_stderr");
        1537         [ #  # ]:          0 :         if (file == NULL) {
        1538                 :          0 :             return NULL;
        1539                 :            :         }
        1540         [ #  # ]:          0 :         if (file == Py_None) {
        1541                 :          0 :             Py_DECREF(file);
        1542                 :            :             /* do nothing if sys.stderr is None and sys.stderr was None
        1543                 :            :                when the thread was created */
        1544                 :          0 :             Py_RETURN_NONE;
        1545                 :            :         }
        1546                 :            :     }
        1547                 :            :     else {
        1548                 :          0 :         Py_INCREF(file);
        1549                 :            :     }
        1550                 :            :
        1551                 :          0 :     int res = thread_excepthook_file(file, exc_type, exc_value, exc_tb,
        1552                 :            :                                      thread);
        1553                 :          0 :     Py_DECREF(file);
        1554         [ #  # ]:          0 :     if (res < 0) {
        1555                 :          0 :         return NULL;
        1556                 :            :     }
        1557                 :            :
        1558                 :          0 :     Py_RETURN_NONE;
        1559                 :            : }
        1560                 :            :
        1561                 :            : PyDoc_STRVAR(excepthook_doc,
        1562                 :            : "excepthook(exc_type, exc_value, exc_traceback, thread)\n\
        1563                 :            : \n\
        1564                 :            : Handle uncaught Thread.run() exception.");
        1565                 :            :
        1566                 :            : static PyMethodDef thread_methods[] = {
        1567                 :            :     {"start_new_thread",        (PyCFunction)thread_PyThread_start_new_thread,
        1568                 :            :      METH_VARARGS, start_new_doc},
        1569                 :            :     {"start_new",               (PyCFunction)thread_PyThread_start_new_thread,
        1570                 :            :      METH_VARARGS, start_new_doc},
        1571                 :            :     {"daemon_threads_allowed",  (PyCFunction)thread_daemon_threads_allowed,
        1572                 :            :      METH_NOARGS, daemon_threads_allowed_doc},
        1573                 :            :     {"allocate_lock",           thread_PyThread_allocate_lock,
        1574                 :            :      METH_NOARGS, allocate_doc},
        1575                 :            :     {"allocate",                thread_PyThread_allocate_lock,
        1576                 :            :      METH_NOARGS, allocate_doc},
        1577                 :            :     {"exit_thread",             thread_PyThread_exit_thread,
        1578                 :            :      METH_NOARGS, exit_doc},
        1579                 :            :     {"exit",                    thread_PyThread_exit_thread,
        1580                 :            :      METH_NOARGS, exit_doc},
        1581                 :            :     {"interrupt_main",          (PyCFunction)thread_PyThread_interrupt_main,
        1582                 :            :      METH_VARARGS, interrupt_doc},
        1583                 :            :     {"get_ident",               thread_get_ident,
        1584                 :            :      METH_NOARGS, get_ident_doc},
        1585                 :            : #ifdef PY_HAVE_THREAD_NATIVE_ID
        1586                 :            :     {"get_native_id",           thread_get_native_id,
        1587                 :            :      METH_NOARGS, get_native_id_doc},
        1588                 :            : #endif
        1589                 :            :     {"_count",                  thread__count,
        1590                 :            :      METH_NOARGS, _count_doc},
        1591                 :            :     {"stack_size",              (PyCFunction)thread_stack_size,
        1592                 :            :      METH_VARARGS, stack_size_doc},
        1593                 :            :     {"_set_sentinel",           thread__set_sentinel,
        1594                 :            :      METH_NOARGS, _set_sentinel_doc},
        1595                 :            :     {"_excepthook",              thread_excepthook,
        1596                 :            :      METH_O, excepthook_doc},
        1597                 :            :     {NULL,                      NULL}           /* sentinel */
        1598                 :            : };
        1599                 :            :
        1600                 :            :
        1601                 :            : /* Initialization function */
        1602                 :            :
        1603                 :            : static int
        1604                 :         26 : thread_module_exec(PyObject *module)
        1605                 :            : {
        1606                 :         26 :     thread_module_state *state = get_thread_state(module);
        1607                 :         26 :     PyObject *d = PyModule_GetDict(module);
        1608                 :            :
        1609                 :            :     // Initialize the C thread library
        1610                 :         26 :     PyThread_init_thread();
        1611                 :            :
        1612                 :            :     // Lock
        1613                 :         26 :     state->lock_type = (PyTypeObject *)PyType_FromSpec(&lock_type_spec);
        1614         [ -  + ]:         26 :     if (state->lock_type == NULL) {
        1615                 :          0 :         return -1;
        1616                 :            :     }
        1617         [ -  + ]:         26 :     if (PyDict_SetItemString(d, "LockType", (PyObject *)state->lock_type) < 0) {
        1618                 :          0 :         return -1;
        1619                 :            :     }
        1620                 :            :
        1621                 :            :     // RLock
        1622                 :         26 :     PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec);
        1623         [ -  + ]:         26 :     if (rlock_type == NULL) {
        1624                 :          0 :         return -1;
        1625                 :            :     }
        1626         [ -  + ]:         26 :     if (PyModule_AddType(module, rlock_type) < 0) {
        1627                 :          0 :         Py_DECREF(rlock_type);
        1628                 :          0 :         return -1;
        1629                 :            :     }
        1630                 :         26 :     Py_DECREF(rlock_type);
        1631                 :            :
        1632                 :            :     // Local dummy
        1633                 :         26 :     state->local_dummy_type = (PyTypeObject *)PyType_FromSpec(&local_dummy_type_spec);
        1634         [ -  + ]:         26 :     if (state->local_dummy_type == NULL) {
        1635                 :          0 :         return -1;
        1636                 :            :     }
        1637                 :            :
        1638                 :            :     // Local
        1639                 :         26 :     state->local_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &local_type_spec, NULL);
        1640         [ -  + ]:         26 :     if (state->local_type == NULL) {
        1641                 :          0 :         return -1;
        1642                 :            :     }
        1643         [ -  + ]:         26 :     if (PyModule_AddType(module, state->local_type) < 0) {
        1644                 :          0 :         return -1;
        1645                 :            :     }
        1646                 :            :
        1647                 :            :     // Add module attributes
        1648         [ -  + ]:         26 :     if (PyDict_SetItemString(d, "error", ThreadError) < 0) {
        1649                 :          0 :         return -1;
        1650                 :            :     }
        1651                 :            :
        1652                 :            :     // _ExceptHookArgs type
        1653                 :         26 :     state->excepthook_type = PyStructSequence_NewType(&ExceptHookArgs_desc);
        1654         [ -  + ]:         26 :     if (state->excepthook_type == NULL) {
        1655                 :          0 :         return -1;
        1656                 :            :     }
        1657         [ -  + ]:         26 :     if (PyModule_AddType(module, state->excepthook_type) < 0) {
        1658                 :          0 :         return -1;
        1659                 :            :     }
        1660                 :            :
        1661                 :            :     // TIMEOUT_MAX
        1662                 :         26 :     double timeout_max = (double)PY_TIMEOUT_MAX * 1e-6;
        1663                 :         26 :     double time_max = _PyTime_AsSecondsDouble(_PyTime_MAX);
        1664         [ -  + ]:         26 :     timeout_max = Py_MIN(timeout_max, time_max);
        1665                 :            :     // Round towards minus infinity
        1666                 :         26 :     timeout_max = floor(timeout_max);
        1667                 :            :
        1668         [ -  + ]:         26 :     if (PyModule_AddObject(module, "TIMEOUT_MAX",
        1669                 :            :                            PyFloat_FromDouble(timeout_max)) < 0) {
        1670                 :          0 :         return -1;
        1671                 :            :     }
        1672                 :            :
        1673                 :         26 :     return 0;
        1674                 :            : }
        1675                 :            :
        1676                 :            :
        1677                 :            : static int
        1678                 :        246 : thread_module_traverse(PyObject *module, visitproc visit, void *arg)
        1679                 :            : {
        1680                 :        246 :     thread_module_state *state = get_thread_state(module);
        1681   [ +  -  -  + ]:        246 :     Py_VISIT(state->excepthook_type);
        1682   [ +  -  -  + ]:        246 :     Py_VISIT(state->lock_type);
        1683   [ +  -  -  + ]:        246 :     Py_VISIT(state->local_type);
        1684   [ +  -  -  + ]:        246 :     Py_VISIT(state->local_dummy_type);
        1685                 :        246 :     return 0;
        1686                 :            : }
        1687                 :            :
        1688                 :            : static int
        1689                 :         49 : thread_module_clear(PyObject *module)
        1690                 :            : {
        1691                 :         49 :     thread_module_state *state = get_thread_state(module);
        1692         [ +  + ]:         49 :     Py_CLEAR(state->excepthook_type);
        1693         [ +  + ]:         49 :     Py_CLEAR(state->lock_type);
        1694         [ +  + ]:         49 :     Py_CLEAR(state->local_type);
        1695         [ +  + ]:         49 :     Py_CLEAR(state->local_dummy_type);
        1696                 :         49 :     return 0;
        1697                 :            : }
        1698                 :            :
        1699                 :            : static void
        1700                 :         26 : thread_module_free(void *module)
        1701                 :            : {
        1702                 :         26 :     thread_module_clear((PyObject *)module);
        1703                 :         26 : }
        1704                 :            :
        1705                 :            :
        1706                 :            :
        1707                 :            : PyDoc_STRVAR(thread_doc,
        1708                 :            : "This module provides primitive operations to write multi-threaded programs.\n\
        1709                 :            : The 'threading' module provides a more convenient interface.");
        1710                 :            :
        1711                 :            : static PyModuleDef_Slot thread_module_slots[] = {
        1712                 :            :     {Py_mod_exec, thread_module_exec},
        1713                 :            :     {0, NULL}
        1714                 :            : };
        1715                 :            :
        1716                 :            : static struct PyModuleDef thread_module = {
        1717                 :            :     PyModuleDef_HEAD_INIT,
        1718                 :            :     .m_name = "_thread",
        1719                 :            :     .m_doc = thread_doc,
        1720                 :            :     .m_size = sizeof(thread_module_state),
        1721                 :            :     .m_methods = thread_methods,
        1722                 :            :     .m_traverse = thread_module_traverse,
        1723                 :            :     .m_clear = thread_module_clear,
        1724                 :            :     .m_free = thread_module_free,
        1725                 :            :     .m_slots = thread_module_slots,
        1726                 :            : };
        1727                 :            :
        1728                 :            : PyMODINIT_FUNC
        1729                 :         26 : PyInit__thread(void)
        1730                 :            : {
        1731                 :         26 :     return PyModuleDef_Init(&thread_module);
        1732                 :            : }

Generated by: LCOV version 1.14