| #include "Python.h" |
| #include "structmember.h" /* offsetof */ |
| #include "pythread.h" |
| |
| /*[clinic input] |
| module _queue |
| class _queue.SimpleQueue "simplequeueobject *" "&PySimpleQueueType" |
| [clinic start generated code]*/ |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=cf49af81bcbbbea6]*/ |
| |
| static PyTypeObject PySimpleQueueType; /* forward decl */ |
| |
| static PyObject *EmptyError; |
| |
| |
| typedef struct { |
| PyObject_HEAD |
| PyThread_type_lock lock; |
| int locked; |
| PyObject *lst; |
| Py_ssize_t lst_pos; |
| PyObject *weakreflist; |
| } simplequeueobject; |
| |
| |
| static void |
| simplequeue_dealloc(simplequeueobject *self) |
| { |
| PyObject_GC_UnTrack(self); |
| if (self->lock != NULL) { |
| /* Unlock the lock so it's safe to free it */ |
| if (self->locked > 0) |
| PyThread_release_lock(self->lock); |
| PyThread_free_lock(self->lock); |
| } |
| Py_XDECREF(self->lst); |
| if (self->weakreflist != NULL) |
| PyObject_ClearWeakRefs((PyObject *) self); |
| Py_TYPE(self)->tp_free(self); |
| } |
| |
| static int |
| simplequeue_traverse(simplequeueobject *self, visitproc visit, void *arg) |
| { |
| Py_VISIT(self->lst); |
| return 0; |
| } |
| |
| /*[clinic input] |
| @classmethod |
| _queue.SimpleQueue.__new__ as simplequeue_new |
| |
| Simple, unbounded, reentrant FIFO queue. |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| simplequeue_new_impl(PyTypeObject *type) |
| /*[clinic end generated code: output=ba97740608ba31cd input=a0674a1643e3e2fb]*/ |
| { |
| simplequeueobject *self; |
| |
| self = (simplequeueobject *) type->tp_alloc(type, 0); |
| if (self != NULL) { |
| self->weakreflist = NULL; |
| self->lst = PyList_New(0); |
| self->lock = PyThread_allocate_lock(); |
| self->lst_pos = 0; |
| if (self->lock == NULL) { |
| Py_DECREF(self); |
| PyErr_SetString(PyExc_MemoryError, "can't allocate lock"); |
| return NULL; |
| } |
| if (self->lst == NULL) { |
| Py_DECREF(self); |
| return NULL; |
| } |
| } |
| |
| return (PyObject *) self; |
| } |
| |
| /*[clinic input] |
| _queue.SimpleQueue.put |
| item: object |
| block: bool = True |
| timeout: object = None |
| |
| Put the item on the queue. |
| |
| The optional 'block' and 'timeout' arguments are ignored, as this method |
| never blocks. They are provided for compatibility with the Queue class. |
| |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item, |
| int block, PyObject *timeout) |
| /*[clinic end generated code: output=4333136e88f90d8b input=6e601fa707a782d5]*/ |
| { |
| /* BEGIN GIL-protected critical section */ |
| if (PyList_Append(self->lst, item) < 0) |
| return NULL; |
| if (self->locked) { |
| /* A get() may be waiting, wake it up */ |
| self->locked = 0; |
| PyThread_release_lock(self->lock); |
| } |
| /* END GIL-protected critical section */ |
| Py_RETURN_NONE; |
| } |
| |
| /*[clinic input] |
| _queue.SimpleQueue.put_nowait |
| item: object |
| |
| Put an item into the queue without blocking. |
| |
| This is exactly equivalent to `put(item)` and is only provided |
| for compatibility with the Queue class. |
| |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _queue_SimpleQueue_put_nowait_impl(simplequeueobject *self, PyObject *item) |
| /*[clinic end generated code: output=0990536715efb1f1 input=36b1ea96756b2ece]*/ |
| { |
| return _queue_SimpleQueue_put_impl(self, item, 0, Py_None); |
| } |
| |
| static PyObject * |
| simplequeue_pop_item(simplequeueobject *self) |
| { |
| Py_ssize_t count, n; |
| PyObject *item; |
| |
| n = PyList_GET_SIZE(self->lst); |
| assert(self->lst_pos < n); |
| |
| item = PyList_GET_ITEM(self->lst, self->lst_pos); |
| Py_INCREF(Py_None); |
| PyList_SET_ITEM(self->lst, self->lst_pos, Py_None); |
| self->lst_pos += 1; |
| count = n - self->lst_pos; |
| if (self->lst_pos > count) { |
| /* The list is more than 50% empty, reclaim space at the beginning */ |
| if (PyList_SetSlice(self->lst, 0, self->lst_pos, NULL)) { |
| /* Undo pop */ |
| self->lst_pos -= 1; |
| PyList_SET_ITEM(self->lst, self->lst_pos, item); |
| return NULL; |
| } |
| self->lst_pos = 0; |
| } |
| return item; |
| } |
| |
| /*[clinic input] |
| _queue.SimpleQueue.get |
| block: bool = True |
| timeout: object = None |
| |
| Remove and return an item from the queue. |
| |
| If optional args 'block' is true and 'timeout' is None (the default), |
| block if necessary until an item is available. If 'timeout' is |
| a non-negative number, it blocks at most 'timeout' seconds and raises |
| the Empty exception if no item was available within that time. |
| Otherwise ('block' is false), return an item if one is immediately |
| available, else raise the Empty exception ('timeout' is ignored |
| in that case). |
| |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _queue_SimpleQueue_get_impl(simplequeueobject *self, int block, |
| PyObject *timeout) |
| /*[clinic end generated code: output=ec82a7157dcccd1a input=4bf691f9f01fa297]*/ |
| { |
| _PyTime_t endtime = 0; |
| _PyTime_t timeout_val; |
| PyObject *item; |
| PyLockStatus r; |
| PY_TIMEOUT_T microseconds; |
| |
| if (block == 0) { |
| /* Non-blocking */ |
| microseconds = 0; |
| } |
| else if (timeout != Py_None) { |
| /* With timeout */ |
| if (_PyTime_FromSecondsObject(&timeout_val, |
| timeout, _PyTime_ROUND_CEILING) < 0) |
| return NULL; |
| if (timeout_val < 0) { |
| PyErr_SetString(PyExc_ValueError, |
| "'timeout' must be a non-negative number"); |
| return NULL; |
| } |
| microseconds = _PyTime_AsMicroseconds(timeout_val, |
| _PyTime_ROUND_CEILING); |
| if (microseconds >= PY_TIMEOUT_MAX) { |
| PyErr_SetString(PyExc_OverflowError, |
| "timeout value is too large"); |
| return NULL; |
| } |
| endtime = _PyTime_GetMonotonicClock() + timeout_val; |
| } |
| else { |
| /* Infinitely blocking */ |
| microseconds = -1; |
| } |
| |
| /* put() signals the queue to be non-empty by releasing the lock. |
| * So we simply try to acquire the lock in a loop, until the condition |
| * (queue non-empty) becomes true. |
| */ |
| while (self->lst_pos == PyList_GET_SIZE(self->lst)) { |
| /* First a simple non-blocking try without releasing the GIL */ |
| r = PyThread_acquire_lock_timed(self->lock, 0, 0); |
| if (r == PY_LOCK_FAILURE && microseconds != 0) { |
| Py_BEGIN_ALLOW_THREADS |
| r = PyThread_acquire_lock_timed(self->lock, microseconds, 1); |
| Py_END_ALLOW_THREADS |
| } |
| if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) { |
| return NULL; |
| } |
| if (r == PY_LOCK_FAILURE) { |
| /* Timed out */ |
| PyErr_SetNone(EmptyError); |
| return NULL; |
| } |
| self->locked = 1; |
| /* Adjust timeout for next iteration (if any) */ |
| if (endtime > 0) { |
| timeout_val = endtime - _PyTime_GetMonotonicClock(); |
| microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING); |
| } |
| } |
| /* BEGIN GIL-protected critical section */ |
| assert(self->lst_pos < PyList_GET_SIZE(self->lst)); |
| item = simplequeue_pop_item(self); |
| if (self->locked) { |
| PyThread_release_lock(self->lock); |
| self->locked = 0; |
| } |
| /* END GIL-protected critical section */ |
| |
| return item; |
| } |
| |
| /*[clinic input] |
| _queue.SimpleQueue.get_nowait |
| |
| Remove and return an item from the queue without blocking. |
| |
| Only get an item if one is immediately available. Otherwise |
| raise the Empty exception. |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _queue_SimpleQueue_get_nowait_impl(simplequeueobject *self) |
| /*[clinic end generated code: output=a89731a75dbe4937 input=6fe5102db540a1b9]*/ |
| { |
| return _queue_SimpleQueue_get_impl(self, 0, Py_None); |
| } |
| |
| /*[clinic input] |
| _queue.SimpleQueue.empty -> bool |
| |
| Return True if the queue is empty, False otherwise (not reliable!). |
| [clinic start generated code]*/ |
| |
| static int |
| _queue_SimpleQueue_empty_impl(simplequeueobject *self) |
| /*[clinic end generated code: output=1a02a1b87c0ef838 input=1a98431c45fd66f9]*/ |
| { |
| return self->lst_pos == PyList_GET_SIZE(self->lst); |
| } |
| |
| /*[clinic input] |
| _queue.SimpleQueue.qsize -> Py_ssize_t |
| |
| Return the approximate size of the queue (not reliable!). |
| [clinic start generated code]*/ |
| |
| static Py_ssize_t |
| _queue_SimpleQueue_qsize_impl(simplequeueobject *self) |
| /*[clinic end generated code: output=f9dcd9d0a90e121e input=7a74852b407868a1]*/ |
| { |
| return PyList_GET_SIZE(self->lst) - self->lst_pos; |
| } |
| |
| |
| #include "clinic/_queuemodule.c.h" |
| |
| |
| static PyMethodDef simplequeue_methods[] = { |
| _QUEUE_SIMPLEQUEUE_EMPTY_METHODDEF |
| _QUEUE_SIMPLEQUEUE_GET_METHODDEF |
| _QUEUE_SIMPLEQUEUE_GET_NOWAIT_METHODDEF |
| _QUEUE_SIMPLEQUEUE_PUT_METHODDEF |
| _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF |
| _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF |
| {NULL, NULL} /* sentinel */ |
| }; |
| |
| |
| static PyTypeObject PySimpleQueueType = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| "_queue.SimpleQueue", /*tp_name*/ |
| sizeof(simplequeueobject), /*tp_basicsize*/ |
| 0, /*tp_itemsize*/ |
| /* methods */ |
| (destructor)simplequeue_dealloc, /*tp_dealloc*/ |
| 0, /*tp_vectorcall_offset*/ |
| 0, /*tp_getattr*/ |
| 0, /*tp_setattr*/ |
| 0, /*tp_as_async*/ |
| 0, /*tp_repr*/ |
| 0, /*tp_as_number*/ |
| 0, /*tp_as_sequence*/ |
| 0, /*tp_as_mapping*/ |
| 0, /*tp_hash*/ |
| 0, /*tp_call*/ |
| 0, /*tp_str*/ |
| 0, /*tp_getattro*/ |
| 0, /*tp_setattro*/ |
| 0, /*tp_as_buffer*/ |
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
| | Py_TPFLAGS_HAVE_GC, /* tp_flags */ |
| simplequeue_new__doc__, /*tp_doc*/ |
| (traverseproc)simplequeue_traverse, /*tp_traverse*/ |
| 0, /*tp_clear*/ |
| 0, /*tp_richcompare*/ |
| offsetof(simplequeueobject, weakreflist), /*tp_weaklistoffset*/ |
| 0, /*tp_iter*/ |
| 0, /*tp_iternext*/ |
| simplequeue_methods, /*tp_methods*/ |
| 0, /* tp_members */ |
| 0, /* tp_getset */ |
| 0, /* tp_base */ |
| 0, /* tp_dict */ |
| 0, /* tp_descr_get */ |
| 0, /* tp_descr_set */ |
| 0, /* tp_dictoffset */ |
| 0, /* tp_init */ |
| 0, /* tp_alloc */ |
| simplequeue_new /* tp_new */ |
| }; |
| |
| |
| /* Initialization function */ |
| |
| PyDoc_STRVAR(queue_module_doc, |
| "C implementation of the Python queue module.\n\ |
| This module is an implementation detail, please do not use it directly."); |
| |
| static struct PyModuleDef queuemodule = { |
| PyModuleDef_HEAD_INIT, |
| "_queue", |
| queue_module_doc, |
| -1, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL |
| }; |
| |
| |
| PyMODINIT_FUNC |
| PyInit__queue(void) |
| { |
| PyObject *m; |
| |
| /* Create the module */ |
| m = PyModule_Create(&queuemodule); |
| if (m == NULL) |
| return NULL; |
| |
| EmptyError = PyErr_NewExceptionWithDoc( |
| "_queue.Empty", |
| "Exception raised by Queue.get(block=0)/get_nowait().", |
| NULL, NULL); |
| if (EmptyError == NULL) |
| return NULL; |
| |
| Py_INCREF(EmptyError); |
| if (PyModule_AddObject(m, "Empty", EmptyError) < 0) |
| return NULL; |
| |
| if (PyType_Ready(&PySimpleQueueType) < 0) |
| return NULL; |
| Py_INCREF(&PySimpleQueueType); |
| if (PyModule_AddObject(m, "SimpleQueue", (PyObject *)&PySimpleQueueType) < 0) |
| return NULL; |
| |
| return m; |
| } |