123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- #ifndef Py_BUILD_CORE_BUILTIN
- # define Py_BUILD_CORE_MODULE 1
- #endif
- #include "Python.h"
- #include "pycore_moduleobject.h" // _PyModule_GetState()
- #include "structmember.h" // PyMemberDef
- #include <stddef.h> // offsetof()
- typedef struct {
- PyTypeObject *SimpleQueueType;
- PyObject *EmptyError;
- } simplequeue_state;
- static simplequeue_state *
- simplequeue_get_state(PyObject *module)
- {
- simplequeue_state *state = _PyModule_GetState(module);
- assert(state);
- return state;
- }
- static struct PyModuleDef queuemodule;
- #define simplequeue_get_state_by_type(type) \
- (simplequeue_get_state(PyType_GetModuleByDef(type, &queuemodule)))
- typedef struct {
- PyObject_HEAD
- PyThread_type_lock lock;
- int locked;
- PyObject *lst;
- Py_ssize_t lst_pos;
- PyObject *weakreflist;
- } simplequeueobject;
- /*[clinic input]
- module _queue
- class _queue.SimpleQueue "simplequeueobject *" "simplequeue_get_state_by_type(type)->SimpleQueueType"
- [clinic start generated code]*/
- /*[clinic end generated code: output=da39a3ee5e6b4b0d input=0a4023fe4d198c8d]*/
- static int
- simplequeue_clear(simplequeueobject *self)
- {
- Py_CLEAR(self->lst);
- return 0;
- }
- static void
- simplequeue_dealloc(simplequeueobject *self)
- {
- PyTypeObject *tp = Py_TYPE(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);
- }
- (void)simplequeue_clear(self);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *) self);
- Py_TYPE(self)->tp_free(self);
- Py_DECREF(tp);
- }
- static int
- simplequeue_traverse(simplequeueobject *self, visitproc visit, void *arg)
- {
- Py_VISIT(self->lst);
- Py_VISIT(Py_TYPE(self));
- 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
- cls: defining_class
- /
- block: bool = True
- timeout as timeout_obj: 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, PyTypeObject *cls,
- int block, PyObject *timeout_obj)
- /*[clinic end generated code: output=5c2cca914cd1e55b input=5b4047bfbc645ec1]*/
- {
- _PyTime_t endtime = 0;
- _PyTime_t timeout;
- PyObject *item;
- PyLockStatus r;
- PY_TIMEOUT_T microseconds;
- PyThreadState *tstate = PyThreadState_Get();
- if (block == 0) {
- /* Non-blocking */
- microseconds = 0;
- }
- else if (timeout_obj != Py_None) {
- /* With timeout */
- if (_PyTime_FromSecondsObject(&timeout,
- timeout_obj, _PyTime_ROUND_CEILING) < 0) {
- return NULL;
- }
- if (timeout < 0) {
- PyErr_SetString(PyExc_ValueError,
- "'timeout' must be a non-negative number");
- return NULL;
- }
- microseconds = _PyTime_AsMicroseconds(timeout,
- _PyTime_ROUND_CEILING);
- if (microseconds > PY_TIMEOUT_MAX) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout value is too large");
- return NULL;
- }
- endtime = _PyDeadline_Init(timeout);
- }
- 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 && _PyEval_MakePendingCalls(tstate) < 0) {
- return NULL;
- }
- if (r == PY_LOCK_FAILURE) {
- PyObject *module = PyType_GetModule(cls);
- simplequeue_state *state = simplequeue_get_state(module);
- /* Timed out */
- PyErr_SetNone(state->EmptyError);
- return NULL;
- }
- self->locked = 1;
- /* Adjust timeout for next iteration (if any) */
- if (microseconds > 0) {
- timeout = _PyDeadline_Get(endtime);
- microseconds = _PyTime_AsMicroseconds(timeout,
- _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
- cls: defining_class
- /
- 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,
- PyTypeObject *cls)
- /*[clinic end generated code: output=620c58e2750f8b8a input=842f732bf04216d3]*/
- {
- return _queue_SimpleQueue_get_impl(self, cls, 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;
- }
- static int
- queue_traverse(PyObject *m, visitproc visit, void *arg)
- {
- simplequeue_state *state = simplequeue_get_state(m);
- Py_VISIT(state->SimpleQueueType);
- Py_VISIT(state->EmptyError);
- return 0;
- }
- static int
- queue_clear(PyObject *m)
- {
- simplequeue_state *state = simplequeue_get_state(m);
- Py_CLEAR(state->SimpleQueueType);
- Py_CLEAR(state->EmptyError);
- return 0;
- }
- static void
- queue_free(void *m)
- {
- queue_clear((PyObject *)m);
- }
- #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
- {"__class_getitem__", Py_GenericAlias,
- METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
- {NULL, NULL} /* sentinel */
- };
- static struct PyMemberDef simplequeue_members[] = {
- {"__weaklistoffset__", T_PYSSIZET, offsetof(simplequeueobject, weakreflist), READONLY},
- {NULL},
- };
- static PyType_Slot simplequeue_slots[] = {
- {Py_tp_dealloc, simplequeue_dealloc},
- {Py_tp_doc, (void *)simplequeue_new__doc__},
- {Py_tp_traverse, simplequeue_traverse},
- {Py_tp_clear, simplequeue_clear},
- {Py_tp_members, simplequeue_members},
- {Py_tp_methods, simplequeue_methods},
- {Py_tp_new, simplequeue_new},
- {0, NULL},
- };
- static PyType_Spec simplequeue_spec = {
- .name = "_queue.SimpleQueue",
- .basicsize = sizeof(simplequeueobject),
- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_IMMUTABLETYPE),
- .slots = simplequeue_slots,
- };
- /* 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 int
- queuemodule_exec(PyObject *module)
- {
- simplequeue_state *state = simplequeue_get_state(module);
- state->EmptyError = PyErr_NewExceptionWithDoc(
- "_queue.Empty",
- "Exception raised by Queue.get(block=0)/get_nowait().",
- NULL, NULL);
- if (state->EmptyError == NULL) {
- return -1;
- }
- if (PyModule_AddObjectRef(module, "Empty", state->EmptyError) < 0) {
- return -1;
- }
- state->SimpleQueueType = (PyTypeObject *)PyType_FromModuleAndSpec(
- module, &simplequeue_spec, NULL);
- if (state->SimpleQueueType == NULL) {
- return -1;
- }
- if (PyModule_AddType(module, state->SimpleQueueType) < 0) {
- return -1;
- }
- return 0;
- }
- static PyModuleDef_Slot queuemodule_slots[] = {
- {Py_mod_exec, queuemodule_exec},
- {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
- {0, NULL}
- };
- static struct PyModuleDef queuemodule = {
- .m_base = PyModuleDef_HEAD_INIT,
- .m_name = "_queue",
- .m_doc = queue_module_doc,
- .m_size = sizeof(simplequeue_state),
- .m_slots = queuemodule_slots,
- .m_traverse = queue_traverse,
- .m_clear = queue_clear,
- .m_free = queue_free,
- };
- PyMODINIT_FUNC
- PyInit__queue(void)
- {
- return PyModuleDef_Init(&queuemodule);
- }
|