1 #include "Python.h" 2 #include "structmember.h" /* offsetof */ 3 #include "pythread.h" 4 5 /*[clinic input] 6 module _queue 7 class _queue.SimpleQueue "simplequeueobject *" "&PySimpleQueueType" 8 [clinic start generated code]*/ 9 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=cf49af81bcbbbea6]*/ 10 11 static PyTypeObject PySimpleQueueType; /* forward decl */ 12 13 static PyObject *EmptyError; 14 15 16 typedef 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 26 static void 27 simplequeue_dealloc(simplequeueobject *self) 28 { 29 _PyObject_GC_UNTRACK(self); 30 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 42 static int 43 simplequeue_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 53 Simple, unbounded, reentrant FIFO queue. 54 [clinic start generated code]*/ 55 56 static PyObject * 57 simplequeue_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 88 Put the item on the queue. 89 90 The optional 'block' and 'timeout' arguments are ignored, as this method 91 never blocks. They are provided for compatibility with the Queue class. 92 93 [clinic start generated code]*/ 94 95 static 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 116 Put an item into the queue without blocking. 117 118 This is exactly equivalent to `put(item)` and is only provided 119 for compatibility with the Queue class. 120 121 [clinic start generated code]*/ 122 123 static 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 130 static PyObject * 131 simplequeue_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 162 Remove and return an item from the queue. 163 164 If optional args 'block' is true and 'timeout' is None (the default), 165 block if necessary until an item is available. If 'timeout' is 166 a non-negative number, it blocks at most 'timeout' seconds and raises 167 the Empty exception if no item was available within that time. 168 Otherwise ('block' is false), return an item if one is immediately 169 available, else raise the Empty exception ('timeout' is ignored 170 in that case). 171 172 [clinic start generated code]*/ 173 174 static 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 255 Remove and return an item from the queue without blocking. 256 257 Only get an item if one is immediately available. Otherwise 258 raise the Empty exception. 259 [clinic start generated code]*/ 260 261 static 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 271 Return True if the queue is empty, False otherwise (not reliable!). 272 [clinic start generated code]*/ 273 274 static 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 284 Return the approximate size of the queue (not reliable!). 285 [clinic start generated code]*/ 286 287 static 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 298 static 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 309 static PyTypeObject PySimpleQueueType = { 310 PyVarObject_HEAD_INIT(NULL, 0) 311 "_queue.SimpleQueue", /*tp_name*/ 312 sizeof(simplequeueobject), /*tp_basicsize*/ 313 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 355 PyDoc_STRVAR(queue_module_doc, 356 "C implementation of the Python queue module.\n\ 357 This module is an implementation detail, please do not use it directly."); 358 359 static 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 372 PyMODINIT_FUNC 373 PyInit__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 } 401