mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 07:01:21 +00:00 
			
		
		
		
	If only offsetof() is needed: include stddef.h instead. When structmember.h is used, add a comment explaining that PyMemberDef is used.
		
			
				
	
	
		
			399 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			399 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "Python.h"
 | 
						|
#include <stddef.h>               // offsetof()
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
module _queue
 | 
						|
class _queue.SimpleQueue "simplequeueobject *" "&PySimpleQueueType"
 | 
						|
[clinic start generated code]*/
 | 
						|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cf49af81bcbbbea6]*/
 | 
						|
 | 
						|
static PyTypeObject PySimpleQueueType;  /* forward decl */
 | 
						|
 | 
						|
static PyObject *EmptyError;
 | 
						|
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    PyObject_HEAD
 | 
						|
    PyThread_type_lock lock;
 | 
						|
    int locked;
 | 
						|
    PyObject *lst;
 | 
						|
    Py_ssize_t lst_pos;
 | 
						|
    PyObject *weakreflist;
 | 
						|
} simplequeueobject;
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
simplequeue_dealloc(simplequeueobject *self)
 | 
						|
{
 | 
						|
    PyObject_GC_UnTrack(self);
 | 
						|
    if (self->lock != NULL) {
 | 
						|
        /* Unlock the lock so it's safe to free it */
 | 
						|
        if (self->locked > 0)
 | 
						|
            PyThread_release_lock(self->lock);
 | 
						|
        PyThread_free_lock(self->lock);
 | 
						|
    }
 | 
						|
    Py_XDECREF(self->lst);
 | 
						|
    if (self->weakreflist != NULL)
 | 
						|
        PyObject_ClearWeakRefs((PyObject *) self);
 | 
						|
    Py_TYPE(self)->tp_free(self);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
simplequeue_traverse(simplequeueobject *self, visitproc visit, void *arg)
 | 
						|
{
 | 
						|
    Py_VISIT(self->lst);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
@classmethod
 | 
						|
_queue.SimpleQueue.__new__ as simplequeue_new
 | 
						|
 | 
						|
Simple, unbounded, reentrant FIFO queue.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
simplequeue_new_impl(PyTypeObject *type)
 | 
						|
/*[clinic end generated code: output=ba97740608ba31cd input=a0674a1643e3e2fb]*/
 | 
						|
{
 | 
						|
    simplequeueobject *self;
 | 
						|
 | 
						|
    self = (simplequeueobject *) type->tp_alloc(type, 0);
 | 
						|
    if (self != NULL) {
 | 
						|
        self->weakreflist = NULL;
 | 
						|
        self->lst = PyList_New(0);
 | 
						|
        self->lock = PyThread_allocate_lock();
 | 
						|
        self->lst_pos = 0;
 | 
						|
        if (self->lock == NULL) {
 | 
						|
            Py_DECREF(self);
 | 
						|
            PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        if (self->lst == NULL) {
 | 
						|
            Py_DECREF(self);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return (PyObject *) self;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
_queue.SimpleQueue.put
 | 
						|
    item: object
 | 
						|
    block: bool = True
 | 
						|
    timeout: object = None
 | 
						|
 | 
						|
Put the item on the queue.
 | 
						|
 | 
						|
The optional 'block' and 'timeout' arguments are ignored, as this method
 | 
						|
never blocks.  They are provided for compatibility with the Queue class.
 | 
						|
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
_queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
 | 
						|
                            int block, PyObject *timeout)
 | 
						|
/*[clinic end generated code: output=4333136e88f90d8b input=6e601fa707a782d5]*/
 | 
						|
{
 | 
						|
    /* BEGIN GIL-protected critical section */
 | 
						|
    if (PyList_Append(self->lst, item) < 0)
 | 
						|
        return NULL;
 | 
						|
    if (self->locked) {
 | 
						|
        /* A get() may be waiting, wake it up */
 | 
						|
        self->locked = 0;
 | 
						|
        PyThread_release_lock(self->lock);
 | 
						|
    }
 | 
						|
    /* END GIL-protected critical section */
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
_queue.SimpleQueue.put_nowait
 | 
						|
    item: object
 | 
						|
 | 
						|
Put an item into the queue without blocking.
 | 
						|
 | 
						|
This is exactly equivalent to `put(item)` and is only provided
 | 
						|
for compatibility with the Queue class.
 | 
						|
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
_queue_SimpleQueue_put_nowait_impl(simplequeueobject *self, PyObject *item)
 | 
						|
/*[clinic end generated code: output=0990536715efb1f1 input=36b1ea96756b2ece]*/
 | 
						|
{
 | 
						|
    return _queue_SimpleQueue_put_impl(self, item, 0, Py_None);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
simplequeue_pop_item(simplequeueobject *self)
 | 
						|
{
 | 
						|
    Py_ssize_t count, n;
 | 
						|
    PyObject *item;
 | 
						|
 | 
						|
    n = PyList_GET_SIZE(self->lst);
 | 
						|
    assert(self->lst_pos < n);
 | 
						|
 | 
						|
    item = PyList_GET_ITEM(self->lst, self->lst_pos);
 | 
						|
    Py_INCREF(Py_None);
 | 
						|
    PyList_SET_ITEM(self->lst, self->lst_pos, Py_None);
 | 
						|
    self->lst_pos += 1;
 | 
						|
    count = n - self->lst_pos;
 | 
						|
    if (self->lst_pos > count) {
 | 
						|
        /* The list is more than 50% empty, reclaim space at the beginning */
 | 
						|
        if (PyList_SetSlice(self->lst, 0, self->lst_pos, NULL)) {
 | 
						|
            /* Undo pop */
 | 
						|
            self->lst_pos -= 1;
 | 
						|
            PyList_SET_ITEM(self->lst, self->lst_pos, item);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        self->lst_pos = 0;
 | 
						|
    }
 | 
						|
    return item;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
_queue.SimpleQueue.get
 | 
						|
    block: bool = True
 | 
						|
    timeout: object = None
 | 
						|
 | 
						|
Remove and return an item from the queue.
 | 
						|
 | 
						|
If optional args 'block' is true and 'timeout' is None (the default),
 | 
						|
block if necessary until an item is available. If 'timeout' is
 | 
						|
a non-negative number, it blocks at most 'timeout' seconds and raises
 | 
						|
the Empty exception if no item was available within that time.
 | 
						|
Otherwise ('block' is false), return an item if one is immediately
 | 
						|
available, else raise the Empty exception ('timeout' is ignored
 | 
						|
in that case).
 | 
						|
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
_queue_SimpleQueue_get_impl(simplequeueobject *self, int block,
 | 
						|
                            PyObject *timeout)
 | 
						|
/*[clinic end generated code: output=ec82a7157dcccd1a input=4bf691f9f01fa297]*/
 | 
						|
{
 | 
						|
    _PyTime_t endtime = 0;
 | 
						|
    _PyTime_t timeout_val;
 | 
						|
    PyObject *item;
 | 
						|
    PyLockStatus r;
 | 
						|
    PY_TIMEOUT_T microseconds;
 | 
						|
 | 
						|
    if (block == 0) {
 | 
						|
        /* Non-blocking */
 | 
						|
        microseconds = 0;
 | 
						|
    }
 | 
						|
    else if (timeout != Py_None) {
 | 
						|
        /* With timeout */
 | 
						|
        if (_PyTime_FromSecondsObject(&timeout_val,
 | 
						|
                                      timeout, _PyTime_ROUND_CEILING) < 0)
 | 
						|
            return NULL;
 | 
						|
        if (timeout_val < 0) {
 | 
						|
            PyErr_SetString(PyExc_ValueError,
 | 
						|
                            "'timeout' must be a non-negative number");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        microseconds = _PyTime_AsMicroseconds(timeout_val,
 | 
						|
                                              _PyTime_ROUND_CEILING);
 | 
						|
        if (microseconds >= PY_TIMEOUT_MAX) {
 | 
						|
            PyErr_SetString(PyExc_OverflowError,
 | 
						|
                            "timeout value is too large");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        endtime = _PyTime_GetMonotonicClock() + timeout_val;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        /* Infinitely blocking */
 | 
						|
        microseconds = -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* put() signals the queue to be non-empty by releasing the lock.
 | 
						|
     * So we simply try to acquire the lock in a loop, until the condition
 | 
						|
     * (queue non-empty) becomes true.
 | 
						|
     */
 | 
						|
    while (self->lst_pos == PyList_GET_SIZE(self->lst)) {
 | 
						|
        /* First a simple non-blocking try without releasing the GIL */
 | 
						|
        r = PyThread_acquire_lock_timed(self->lock, 0, 0);
 | 
						|
        if (r == PY_LOCK_FAILURE && microseconds != 0) {
 | 
						|
            Py_BEGIN_ALLOW_THREADS
 | 
						|
            r = PyThread_acquire_lock_timed(self->lock, microseconds, 1);
 | 
						|
            Py_END_ALLOW_THREADS
 | 
						|
        }
 | 
						|
        if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) {
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        if (r == PY_LOCK_FAILURE) {
 | 
						|
            /* Timed out */
 | 
						|
            PyErr_SetNone(EmptyError);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        self->locked = 1;
 | 
						|
        /* Adjust timeout for next iteration (if any) */
 | 
						|
        if (endtime > 0) {
 | 
						|
            timeout_val = endtime - _PyTime_GetMonotonicClock();
 | 
						|
            microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /* BEGIN GIL-protected critical section */
 | 
						|
    assert(self->lst_pos < PyList_GET_SIZE(self->lst));
 | 
						|
    item = simplequeue_pop_item(self);
 | 
						|
    if (self->locked) {
 | 
						|
        PyThread_release_lock(self->lock);
 | 
						|
        self->locked = 0;
 | 
						|
    }
 | 
						|
    /* END GIL-protected critical section */
 | 
						|
 | 
						|
    return item;
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
_queue.SimpleQueue.get_nowait
 | 
						|
 | 
						|
Remove and return an item from the queue without blocking.
 | 
						|
 | 
						|
Only get an item if one is immediately available. Otherwise
 | 
						|
raise the Empty exception.
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
_queue_SimpleQueue_get_nowait_impl(simplequeueobject *self)
 | 
						|
/*[clinic end generated code: output=a89731a75dbe4937 input=6fe5102db540a1b9]*/
 | 
						|
{
 | 
						|
    return _queue_SimpleQueue_get_impl(self, 0, Py_None);
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
_queue.SimpleQueue.empty -> bool
 | 
						|
 | 
						|
Return True if the queue is empty, False otherwise (not reliable!).
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static int
 | 
						|
_queue_SimpleQueue_empty_impl(simplequeueobject *self)
 | 
						|
/*[clinic end generated code: output=1a02a1b87c0ef838 input=1a98431c45fd66f9]*/
 | 
						|
{
 | 
						|
    return self->lst_pos == PyList_GET_SIZE(self->lst);
 | 
						|
}
 | 
						|
 | 
						|
/*[clinic input]
 | 
						|
_queue.SimpleQueue.qsize -> Py_ssize_t
 | 
						|
 | 
						|
Return the approximate size of the queue (not reliable!).
 | 
						|
[clinic start generated code]*/
 | 
						|
 | 
						|
static Py_ssize_t
 | 
						|
_queue_SimpleQueue_qsize_impl(simplequeueobject *self)
 | 
						|
/*[clinic end generated code: output=f9dcd9d0a90e121e input=7a74852b407868a1]*/
 | 
						|
{
 | 
						|
    return PyList_GET_SIZE(self->lst) - self->lst_pos;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#include "clinic/_queuemodule.c.h"
 | 
						|
 | 
						|
 | 
						|
static PyMethodDef simplequeue_methods[] = {
 | 
						|
    _QUEUE_SIMPLEQUEUE_EMPTY_METHODDEF
 | 
						|
    _QUEUE_SIMPLEQUEUE_GET_METHODDEF
 | 
						|
    _QUEUE_SIMPLEQUEUE_GET_NOWAIT_METHODDEF
 | 
						|
    _QUEUE_SIMPLEQUEUE_PUT_METHODDEF
 | 
						|
    _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF
 | 
						|
    _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF
 | 
						|
    {"__class_getitem__",    (PyCFunction)Py_GenericAlias,
 | 
						|
    METH_O|METH_CLASS,       PyDoc_STR("See PEP 585")},
 | 
						|
    {NULL,           NULL}              /* sentinel */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static PyTypeObject PySimpleQueueType = {
 | 
						|
    PyVarObject_HEAD_INIT(NULL, 0)
 | 
						|
    "_queue.SimpleQueue",               /*tp_name*/
 | 
						|
    sizeof(simplequeueobject),          /*tp_basicsize*/
 | 
						|
    0,                                  /*tp_itemsize*/
 | 
						|
    /* methods */
 | 
						|
    (destructor)simplequeue_dealloc,    /*tp_dealloc*/
 | 
						|
    0,                                  /*tp_vectorcall_offset*/
 | 
						|
    0,                                  /*tp_getattr*/
 | 
						|
    0,                                  /*tp_setattr*/
 | 
						|
    0,                                  /*tp_as_async*/
 | 
						|
    0,                                  /*tp_repr*/
 | 
						|
    0,                                  /*tp_as_number*/
 | 
						|
    0,                                  /*tp_as_sequence*/
 | 
						|
    0,                                  /*tp_as_mapping*/
 | 
						|
    0,                                  /*tp_hash*/
 | 
						|
    0,                                  /*tp_call*/
 | 
						|
    0,                                  /*tp_str*/
 | 
						|
    0,                                  /*tp_getattro*/
 | 
						|
    0,                                  /*tp_setattro*/
 | 
						|
    0,                                  /*tp_as_buffer*/
 | 
						|
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
 | 
						|
        | Py_TPFLAGS_HAVE_GC,           /* tp_flags */
 | 
						|
    simplequeue_new__doc__,             /*tp_doc*/
 | 
						|
    (traverseproc)simplequeue_traverse, /*tp_traverse*/
 | 
						|
    0,                                  /*tp_clear*/
 | 
						|
    0,                                  /*tp_richcompare*/
 | 
						|
    offsetof(simplequeueobject, weakreflist), /*tp_weaklistoffset*/
 | 
						|
    0,                                  /*tp_iter*/
 | 
						|
    0,                                  /*tp_iternext*/
 | 
						|
    simplequeue_methods,                /*tp_methods*/
 | 
						|
    0,                                  /* tp_members */
 | 
						|
    0,                                  /* tp_getset */
 | 
						|
    0,                                  /* tp_base */
 | 
						|
    0,                                  /* tp_dict */
 | 
						|
    0,                                  /* tp_descr_get */
 | 
						|
    0,                                  /* tp_descr_set */
 | 
						|
    0,                                  /* tp_dictoffset */
 | 
						|
    0,                                  /* tp_init */
 | 
						|
    0,                                  /* tp_alloc */
 | 
						|
    simplequeue_new                     /* tp_new */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/* Initialization function */
 | 
						|
 | 
						|
PyDoc_STRVAR(queue_module_doc,
 | 
						|
"C implementation of the Python queue module.\n\
 | 
						|
This module is an implementation detail, please do not use it directly.");
 | 
						|
 | 
						|
static struct PyModuleDef queuemodule = {
 | 
						|
    PyModuleDef_HEAD_INIT,
 | 
						|
    "_queue",
 | 
						|
    queue_module_doc,
 | 
						|
    -1,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
PyMODINIT_FUNC
 | 
						|
PyInit__queue(void)
 | 
						|
{
 | 
						|
    PyObject *m;
 | 
						|
 | 
						|
    /* Create the module */
 | 
						|
    m = PyModule_Create(&queuemodule);
 | 
						|
    if (m == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    EmptyError = PyErr_NewExceptionWithDoc(
 | 
						|
        "_queue.Empty",
 | 
						|
        "Exception raised by Queue.get(block=0)/get_nowait().",
 | 
						|
        NULL, NULL);
 | 
						|
    if (EmptyError == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    Py_INCREF(EmptyError);
 | 
						|
    if (PyModule_AddObject(m, "Empty", EmptyError) < 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if (PyModule_AddType(m, &PySimpleQueueType) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return m;
 | 
						|
}
 |