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