blob: 35906f08c12b72870df00522b080ac796b51d5d8 [file] [log] [blame]
Antoine Pitrou94e16962018-01-16 00:27:16 +01001#include "Python.h"
2#include "structmember.h" /* offsetof */
3#include "pythread.h"
4
5/*[clinic input]
6module _queue
7class _queue.SimpleQueue "simplequeueobject *" "&PySimpleQueueType"
8[clinic start generated code]*/
9/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cf49af81bcbbbea6]*/
10
Benjamin Peterson9b50a7f2018-07-07 15:21:15 -070011static PyTypeObject PySimpleQueueType; /* forward decl */
Antoine Pitrou94e16962018-01-16 00:27:16 +010012
13static PyObject *EmptyError;
14
15
16typedef struct {
17 PyObject_HEAD
18 PyThread_type_lock lock;
19 int locked;
20 PyObject *lst;
21 Py_ssize_t lst_pos;
22 PyObject *weakreflist;
23} simplequeueobject;
24
25
26static void
27simplequeue_dealloc(simplequeueobject *self)
28{
Victor Stinner1a6be912018-11-13 12:52:18 +010029 PyObject_GC_UnTrack(self);
Antoine Pitrou94e16962018-01-16 00:27:16 +010030 if (self->lock != NULL) {
31 /* Unlock the lock so it's safe to free it */
32 if (self->locked > 0)
33 PyThread_release_lock(self->lock);
34 PyThread_free_lock(self->lock);
35 }
36 Py_XDECREF(self->lst);
37 if (self->weakreflist != NULL)
38 PyObject_ClearWeakRefs((PyObject *) self);
39 Py_TYPE(self)->tp_free(self);
40}
41
42static int
43simplequeue_traverse(simplequeueobject *self, visitproc visit, void *arg)
44{
45 Py_VISIT(self->lst);
46 return 0;
47}
48
49/*[clinic input]
50@classmethod
51_queue.SimpleQueue.__new__ as simplequeue_new
52
53Simple, unbounded, reentrant FIFO queue.
54[clinic start generated code]*/
55
56static PyObject *
57simplequeue_new_impl(PyTypeObject *type)
58/*[clinic end generated code: output=ba97740608ba31cd input=a0674a1643e3e2fb]*/
59{
60 simplequeueobject *self;
61
62 self = (simplequeueobject *) type->tp_alloc(type, 0);
63 if (self != NULL) {
64 self->weakreflist = NULL;
65 self->lst = PyList_New(0);
66 self->lock = PyThread_allocate_lock();
67 self->lst_pos = 0;
68 if (self->lock == NULL) {
69 Py_DECREF(self);
70 PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
71 return NULL;
72 }
73 if (self->lst == NULL) {
74 Py_DECREF(self);
75 return NULL;
76 }
77 }
78
79 return (PyObject *) self;
80}
81
82/*[clinic input]
83_queue.SimpleQueue.put
84 item: object
85 block: bool = True
86 timeout: object = None
87
88Put the item on the queue.
89
90The optional 'block' and 'timeout' arguments are ignored, as this method
91never blocks. They are provided for compatibility with the Queue class.
92
93[clinic start generated code]*/
94
95static PyObject *
96_queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
97 int block, PyObject *timeout)
98/*[clinic end generated code: output=4333136e88f90d8b input=6e601fa707a782d5]*/
99{
100 /* BEGIN GIL-protected critical section */
101 if (PyList_Append(self->lst, item) < 0)
102 return NULL;
103 if (self->locked) {
104 /* A get() may be waiting, wake it up */
105 self->locked = 0;
106 PyThread_release_lock(self->lock);
107 }
108 /* END GIL-protected critical section */
109 Py_RETURN_NONE;
110}
111
112/*[clinic input]
113_queue.SimpleQueue.put_nowait
114 item: object
115
116Put an item into the queue without blocking.
117
118This is exactly equivalent to `put(item)` and is only provided
119for compatibility with the Queue class.
120
121[clinic start generated code]*/
122
123static PyObject *
124_queue_SimpleQueue_put_nowait_impl(simplequeueobject *self, PyObject *item)
125/*[clinic end generated code: output=0990536715efb1f1 input=36b1ea96756b2ece]*/
126{
127 return _queue_SimpleQueue_put_impl(self, item, 0, Py_None);
128}
129
130static PyObject *
131simplequeue_pop_item(simplequeueobject *self)
132{
133 Py_ssize_t count, n;
134 PyObject *item;
135
136 n = PyList_GET_SIZE(self->lst);
137 assert(self->lst_pos < n);
138
139 item = PyList_GET_ITEM(self->lst, self->lst_pos);
140 Py_INCREF(Py_None);
141 PyList_SET_ITEM(self->lst, self->lst_pos, Py_None);
142 self->lst_pos += 1;
143 count = n - self->lst_pos;
144 if (self->lst_pos > count) {
145 /* The list is more than 50% empty, reclaim space at the beginning */
146 if (PyList_SetSlice(self->lst, 0, self->lst_pos, NULL)) {
147 /* Undo pop */
148 self->lst_pos -= 1;
149 PyList_SET_ITEM(self->lst, self->lst_pos, item);
150 return NULL;
151 }
152 self->lst_pos = 0;
153 }
154 return item;
155}
156
157/*[clinic input]
158_queue.SimpleQueue.get
159 block: bool = True
160 timeout: object = None
161
162Remove and return an item from the queue.
163
164If optional args 'block' is true and 'timeout' is None (the default),
165block if necessary until an item is available. If 'timeout' is
166a non-negative number, it blocks at most 'timeout' seconds and raises
167the Empty exception if no item was available within that time.
168Otherwise ('block' is false), return an item if one is immediately
169available, else raise the Empty exception ('timeout' is ignored
170in that case).
171
172[clinic start generated code]*/
173
174static PyObject *
175_queue_SimpleQueue_get_impl(simplequeueobject *self, int block,
176 PyObject *timeout)
177/*[clinic end generated code: output=ec82a7157dcccd1a input=4bf691f9f01fa297]*/
178{
179 _PyTime_t endtime = 0;
180 _PyTime_t timeout_val;
181 PyObject *item;
182 PyLockStatus r;
183 PY_TIMEOUT_T microseconds;
184
185 if (block == 0) {
186 /* Non-blocking */
187 microseconds = 0;
188 }
189 else if (timeout != Py_None) {
190 /* With timeout */
191 if (_PyTime_FromSecondsObject(&timeout_val,
192 timeout, _PyTime_ROUND_CEILING) < 0)
193 return NULL;
194 if (timeout_val < 0) {
195 PyErr_SetString(PyExc_ValueError,
196 "'timeout' must be a non-negative number");
197 return NULL;
198 }
199 microseconds = _PyTime_AsMicroseconds(timeout_val,
200 _PyTime_ROUND_CEILING);
201 if (microseconds >= PY_TIMEOUT_MAX) {
202 PyErr_SetString(PyExc_OverflowError,
203 "timeout value is too large");
204 return NULL;
205 }
206 endtime = _PyTime_GetMonotonicClock() + timeout_val;
207 }
208 else {
209 /* Infinitely blocking */
210 microseconds = -1;
211 }
212
213 /* put() signals the queue to be non-empty by releasing the lock.
214 * So we simply try to acquire the lock in a loop, until the condition
215 * (queue non-empty) becomes true.
216 */
217 while (self->lst_pos == PyList_GET_SIZE(self->lst)) {
218 /* First a simple non-blocking try without releasing the GIL */
219 r = PyThread_acquire_lock_timed(self->lock, 0, 0);
220 if (r == PY_LOCK_FAILURE && microseconds != 0) {
221 Py_BEGIN_ALLOW_THREADS
222 r = PyThread_acquire_lock_timed(self->lock, microseconds, 1);
223 Py_END_ALLOW_THREADS
224 }
225 if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) {
226 return NULL;
227 }
228 if (r == PY_LOCK_FAILURE) {
229 /* Timed out */
230 PyErr_SetNone(EmptyError);
231 return NULL;
232 }
233 self->locked = 1;
234 /* Adjust timeout for next iteration (if any) */
235 if (endtime > 0) {
236 timeout_val = endtime - _PyTime_GetMonotonicClock();
237 microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING);
238 }
239 }
240 /* BEGIN GIL-protected critical section */
241 assert(self->lst_pos < PyList_GET_SIZE(self->lst));
242 item = simplequeue_pop_item(self);
243 if (self->locked) {
244 PyThread_release_lock(self->lock);
245 self->locked = 0;
246 }
247 /* END GIL-protected critical section */
248
249 return item;
250}
251
252/*[clinic input]
253_queue.SimpleQueue.get_nowait
254
255Remove and return an item from the queue without blocking.
256
257Only get an item if one is immediately available. Otherwise
258raise the Empty exception.
259[clinic start generated code]*/
260
261static PyObject *
262_queue_SimpleQueue_get_nowait_impl(simplequeueobject *self)
263/*[clinic end generated code: output=a89731a75dbe4937 input=6fe5102db540a1b9]*/
264{
265 return _queue_SimpleQueue_get_impl(self, 0, Py_None);
266}
267
268/*[clinic input]
269_queue.SimpleQueue.empty -> bool
270
271Return True if the queue is empty, False otherwise (not reliable!).
272[clinic start generated code]*/
273
274static int
275_queue_SimpleQueue_empty_impl(simplequeueobject *self)
276/*[clinic end generated code: output=1a02a1b87c0ef838 input=1a98431c45fd66f9]*/
277{
278 return self->lst_pos == PyList_GET_SIZE(self->lst);
279}
280
281/*[clinic input]
282_queue.SimpleQueue.qsize -> Py_ssize_t
283
284Return the approximate size of the queue (not reliable!).
285[clinic start generated code]*/
286
287static Py_ssize_t
288_queue_SimpleQueue_qsize_impl(simplequeueobject *self)
289/*[clinic end generated code: output=f9dcd9d0a90e121e input=7a74852b407868a1]*/
290{
291 return PyList_GET_SIZE(self->lst) - self->lst_pos;
292}
293
294
295#include "clinic/_queuemodule.c.h"
296
297
298static PyMethodDef simplequeue_methods[] = {
299 _QUEUE_SIMPLEQUEUE_EMPTY_METHODDEF
300 _QUEUE_SIMPLEQUEUE_GET_METHODDEF
301 _QUEUE_SIMPLEQUEUE_GET_NOWAIT_METHODDEF
302 _QUEUE_SIMPLEQUEUE_PUT_METHODDEF
303 _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF
304 _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF
305 {NULL, NULL} /* sentinel */
306};
307
308
Benjamin Peterson9b50a7f2018-07-07 15:21:15 -0700309static PyTypeObject PySimpleQueueType = {
Antoine Pitrou94e16962018-01-16 00:27:16 +0100310 PyVarObject_HEAD_INIT(NULL, 0)
311 "_queue.SimpleQueue", /*tp_name*/
Peter Eisentraut0e0bc4e2018-09-10 18:46:08 +0200312 sizeof(simplequeueobject), /*tp_basicsize*/
Antoine Pitrou94e16962018-01-16 00:27:16 +0100313 0, /*tp_itemsize*/
314 /* methods */
315 (destructor)simplequeue_dealloc, /*tp_dealloc*/
316 0, /*tp_print*/
317 0, /*tp_getattr*/
318 0, /*tp_setattr*/
319 0, /*tp_reserved*/
320 0, /*tp_repr*/
321 0, /*tp_as_number*/
322 0, /*tp_as_sequence*/
323 0, /*tp_as_mapping*/
324 0, /*tp_hash*/
325 0, /*tp_call*/
326 0, /*tp_str*/
327 0, /*tp_getattro*/
328 0, /*tp_setattro*/
329 0, /*tp_as_buffer*/
330 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
331 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
332 simplequeue_new__doc__, /*tp_doc*/
333 (traverseproc)simplequeue_traverse, /*tp_traverse*/
334 0, /*tp_clear*/
335 0, /*tp_richcompare*/
336 offsetof(simplequeueobject, weakreflist), /*tp_weaklistoffset*/
337 0, /*tp_iter*/
338 0, /*tp_iternext*/
339 simplequeue_methods, /*tp_methods*/
340 0, /* tp_members */
341 0, /* tp_getset */
342 0, /* tp_base */
343 0, /* tp_dict */
344 0, /* tp_descr_get */
345 0, /* tp_descr_set */
346 0, /* tp_dictoffset */
347 0, /* tp_init */
348 0, /* tp_alloc */
349 simplequeue_new /* tp_new */
350};
351
352
353/* Initialization function */
354
355PyDoc_STRVAR(queue_module_doc,
356"C implementation of the Python queue module.\n\
357This module is an implementation detail, please do not use it directly.");
358
359static struct PyModuleDef queuemodule = {
360 PyModuleDef_HEAD_INIT,
361 "_queue",
362 queue_module_doc,
363 -1,
364 NULL,
365 NULL,
366 NULL,
367 NULL,
368 NULL
369};
370
371
372PyMODINIT_FUNC
373PyInit__queue(void)
374{
375 PyObject *m;
376
377 /* Create the module */
378 m = PyModule_Create(&queuemodule);
379 if (m == NULL)
380 return NULL;
381
382 EmptyError = PyErr_NewExceptionWithDoc(
383 "_queue.Empty",
384 "Exception raised by Queue.get(block=0)/get_nowait().",
385 NULL, NULL);
386 if (EmptyError == NULL)
387 return NULL;
388
389 Py_INCREF(EmptyError);
390 if (PyModule_AddObject(m, "Empty", EmptyError) < 0)
391 return NULL;
392
393 if (PyType_Ready(&PySimpleQueueType) < 0)
394 return NULL;
395 Py_INCREF(&PySimpleQueueType);
396 if (PyModule_AddObject(m, "SimpleQueue", (PyObject *)&PySimpleQueueType) < 0)
397 return NULL;
398
399 return m;
400}