diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:24:06 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:41:34 +0300 |
commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/tools/python3/src/Modules/_queuemodule.c | |
parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
download | ydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz |
add ydb deps
Diffstat (limited to 'contrib/tools/python3/src/Modules/_queuemodule.c')
-rw-r--r-- | contrib/tools/python3/src/Modules/_queuemodule.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Modules/_queuemodule.c b/contrib/tools/python3/src/Modules/_queuemodule.c new file mode 100644 index 0000000000..af19dd6c19 --- /dev/null +++ b/contrib/tools/python3/src/Modules/_queuemodule.c @@ -0,0 +1,454 @@ +#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; + + 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 && Py_MakePendingCalls() < 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}, + {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); +} |