Antoine Pitrou | 94e1696 | 2018-01-16 00:27:16 +0100 | [diff] [blame] | 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 | |
Benjamin Peterson | 9b50a7f | 2018-07-07 15:21:15 -0700 | [diff] [blame] | 11 | static PyTypeObject PySimpleQueueType; /* forward decl */ |
Antoine Pitrou | 94e1696 | 2018-01-16 00:27:16 +0100 | [diff] [blame] | 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 | |
Benjamin Peterson | 9b50a7f | 2018-07-07 15:21:15 -0700 | [diff] [blame] | 309 | static PyTypeObject PySimpleQueueType = { |
Antoine Pitrou | 94e1696 | 2018-01-16 00:27:16 +0100 | [diff] [blame] | 310 | PyVarObject_HEAD_INIT(NULL, 0) |
| 311 | "_queue.SimpleQueue", /*tp_name*/ |
| 312 | sizeof(simplequeueobject), /*tp_size*/ |
| 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 | } |