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