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