Home | History | Annotate | Download | only in Modules
      1 #include "Python.h"
      2 #include "structmember.h" /* offsetof */
      3 #include "pythread.h"
      4 
      5 /*[clinic input]
      6 module _queue
      7 class _queue.SimpleQueue "simplequeueobject *" "&PySimpleQueueType"
      8 [clinic start generated code]*/
      9 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=cf49af81bcbbbea6]*/
     10 
     11 static PyTypeObject PySimpleQueueType;  /* forward decl */
     12 
     13 static PyObject *EmptyError;
     14 
     15 
     16 typedef struct {
     17     PyObject_HEAD
     18     PyThread_type_lock lock;
     19     int locked;
     20     PyObject *lst;
     21     Py_ssize_t lst_pos;
     22     PyObject *weakreflist;
     23 } simplequeueobject;
     24 
     25 
     26 static void
     27 simplequeue_dealloc(simplequeueobject *self)
     28 {
     29     _PyObject_GC_UNTRACK(self);
     30     if (self->lock != NULL) {
     31         /* Unlock the lock so it's safe to free it */
     32         if (self->locked > 0)
     33             PyThread_release_lock(self->lock);
     34         PyThread_free_lock(self->lock);
     35     }
     36     Py_XDECREF(self->lst);
     37     if (self->weakreflist != NULL)
     38         PyObject_ClearWeakRefs((PyObject *) self);
     39     Py_TYPE(self)->tp_free(self);
     40 }
     41 
     42 static int
     43 simplequeue_traverse(simplequeueobject *self, visitproc visit, void *arg)
     44 {
     45     Py_VISIT(self->lst);
     46     return 0;
     47 }
     48 
     49 /*[clinic input]
     50 @classmethod
     51 _queue.SimpleQueue.__new__ as simplequeue_new
     52 
     53 Simple, unbounded, reentrant FIFO queue.
     54 [clinic start generated code]*/
     55 
     56 static PyObject *
     57 simplequeue_new_impl(PyTypeObject *type)
     58 /*[clinic end generated code: output=ba97740608ba31cd input=a0674a1643e3e2fb]*/
     59 {
     60     simplequeueobject *self;
     61 
     62     self = (simplequeueobject *) type->tp_alloc(type, 0);
     63     if (self != NULL) {
     64         self->weakreflist = NULL;
     65         self->lst = PyList_New(0);
     66         self->lock = PyThread_allocate_lock();
     67         self->lst_pos = 0;
     68         if (self->lock == NULL) {
     69             Py_DECREF(self);
     70             PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
     71             return NULL;
     72         }
     73         if (self->lst == NULL) {
     74             Py_DECREF(self);
     75             return NULL;
     76         }
     77     }
     78 
     79     return (PyObject *) self;
     80 }
     81 
     82 /*[clinic input]
     83 _queue.SimpleQueue.put
     84     item: object
     85     block: bool = True
     86     timeout: object = None
     87 
     88 Put the item on the queue.
     89 
     90 The optional 'block' and 'timeout' arguments are ignored, as this method
     91 never blocks.  They are provided for compatibility with the Queue class.
     92 
     93 [clinic start generated code]*/
     94 
     95 static PyObject *
     96 _queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
     97                             int block, PyObject *timeout)
     98 /*[clinic end generated code: output=4333136e88f90d8b input=6e601fa707a782d5]*/
     99 {
    100     /* BEGIN GIL-protected critical section */
    101     if (PyList_Append(self->lst, item) < 0)
    102         return NULL;
    103     if (self->locked) {
    104         /* A get() may be waiting, wake it up */
    105         self->locked = 0;
    106         PyThread_release_lock(self->lock);
    107     }
    108     /* END GIL-protected critical section */
    109     Py_RETURN_NONE;
    110 }
    111 
    112 /*[clinic input]
    113 _queue.SimpleQueue.put_nowait
    114     item: object
    115 
    116 Put an item into the queue without blocking.
    117 
    118 This is exactly equivalent to `put(item)` and is only provided
    119 for compatibility with the Queue class.
    120 
    121 [clinic start generated code]*/
    122 
    123 static PyObject *
    124 _queue_SimpleQueue_put_nowait_impl(simplequeueobject *self, PyObject *item)
    125 /*[clinic end generated code: output=0990536715efb1f1 input=36b1ea96756b2ece]*/
    126 {
    127     return _queue_SimpleQueue_put_impl(self, item, 0, Py_None);
    128 }
    129 
    130 static PyObject *
    131 simplequeue_pop_item(simplequeueobject *self)
    132 {
    133     Py_ssize_t count, n;
    134     PyObject *item;
    135 
    136     n = PyList_GET_SIZE(self->lst);
    137     assert(self->lst_pos < n);
    138 
    139     item = PyList_GET_ITEM(self->lst, self->lst_pos);
    140     Py_INCREF(Py_None);
    141     PyList_SET_ITEM(self->lst, self->lst_pos, Py_None);
    142     self->lst_pos += 1;
    143     count = n - self->lst_pos;
    144     if (self->lst_pos > count) {
    145         /* The list is more than 50% empty, reclaim space at the beginning */
    146         if (PyList_SetSlice(self->lst, 0, self->lst_pos, NULL)) {
    147             /* Undo pop */
    148             self->lst_pos -= 1;
    149             PyList_SET_ITEM(self->lst, self->lst_pos, item);
    150             return NULL;
    151         }
    152         self->lst_pos = 0;
    153     }
    154     return item;
    155 }
    156 
    157 /*[clinic input]
    158 _queue.SimpleQueue.get
    159     block: bool = True
    160     timeout: object = None
    161 
    162 Remove and return an item from the queue.
    163 
    164 If optional args 'block' is true and 'timeout' is None (the default),
    165 block if necessary until an item is available. If 'timeout' is
    166 a non-negative number, it blocks at most 'timeout' seconds and raises
    167 the Empty exception if no item was available within that time.
    168 Otherwise ('block' is false), return an item if one is immediately
    169 available, else raise the Empty exception ('timeout' is ignored
    170 in that case).
    171 
    172 [clinic start generated code]*/
    173 
    174 static PyObject *
    175 _queue_SimpleQueue_get_impl(simplequeueobject *self, int block,
    176                             PyObject *timeout)
    177 /*[clinic end generated code: output=ec82a7157dcccd1a input=4bf691f9f01fa297]*/
    178 {
    179     _PyTime_t endtime = 0;
    180     _PyTime_t timeout_val;
    181     PyObject *item;
    182     PyLockStatus r;
    183     PY_TIMEOUT_T microseconds;
    184 
    185     if (block == 0) {
    186         /* Non-blocking */
    187         microseconds = 0;
    188     }
    189     else if (timeout != Py_None) {
    190         /* With timeout */
    191         if (_PyTime_FromSecondsObject(&timeout_val,
    192                                       timeout, _PyTime_ROUND_CEILING) < 0)
    193             return NULL;
    194         if (timeout_val < 0) {
    195             PyErr_SetString(PyExc_ValueError,
    196                             "'timeout' must be a non-negative number");
    197             return NULL;
    198         }
    199         microseconds = _PyTime_AsMicroseconds(timeout_val,
    200                                               _PyTime_ROUND_CEILING);
    201         if (microseconds >= PY_TIMEOUT_MAX) {
    202             PyErr_SetString(PyExc_OverflowError,
    203                             "timeout value is too large");
    204             return NULL;
    205         }
    206         endtime = _PyTime_GetMonotonicClock() + timeout_val;
    207     }
    208     else {
    209         /* Infinitely blocking */
    210         microseconds = -1;
    211     }
    212 
    213     /* put() signals the queue to be non-empty by releasing the lock.
    214      * So we simply try to acquire the lock in a loop, until the condition
    215      * (queue non-empty) becomes true.
    216      */
    217     while (self->lst_pos == PyList_GET_SIZE(self->lst)) {
    218         /* First a simple non-blocking try without releasing the GIL */
    219         r = PyThread_acquire_lock_timed(self->lock, 0, 0);
    220         if (r == PY_LOCK_FAILURE && microseconds != 0) {
    221             Py_BEGIN_ALLOW_THREADS
    222             r = PyThread_acquire_lock_timed(self->lock, microseconds, 1);
    223             Py_END_ALLOW_THREADS
    224         }
    225         if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) {
    226             return NULL;
    227         }
    228         if (r == PY_LOCK_FAILURE) {
    229             /* Timed out */
    230             PyErr_SetNone(EmptyError);
    231             return NULL;
    232         }
    233         self->locked = 1;
    234         /* Adjust timeout for next iteration (if any) */
    235         if (endtime > 0) {
    236             timeout_val = endtime - _PyTime_GetMonotonicClock();
    237             microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING);
    238         }
    239     }
    240     /* BEGIN GIL-protected critical section */
    241     assert(self->lst_pos < PyList_GET_SIZE(self->lst));
    242     item = simplequeue_pop_item(self);
    243     if (self->locked) {
    244         PyThread_release_lock(self->lock);
    245         self->locked = 0;
    246     }
    247     /* END GIL-protected critical section */
    248 
    249     return item;
    250 }
    251 
    252 /*[clinic input]
    253 _queue.SimpleQueue.get_nowait
    254 
    255 Remove and return an item from the queue without blocking.
    256 
    257 Only get an item if one is immediately available. Otherwise
    258 raise the Empty exception.
    259 [clinic start generated code]*/
    260 
    261 static PyObject *
    262 _queue_SimpleQueue_get_nowait_impl(simplequeueobject *self)
    263 /*[clinic end generated code: output=a89731a75dbe4937 input=6fe5102db540a1b9]*/
    264 {
    265     return _queue_SimpleQueue_get_impl(self, 0, Py_None);
    266 }
    267 
    268 /*[clinic input]
    269 _queue.SimpleQueue.empty -> bool
    270 
    271 Return True if the queue is empty, False otherwise (not reliable!).
    272 [clinic start generated code]*/
    273 
    274 static int
    275 _queue_SimpleQueue_empty_impl(simplequeueobject *self)
    276 /*[clinic end generated code: output=1a02a1b87c0ef838 input=1a98431c45fd66f9]*/
    277 {
    278     return self->lst_pos == PyList_GET_SIZE(self->lst);
    279 }
    280 
    281 /*[clinic input]
    282 _queue.SimpleQueue.qsize -> Py_ssize_t
    283 
    284 Return the approximate size of the queue (not reliable!).
    285 [clinic start generated code]*/
    286 
    287 static Py_ssize_t
    288 _queue_SimpleQueue_qsize_impl(simplequeueobject *self)
    289 /*[clinic end generated code: output=f9dcd9d0a90e121e input=7a74852b407868a1]*/
    290 {
    291     return PyList_GET_SIZE(self->lst) - self->lst_pos;
    292 }
    293 
    294 
    295 #include "clinic/_queuemodule.c.h"
    296 
    297 
    298 static PyMethodDef simplequeue_methods[] = {
    299     _QUEUE_SIMPLEQUEUE_EMPTY_METHODDEF
    300     _QUEUE_SIMPLEQUEUE_GET_METHODDEF
    301     _QUEUE_SIMPLEQUEUE_GET_NOWAIT_METHODDEF
    302     _QUEUE_SIMPLEQUEUE_PUT_METHODDEF
    303     _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF
    304     _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF
    305     {NULL,           NULL}              /* sentinel */
    306 };
    307 
    308 
    309 static PyTypeObject PySimpleQueueType = {
    310     PyVarObject_HEAD_INIT(NULL, 0)
    311     "_queue.SimpleQueue",               /*tp_name*/
    312     sizeof(simplequeueobject),          /*tp_basicsize*/
    313     0,                                  /*tp_itemsize*/
    314     /* methods */
    315     (destructor)simplequeue_dealloc,    /*tp_dealloc*/
    316     0,                                  /*tp_print*/
    317     0,                                  /*tp_getattr*/
    318     0,                                  /*tp_setattr*/
    319     0,                                  /*tp_reserved*/
    320     0,                                  /*tp_repr*/
    321     0,                                  /*tp_as_number*/
    322     0,                                  /*tp_as_sequence*/
    323     0,                                  /*tp_as_mapping*/
    324     0,                                  /*tp_hash*/
    325     0,                                  /*tp_call*/
    326     0,                                  /*tp_str*/
    327     0,                                  /*tp_getattro*/
    328     0,                                  /*tp_setattro*/
    329     0,                                  /*tp_as_buffer*/
    330     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
    331         | Py_TPFLAGS_HAVE_GC,           /* tp_flags */
    332     simplequeue_new__doc__,             /*tp_doc*/
    333     (traverseproc)simplequeue_traverse, /*tp_traverse*/
    334     0,                                  /*tp_clear*/
    335     0,                                  /*tp_richcompare*/
    336     offsetof(simplequeueobject, weakreflist), /*tp_weaklistoffset*/
    337     0,                                  /*tp_iter*/
    338     0,                                  /*tp_iternext*/
    339     simplequeue_methods,                /*tp_methods*/
    340     0,                                  /* tp_members */
    341     0,                                  /* tp_getset */
    342     0,                                  /* tp_base */
    343     0,                                  /* tp_dict */
    344     0,                                  /* tp_descr_get */
    345     0,                                  /* tp_descr_set */
    346     0,                                  /* tp_dictoffset */
    347     0,                                  /* tp_init */
    348     0,                                  /* tp_alloc */
    349     simplequeue_new                     /* tp_new */
    350 };
    351 
    352 
    353 /* Initialization function */
    354 
    355 PyDoc_STRVAR(queue_module_doc,
    356 "C implementation of the Python queue module.\n\
    357 This module is an implementation detail, please do not use it directly.");
    358 
    359 static struct PyModuleDef queuemodule = {
    360     PyModuleDef_HEAD_INIT,
    361     "_queue",
    362     queue_module_doc,
    363     -1,
    364     NULL,
    365     NULL,
    366     NULL,
    367     NULL,
    368     NULL
    369 };
    370 
    371 
    372 PyMODINIT_FUNC
    373 PyInit__queue(void)
    374 {
    375     PyObject *m;
    376 
    377     /* Create the module */
    378     m = PyModule_Create(&queuemodule);
    379     if (m == NULL)
    380         return NULL;
    381 
    382     EmptyError = PyErr_NewExceptionWithDoc(
    383         "_queue.Empty",
    384         "Exception raised by Queue.get(block=0)/get_nowait().",
    385         NULL, NULL);
    386     if (EmptyError == NULL)
    387         return NULL;
    388 
    389     Py_INCREF(EmptyError);
    390     if (PyModule_AddObject(m, "Empty", EmptyError) < 0)
    391         return NULL;
    392 
    393     if (PyType_Ready(&PySimpleQueueType) < 0)
    394         return NULL;
    395     Py_INCREF(&PySimpleQueueType);
    396     if (PyModule_AddObject(m, "SimpleQueue", (PyObject *)&PySimpleQueueType) < 0)
    397         return NULL;
    398 
    399     return m;
    400 }
    401