LCOV - code coverage report
Current view: top level - Modules - _queuemodule.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit acb105a7c1f] Lines: 113 133 85.0 %
Date: 2022-07-20 13:12:14 Functions: 17 17 100.0 %
Branches: 52 76 68.4 %

           Branch data     Line data    Source code
       1                 :            : #ifndef Py_BUILD_CORE_BUILTIN
       2                 :            : #  define Py_BUILD_CORE_MODULE 1
       3                 :            : #endif
       4                 :            : 
       5                 :            : #include "Python.h"
       6                 :            : #include "pycore_moduleobject.h"  // _PyModule_GetState()
       7                 :            : #include "structmember.h"         // PyMemberDef
       8                 :            : #include <stddef.h>               // offsetof()
       9                 :            : 
      10                 :            : typedef struct {
      11                 :            :     PyTypeObject *SimpleQueueType;
      12                 :            :     PyObject *EmptyError;
      13                 :            : } simplequeue_state;
      14                 :            : 
      15                 :            : static simplequeue_state *
      16                 :      38173 : simplequeue_get_state(PyObject *module)
      17                 :            : {
      18                 :      38173 :     simplequeue_state *state = _PyModule_GetState(module);
      19                 :            :     assert(state);
      20                 :      38173 :     return state;
      21                 :            : }
      22                 :            : static struct PyModuleDef queuemodule;
      23                 :            : #define simplequeue_get_state_by_type(type) \
      24                 :            :     (simplequeue_get_state(PyType_GetModuleByDef(type, &queuemodule)))
      25                 :            : 
      26                 :            : typedef struct {
      27                 :            :     PyObject_HEAD
      28                 :            :     PyThread_type_lock lock;
      29                 :            :     int locked;
      30                 :            :     PyObject *lst;
      31                 :            :     Py_ssize_t lst_pos;
      32                 :            :     PyObject *weakreflist;
      33                 :            : } simplequeueobject;
      34                 :            : 
      35                 :            : /*[clinic input]
      36                 :            : module _queue
      37                 :            : class _queue.SimpleQueue "simplequeueobject *" "simplequeue_get_state_by_type(type)->SimpleQueueType"
      38                 :            : [clinic start generated code]*/
      39                 :            : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=0a4023fe4d198c8d]*/
      40                 :            : 
      41                 :            : static int
      42                 :        381 : simplequeue_clear(simplequeueobject *self)
      43                 :            : {
      44         [ +  + ]:        381 :     Py_CLEAR(self->lst);
      45                 :        381 :     return 0;
      46                 :            : }
      47                 :            : 
      48                 :            : static void
      49                 :        379 : simplequeue_dealloc(simplequeueobject *self)
      50                 :            : {
      51                 :        379 :     PyTypeObject *tp = Py_TYPE(self);
      52                 :            : 
      53                 :        379 :     PyObject_GC_UnTrack(self);
      54         [ +  - ]:        379 :     if (self->lock != NULL) {
      55                 :            :         /* Unlock the lock so it's safe to free it */
      56         [ +  + ]:        379 :         if (self->locked > 0)
      57                 :          1 :             PyThread_release_lock(self->lock);
      58                 :        379 :         PyThread_free_lock(self->lock);
      59                 :            :     }
      60                 :        379 :     (void)simplequeue_clear(self);
      61         [ -  + ]:        379 :     if (self->weakreflist != NULL)
      62                 :          0 :         PyObject_ClearWeakRefs((PyObject *) self);
      63                 :        379 :     Py_TYPE(self)->tp_free(self);
      64                 :        379 :     Py_DECREF(tp);
      65                 :        379 : }
      66                 :            : 
      67                 :            : static int
      68                 :        726 : simplequeue_traverse(simplequeueobject *self, visitproc visit, void *arg)
      69                 :            : {
      70   [ +  -  -  + ]:        726 :     Py_VISIT(self->lst);
      71   [ +  -  -  + ]:        726 :     Py_VISIT(Py_TYPE(self));
      72                 :        726 :     return 0;
      73                 :            : }
      74                 :            : 
      75                 :            : /*[clinic input]
      76                 :            : @classmethod
      77                 :            : _queue.SimpleQueue.__new__ as simplequeue_new
      78                 :            : 
      79                 :            : Simple, unbounded, reentrant FIFO queue.
      80                 :            : [clinic start generated code]*/
      81                 :            : 
      82                 :            : static PyObject *
      83                 :        379 : simplequeue_new_impl(PyTypeObject *type)
      84                 :            : /*[clinic end generated code: output=ba97740608ba31cd input=a0674a1643e3e2fb]*/
      85                 :            : {
      86                 :            :     simplequeueobject *self;
      87                 :            : 
      88                 :        379 :     self = (simplequeueobject *) type->tp_alloc(type, 0);
      89         [ +  - ]:        379 :     if (self != NULL) {
      90                 :        379 :         self->weakreflist = NULL;
      91                 :        379 :         self->lst = PyList_New(0);
      92                 :        379 :         self->lock = PyThread_allocate_lock();
      93                 :        379 :         self->lst_pos = 0;
      94         [ -  + ]:        379 :         if (self->lock == NULL) {
      95                 :          0 :             Py_DECREF(self);
      96                 :          0 :             PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
      97                 :          0 :             return NULL;
      98                 :            :         }
      99         [ -  + ]:        379 :         if (self->lst == NULL) {
     100                 :          0 :             Py_DECREF(self);
     101                 :          0 :             return NULL;
     102                 :            :         }
     103                 :            :     }
     104                 :            : 
     105                 :        379 :     return (PyObject *) self;
     106                 :            : }
     107                 :            : 
     108                 :            : /*[clinic input]
     109                 :            : _queue.SimpleQueue.put
     110                 :            :     item: object
     111                 :            :     block: bool = True
     112                 :            :     timeout: object = None
     113                 :            : 
     114                 :            : Put the item on the queue.
     115                 :            : 
     116                 :            : The optional 'block' and 'timeout' arguments are ignored, as this method
     117                 :            : never blocks.  They are provided for compatibility with the Queue class.
     118                 :            : 
     119                 :            : [clinic start generated code]*/
     120                 :            : 
     121                 :            : static PyObject *
     122                 :      52636 : _queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
     123                 :            :                             int block, PyObject *timeout)
     124                 :            : /*[clinic end generated code: output=4333136e88f90d8b input=6e601fa707a782d5]*/
     125                 :            : {
     126                 :            :     /* BEGIN GIL-protected critical section */
     127         [ -  + ]:      52636 :     if (PyList_Append(self->lst, item) < 0)
     128                 :          0 :         return NULL;
     129         [ +  + ]:      52636 :     if (self->locked) {
     130                 :            :         /* A get() may be waiting, wake it up */
     131                 :       1267 :         self->locked = 0;
     132                 :       1267 :         PyThread_release_lock(self->lock);
     133                 :            :     }
     134                 :            :     /* END GIL-protected critical section */
     135                 :      52636 :     Py_RETURN_NONE;
     136                 :            : }
     137                 :            : 
     138                 :            : /*[clinic input]
     139                 :            : _queue.SimpleQueue.put_nowait
     140                 :            :     item: object
     141                 :            : 
     142                 :            : Put an item into the queue without blocking.
     143                 :            : 
     144                 :            : This is exactly equivalent to `put(item)` and is only provided
     145                 :            : for compatibility with the Queue class.
     146                 :            : 
     147                 :            : [clinic start generated code]*/
     148                 :            : 
     149                 :            : static PyObject *
     150                 :          1 : _queue_SimpleQueue_put_nowait_impl(simplequeueobject *self, PyObject *item)
     151                 :            : /*[clinic end generated code: output=0990536715efb1f1 input=36b1ea96756b2ece]*/
     152                 :            : {
     153                 :          1 :     return _queue_SimpleQueue_put_impl(self, item, 0, Py_None);
     154                 :            : }
     155                 :            : 
     156                 :            : static PyObject *
     157                 :      42330 : simplequeue_pop_item(simplequeueobject *self)
     158                 :            : {
     159                 :            :     Py_ssize_t count, n;
     160                 :            :     PyObject *item;
     161                 :            : 
     162                 :      42330 :     n = PyList_GET_SIZE(self->lst);
     163                 :            :     assert(self->lst_pos < n);
     164                 :            : 
     165                 :      42330 :     item = PyList_GET_ITEM(self->lst, self->lst_pos);
     166                 :      42330 :     Py_INCREF(Py_None);
     167                 :      42330 :     PyList_SET_ITEM(self->lst, self->lst_pos, Py_None);
     168                 :      42330 :     self->lst_pos += 1;
     169                 :      42330 :     count = n - self->lst_pos;
     170         [ +  + ]:      42330 :     if (self->lst_pos > count) {
     171                 :            :         /* The list is more than 50% empty, reclaim space at the beginning */
     172         [ -  + ]:      10229 :         if (PyList_SetSlice(self->lst, 0, self->lst_pos, NULL)) {
     173                 :            :             /* Undo pop */
     174                 :          0 :             self->lst_pos -= 1;
     175                 :          0 :             PyList_SET_ITEM(self->lst, self->lst_pos, item);
     176                 :          0 :             return NULL;
     177                 :            :         }
     178                 :      10229 :         self->lst_pos = 0;
     179                 :            :     }
     180                 :      42330 :     return item;
     181                 :            : }
     182                 :            : 
     183                 :            : /*[clinic input]
     184                 :            : _queue.SimpleQueue.get
     185                 :            : 
     186                 :            :     cls: defining_class
     187                 :            :     /
     188                 :            :     block: bool = True
     189                 :            :     timeout as timeout_obj: object = None
     190                 :            : 
     191                 :            : Remove and return an item from the queue.
     192                 :            : 
     193                 :            : If optional args 'block' is true and 'timeout' is None (the default),
     194                 :            : block if necessary until an item is available. If 'timeout' is
     195                 :            : a non-negative number, it blocks at most 'timeout' seconds and raises
     196                 :            : the Empty exception if no item was available within that time.
     197                 :            : Otherwise ('block' is false), return an item if one is immediately
     198                 :            : available, else raise the Empty exception ('timeout' is ignored
     199                 :            : in that case).
     200                 :            : 
     201                 :            : [clinic start generated code]*/
     202                 :            : 
     203                 :            : static PyObject *
     204                 :      42358 : _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
     205                 :            :                             int block, PyObject *timeout_obj)
     206                 :            : /*[clinic end generated code: output=5c2cca914cd1e55b input=5b4047bfbc645ec1]*/
     207                 :            : {
     208                 :      42358 :     _PyTime_t endtime = 0;
     209                 :            :     _PyTime_t timeout;
     210                 :            :     PyObject *item;
     211                 :            :     PyLockStatus r;
     212                 :            :     PY_TIMEOUT_T microseconds;
     213                 :            : 
     214         [ +  + ]:      42358 :     if (block == 0) {
     215                 :            :         /* Non-blocking */
     216                 :      10653 :         microseconds = 0;
     217                 :            :     }
     218         [ +  + ]:      31705 :     else if (timeout_obj != Py_None) {
     219                 :            :         /* With timeout */
     220         [ -  + ]:       1053 :         if (_PyTime_FromSecondsObject(&timeout,
     221                 :            :                                       timeout_obj, _PyTime_ROUND_CEILING) < 0) {
     222                 :          0 :             return NULL;
     223                 :            :         }
     224         [ +  + ]:       1053 :         if (timeout < 0) {
     225                 :          1 :             PyErr_SetString(PyExc_ValueError,
     226                 :            :                             "'timeout' must be a non-negative number");
     227                 :          1 :             return NULL;
     228                 :            :         }
     229                 :       1052 :         microseconds = _PyTime_AsMicroseconds(timeout,
     230                 :            :                                               _PyTime_ROUND_CEILING);
     231         [ -  + ]:       1052 :         if (microseconds > PY_TIMEOUT_MAX) {
     232                 :          0 :             PyErr_SetString(PyExc_OverflowError,
     233                 :            :                             "timeout value is too large");
     234                 :          0 :             return NULL;
     235                 :            :         }
     236                 :       1052 :         endtime = _PyDeadline_Init(timeout);
     237                 :            :     }
     238                 :            :     else {
     239                 :            :         /* Infinitely blocking */
     240                 :      30652 :         microseconds = -1;
     241                 :            :     }
     242                 :            : 
     243                 :            :     /* put() signals the queue to be non-empty by releasing the lock.
     244                 :            :      * So we simply try to acquire the lock in a loop, until the condition
     245                 :            :      * (queue non-empty) becomes true.
     246                 :            :      */
     247         [ +  + ]:      87382 :     while (self->lst_pos == PyList_GET_SIZE(self->lst)) {
     248                 :            :         /* First a simple non-blocking try without releasing the GIL */
     249                 :       2695 :         r = PyThread_acquire_lock_timed(self->lock, 0, 0);
     250   [ +  +  +  + ]:       2695 :         if (r == PY_LOCK_FAILURE && microseconds != 0) {
     251                 :       1467 :             Py_BEGIN_ALLOW_THREADS
     252                 :       1467 :             r = PyThread_acquire_lock_timed(self->lock, microseconds, 1);
     253                 :       1466 :             Py_END_ALLOW_THREADS
     254                 :            :         }
     255                 :            : 
     256   [ -  +  -  - ]:       2695 :         if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) {
     257                 :          0 :             return NULL;
     258                 :            :         }
     259         [ +  + ]:       2695 :         if (r == PY_LOCK_FAILURE) {
     260                 :         27 :             PyObject *module = PyType_GetModule(cls);
     261                 :         27 :             simplequeue_state *state = simplequeue_get_state(module);
     262                 :            :             /* Timed out */
     263                 :         27 :             PyErr_SetNone(state->EmptyError);
     264                 :         27 :             return NULL;
     265                 :            :         }
     266                 :       2668 :         self->locked = 1;
     267                 :            : 
     268                 :            :         /* Adjust timeout for next iteration (if any) */
     269         [ +  - ]:       2668 :         if (microseconds > 0) {
     270                 :          0 :             timeout = _PyDeadline_Get(endtime);
     271                 :          0 :             microseconds = _PyTime_AsMicroseconds(timeout,
     272                 :            :                                                   _PyTime_ROUND_CEILING);
     273                 :            :         }
     274                 :            :     }
     275                 :            : 
     276                 :            :     /* BEGIN GIL-protected critical section */
     277                 :            :     assert(self->lst_pos < PyList_GET_SIZE(self->lst));
     278                 :      42330 :     item = simplequeue_pop_item(self);
     279         [ +  + ]:      42330 :     if (self->locked) {
     280                 :       1400 :         PyThread_release_lock(self->lock);
     281                 :       1400 :         self->locked = 0;
     282                 :            :     }
     283                 :            :     /* END GIL-protected critical section */
     284                 :            : 
     285                 :      42330 :     return item;
     286                 :            : }
     287                 :            : 
     288                 :            : /*[clinic input]
     289                 :            : _queue.SimpleQueue.get_nowait
     290                 :            : 
     291                 :            :     cls: defining_class
     292                 :            :     /
     293                 :            : 
     294                 :            : Remove and return an item from the queue without blocking.
     295                 :            : 
     296                 :            : Only get an item if one is immediately available. Otherwise
     297                 :            : raise the Empty exception.
     298                 :            : [clinic start generated code]*/
     299                 :            : 
     300                 :            : static PyObject *
     301                 :         51 : _queue_SimpleQueue_get_nowait_impl(simplequeueobject *self,
     302                 :            :                                    PyTypeObject *cls)
     303                 :            : /*[clinic end generated code: output=620c58e2750f8b8a input=842f732bf04216d3]*/
     304                 :            : {
     305                 :         51 :     return _queue_SimpleQueue_get_impl(self, cls, 0, Py_None);
     306                 :            : }
     307                 :            : 
     308                 :            : /*[clinic input]
     309                 :            : _queue.SimpleQueue.empty -> bool
     310                 :            : 
     311                 :            : Return True if the queue is empty, False otherwise (not reliable!).
     312                 :            : [clinic start generated code]*/
     313                 :            : 
     314                 :            : static int
     315                 :         10 : _queue_SimpleQueue_empty_impl(simplequeueobject *self)
     316                 :            : /*[clinic end generated code: output=1a02a1b87c0ef838 input=1a98431c45fd66f9]*/
     317                 :            : {
     318                 :         10 :     return self->lst_pos == PyList_GET_SIZE(self->lst);
     319                 :            : }
     320                 :            : 
     321                 :            : /*[clinic input]
     322                 :            : _queue.SimpleQueue.qsize -> Py_ssize_t
     323                 :            : 
     324                 :            : Return the approximate size of the queue (not reliable!).
     325                 :            : [clinic start generated code]*/
     326                 :            : 
     327                 :            : static Py_ssize_t
     328                 :         12 : _queue_SimpleQueue_qsize_impl(simplequeueobject *self)
     329                 :            : /*[clinic end generated code: output=f9dcd9d0a90e121e input=7a74852b407868a1]*/
     330                 :            : {
     331                 :         12 :     return PyList_GET_SIZE(self->lst) - self->lst_pos;
     332                 :            : }
     333                 :            : 
     334                 :            : static int
     335                 :      34596 : queue_traverse(PyObject *m, visitproc visit, void *arg)
     336                 :            : {
     337                 :      34596 :     simplequeue_state *state = simplequeue_get_state(m);
     338   [ +  +  -  + ]:      34596 :     Py_VISIT(state->SimpleQueueType);
     339   [ +  -  -  + ]:      34596 :     Py_VISIT(state->EmptyError);
     340                 :      34596 :     return 0;
     341                 :            : }
     342                 :            : 
     343                 :            : static int
     344                 :       1796 : queue_clear(PyObject *m)
     345                 :            : {
     346                 :       1796 :     simplequeue_state *state = simplequeue_get_state(m);
     347         [ +  + ]:       1796 :     Py_CLEAR(state->SimpleQueueType);
     348         [ +  + ]:       1796 :     Py_CLEAR(state->EmptyError);
     349                 :       1796 :     return 0;
     350                 :            : }
     351                 :            : 
     352                 :            : static void
     353                 :        996 : queue_free(void *m)
     354                 :            : {
     355                 :        996 :     queue_clear((PyObject *)m);
     356                 :        996 : }
     357                 :            : 
     358                 :            : #include "clinic/_queuemodule.c.h"
     359                 :            : 
     360                 :            : 
     361                 :            : static PyMethodDef simplequeue_methods[] = {
     362                 :            :     _QUEUE_SIMPLEQUEUE_EMPTY_METHODDEF
     363                 :            :     _QUEUE_SIMPLEQUEUE_GET_METHODDEF
     364                 :            :     _QUEUE_SIMPLEQUEUE_GET_NOWAIT_METHODDEF
     365                 :            :     _QUEUE_SIMPLEQUEUE_PUT_METHODDEF
     366                 :            :     _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF
     367                 :            :     _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF
     368                 :            :     {"__class_getitem__",    Py_GenericAlias,
     369                 :            :     METH_O|METH_CLASS,       PyDoc_STR("See PEP 585")},
     370                 :            :     {NULL,           NULL}              /* sentinel */
     371                 :            : };
     372                 :            : 
     373                 :            : static struct PyMemberDef simplequeue_members[] = {
     374                 :            :     {"__weaklistoffset__", T_PYSSIZET, offsetof(simplequeueobject, weakreflist), READONLY},
     375                 :            :     {NULL},
     376                 :            : };
     377                 :            : 
     378                 :            : static PyType_Slot simplequeue_slots[] = {
     379                 :            :     {Py_tp_dealloc, simplequeue_dealloc},
     380                 :            :     {Py_tp_doc, (void *)simplequeue_new__doc__},
     381                 :            :     {Py_tp_traverse, simplequeue_traverse},
     382                 :            :     {Py_tp_clear, simplequeue_clear},
     383                 :            :     {Py_tp_members, simplequeue_members},
     384                 :            :     {Py_tp_methods, simplequeue_methods},
     385                 :            :     {Py_tp_new, simplequeue_new},
     386                 :            :     {0, NULL},
     387                 :            : };
     388                 :            : 
     389                 :            : static PyType_Spec simplequeue_spec = {
     390                 :            :     .name = "_queue.SimpleQueue",
     391                 :            :     .basicsize = sizeof(simplequeueobject),
     392                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
     393                 :            :               Py_TPFLAGS_IMMUTABLETYPE),
     394                 :            :     .slots = simplequeue_slots,
     395                 :            : };
     396                 :            : 
     397                 :            : 
     398                 :            : /* Initialization function */
     399                 :            : 
     400                 :            : PyDoc_STRVAR(queue_module_doc,
     401                 :            : "C implementation of the Python queue module.\n\
     402                 :            : This module is an implementation detail, please do not use it directly.");
     403                 :            : 
     404                 :            : static int
     405                 :        996 : queuemodule_exec(PyObject *module)
     406                 :            : {
     407                 :        996 :     simplequeue_state *state = simplequeue_get_state(module);
     408                 :            : 
     409                 :        996 :     state->EmptyError = PyErr_NewExceptionWithDoc(
     410                 :            :         "_queue.Empty",
     411                 :            :         "Exception raised by Queue.get(block=0)/get_nowait().",
     412                 :            :         NULL, NULL);
     413         [ -  + ]:        996 :     if (state->EmptyError == NULL) {
     414                 :          0 :         return -1;
     415                 :            :     }
     416         [ -  + ]:        996 :     if (PyModule_AddObjectRef(module, "Empty", state->EmptyError) < 0) {
     417                 :          0 :         return -1;
     418                 :            :     }
     419                 :            : 
     420                 :        996 :     state->SimpleQueueType = (PyTypeObject *)PyType_FromModuleAndSpec(
     421                 :            :         module, &simplequeue_spec, NULL);
     422         [ -  + ]:        996 :     if (state->SimpleQueueType == NULL) {
     423                 :          0 :         return -1;
     424                 :            :     }
     425         [ -  + ]:        996 :     if (PyModule_AddType(module, state->SimpleQueueType) < 0) {
     426                 :          0 :         return -1;
     427                 :            :     }
     428                 :            : 
     429                 :        996 :     return 0;
     430                 :            : }
     431                 :            : 
     432                 :            : static PyModuleDef_Slot queuemodule_slots[] = {
     433                 :            :     {Py_mod_exec, queuemodule_exec},
     434                 :            :     {0, NULL}
     435                 :            : };
     436                 :            : 
     437                 :            : 
     438                 :            : static struct PyModuleDef queuemodule = {
     439                 :            :     .m_base = PyModuleDef_HEAD_INIT,
     440                 :            :     .m_name = "_queue",
     441                 :            :     .m_doc = queue_module_doc,
     442                 :            :     .m_size = sizeof(simplequeue_state),
     443                 :            :     .m_slots = queuemodule_slots,
     444                 :            :     .m_traverse = queue_traverse,
     445                 :            :     .m_clear = queue_clear,
     446                 :            :     .m_free = queue_free,
     447                 :            : };
     448                 :            : 
     449                 :            : 
     450                 :            : PyMODINIT_FUNC
     451                 :        996 : PyInit__queue(void)
     452                 :            : {
     453                 :        996 :    return PyModuleDef_Init(&queuemodule);
     454                 :            : }

Generated by: LCOV version 1.14