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