bpo-14976: Reentrant simple queue (#3346)
Add a queue.SimpleQueue class, an unbounded FIFO queue with a reentrant C implementation of put().
This commit is contained in:
parent
5ec0feeeec
commit
94e1696d04
|
@ -23,8 +23,14 @@ the first retrieved (operating like a stack). With a priority queue,
|
||||||
the entries are kept sorted (using the :mod:`heapq` module) and the
|
the entries are kept sorted (using the :mod:`heapq` module) and the
|
||||||
lowest valued entry is retrieved first.
|
lowest valued entry is retrieved first.
|
||||||
|
|
||||||
Internally, the module uses locks to temporarily block competing threads;
|
Internally, those three types of queues use locks to temporarily block
|
||||||
however, it is not designed to handle reentrancy within a thread.
|
competing threads; however, they are not designed to handle reentrancy
|
||||||
|
within a thread.
|
||||||
|
|
||||||
|
In addition, the module implements a "simple"
|
||||||
|
:abbr:`FIFO (first-in, first-out)` queue type where
|
||||||
|
specific implementations can provide additional guarantees
|
||||||
|
in exchange for the smaller functionality.
|
||||||
|
|
||||||
The :mod:`queue` module defines the following classes and exceptions:
|
The :mod:`queue` module defines the following classes and exceptions:
|
||||||
|
|
||||||
|
@ -67,6 +73,14 @@ The :mod:`queue` module defines the following classes and exceptions:
|
||||||
priority: int
|
priority: int
|
||||||
item: Any=field(compare=False)
|
item: Any=field(compare=False)
|
||||||
|
|
||||||
|
.. class:: SimpleQueue()
|
||||||
|
|
||||||
|
Constructor for an unbounded :abbr:`FIFO (first-in, first-out)` queue.
|
||||||
|
Simple queues lack advanced functionality such as task tracking.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
|
||||||
.. exception:: Empty
|
.. exception:: Empty
|
||||||
|
|
||||||
Exception raised when non-blocking :meth:`~Queue.get` (or
|
Exception raised when non-blocking :meth:`~Queue.get` (or
|
||||||
|
@ -201,6 +215,60 @@ Example of how to wait for enqueued tasks to be completed::
|
||||||
t.join()
|
t.join()
|
||||||
|
|
||||||
|
|
||||||
|
SimpleQueue Objects
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
:class:`SimpleQueue` objects provide the public methods described below.
|
||||||
|
|
||||||
|
.. method:: SimpleQueue.qsize()
|
||||||
|
|
||||||
|
Return the approximate size of the queue. Note, qsize() > 0 doesn't
|
||||||
|
guarantee that a subsequent get() will not block.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SimpleQueue.empty()
|
||||||
|
|
||||||
|
Return ``True`` if the queue is empty, ``False`` otherwise. If empty()
|
||||||
|
returns ``False`` it doesn't guarantee that a subsequent call to get()
|
||||||
|
will not block.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SimpleQueue.put(item, block=True, timeout=None)
|
||||||
|
|
||||||
|
Put *item* into the queue. The method never blocks and always succeeds
|
||||||
|
(except for potential low-level errors such as failure to allocate memory).
|
||||||
|
The optional args *block* and *timeout* are ignored and only provided
|
||||||
|
for compatibility with :meth:`Queue.put`.
|
||||||
|
|
||||||
|
.. impl-detail::
|
||||||
|
This method has a C implementation which is reentrant. That is, a
|
||||||
|
``put()`` or ``get()`` call can be interrupted by another ``put()``
|
||||||
|
call in the same thread without deadlocking or corrupting internal
|
||||||
|
state inside the queue. This makes it appropriate for use in
|
||||||
|
destructors such as ``__del__`` methods or :mod:`weakref` callbacks.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SimpleQueue.put_nowait(item)
|
||||||
|
|
||||||
|
Equivalent to ``put(item)``, provided for compatibility with
|
||||||
|
:meth:`Queue.put_nowait`.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SimpleQueue.get(block=True, timeout=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 positive number, it blocks at most *timeout* seconds and
|
||||||
|
raises the :exc:`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 :exc:`Empty` exception (*timeout* is ignored in that case).
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SimpleQueue.get_nowait()
|
||||||
|
|
||||||
|
Equivalent to ``get(False)``.
|
||||||
|
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
Class :class:`multiprocessing.Queue`
|
Class :class:`multiprocessing.Queue`
|
||||||
|
@ -210,4 +278,3 @@ Example of how to wait for enqueued tasks to be completed::
|
||||||
:class:`collections.deque` is an alternative implementation of unbounded
|
:class:`collections.deque` is an alternative implementation of unbounded
|
||||||
queues with fast atomic :meth:`~collections.deque.append` and
|
queues with fast atomic :meth:`~collections.deque.append` and
|
||||||
:meth:`~collections.deque.popleft` operations that do not require locking.
|
:meth:`~collections.deque.popleft` operations that do not require locking.
|
||||||
|
|
||||||
|
|
86
Lib/queue.py
86
Lib/queue.py
|
@ -4,17 +4,26 @@ import threading
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from heapq import heappush, heappop
|
from heapq import heappush, heappop
|
||||||
from time import monotonic as time
|
from time import monotonic as time
|
||||||
|
try:
|
||||||
|
from _queue import SimpleQueue
|
||||||
|
except ImportError:
|
||||||
|
SimpleQueue = None
|
||||||
|
|
||||||
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
|
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue']
|
||||||
|
|
||||||
class Empty(Exception):
|
|
||||||
'Exception raised by Queue.get(block=0)/get_nowait().'
|
try:
|
||||||
pass
|
from _queue import Empty
|
||||||
|
except AttributeError:
|
||||||
|
class Empty(Exception):
|
||||||
|
'Exception raised by Queue.get(block=0)/get_nowait().'
|
||||||
|
pass
|
||||||
|
|
||||||
class Full(Exception):
|
class Full(Exception):
|
||||||
'Exception raised by Queue.put(block=0)/put_nowait().'
|
'Exception raised by Queue.put(block=0)/put_nowait().'
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Queue:
|
class Queue:
|
||||||
'''Create a queue object with a given maximum size.
|
'''Create a queue object with a given maximum size.
|
||||||
|
|
||||||
|
@ -241,3 +250,72 @@ class LifoQueue(Queue):
|
||||||
|
|
||||||
def _get(self):
|
def _get(self):
|
||||||
return self.queue.pop()
|
return self.queue.pop()
|
||||||
|
|
||||||
|
|
||||||
|
class _PySimpleQueue:
|
||||||
|
'''Simple, unbounded FIFO queue.
|
||||||
|
|
||||||
|
This pure Python implementation is not reentrant.
|
||||||
|
'''
|
||||||
|
# Note: while this pure Python version provides fairness
|
||||||
|
# (by using a threading.Semaphore which is itself fair, being based
|
||||||
|
# on threading.Condition), fairness is not part of the API contract.
|
||||||
|
# This allows the C version to use a different implementation.
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._queue = deque()
|
||||||
|
self._count = threading.Semaphore(0)
|
||||||
|
|
||||||
|
def put(self, item, block=True, timeout=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.
|
||||||
|
'''
|
||||||
|
self._queue.append(item)
|
||||||
|
self._count.release()
|
||||||
|
|
||||||
|
def get(self, block=True, timeout=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).
|
||||||
|
'''
|
||||||
|
if timeout is not None and timeout < 0:
|
||||||
|
raise ValueError("'timeout' must be a non-negative number")
|
||||||
|
if not self._count.acquire(block, timeout):
|
||||||
|
raise Empty
|
||||||
|
return self._queue.popleft()
|
||||||
|
|
||||||
|
def put_nowait(self, item):
|
||||||
|
'''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.
|
||||||
|
'''
|
||||||
|
return self.put(item, block=False)
|
||||||
|
|
||||||
|
def get_nowait(self):
|
||||||
|
'''Remove and return an item from the queue without blocking.
|
||||||
|
|
||||||
|
Only get an item if one is immediately available. Otherwise
|
||||||
|
raise the Empty exception.
|
||||||
|
'''
|
||||||
|
return self.get(block=False)
|
||||||
|
|
||||||
|
def empty(self):
|
||||||
|
'''Return True if the queue is empty, False otherwise (not reliable!).'''
|
||||||
|
return len(self._queue) == 0
|
||||||
|
|
||||||
|
def qsize(self):
|
||||||
|
'''Return the approximate size of the queue (not reliable!).'''
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
|
||||||
|
if SimpleQueue is None:
|
||||||
|
SimpleQueue = _PySimpleQueue
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
# Some simple queue module tests, plus some failure conditions
|
# Some simple queue module tests, plus some failure conditions
|
||||||
# to ensure the Queue locks remain stable.
|
# to ensure the Queue locks remain stable.
|
||||||
|
import collections
|
||||||
|
import itertools
|
||||||
import queue
|
import queue
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
import weakref
|
||||||
from test import support
|
from test import support
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _queue
|
||||||
|
except ImportError:
|
||||||
|
_queue = None
|
||||||
|
|
||||||
QUEUE_SIZE = 5
|
QUEUE_SIZE = 5
|
||||||
|
|
||||||
def qfull(q):
|
def qfull(q):
|
||||||
|
@ -84,7 +94,7 @@ class BaseQueueTestMixin(BlockingTestMixin):
|
||||||
self.cum = 0
|
self.cum = 0
|
||||||
self.cumlock = threading.Lock()
|
self.cumlock = threading.Lock()
|
||||||
|
|
||||||
def simple_queue_test(self, q):
|
def basic_queue_test(self, q):
|
||||||
if q.qsize():
|
if q.qsize():
|
||||||
raise RuntimeError("Call this function with an empty queue")
|
raise RuntimeError("Call this function with an empty queue")
|
||||||
self.assertTrue(q.empty())
|
self.assertTrue(q.empty())
|
||||||
|
@ -192,12 +202,12 @@ class BaseQueueTestMixin(BlockingTestMixin):
|
||||||
else:
|
else:
|
||||||
self.fail("Did not detect task count going negative")
|
self.fail("Did not detect task count going negative")
|
||||||
|
|
||||||
def test_simple_queue(self):
|
def test_basic(self):
|
||||||
# Do it a couple of times on the same queue.
|
# Do it a couple of times on the same queue.
|
||||||
# Done twice to make sure works with same instance reused.
|
# Done twice to make sure works with same instance reused.
|
||||||
q = self.type2test(QUEUE_SIZE)
|
q = self.type2test(QUEUE_SIZE)
|
||||||
self.simple_queue_test(q)
|
self.basic_queue_test(q)
|
||||||
self.simple_queue_test(q)
|
self.basic_queue_test(q)
|
||||||
|
|
||||||
def test_negative_timeout_raises_exception(self):
|
def test_negative_timeout_raises_exception(self):
|
||||||
q = self.type2test(QUEUE_SIZE)
|
q = self.type2test(QUEUE_SIZE)
|
||||||
|
@ -353,5 +363,227 @@ class FailingQueueTest(BlockingTestMixin, unittest.TestCase):
|
||||||
self.failing_queue_test(q)
|
self.failing_queue_test(q)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSimpleQueueTest:
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.q = self.type2test()
|
||||||
|
|
||||||
|
def feed(self, q, seq, rnd):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
val = seq.pop()
|
||||||
|
except IndexError:
|
||||||
|
return
|
||||||
|
q.put(val)
|
||||||
|
if rnd.random() > 0.5:
|
||||||
|
time.sleep(rnd.random() * 1e-3)
|
||||||
|
|
||||||
|
def consume(self, q, results, sentinel):
|
||||||
|
while True:
|
||||||
|
val = q.get()
|
||||||
|
if val == sentinel:
|
||||||
|
return
|
||||||
|
results.append(val)
|
||||||
|
|
||||||
|
def consume_nonblock(self, q, results, sentinel):
|
||||||
|
while True:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
val = q.get(block=False)
|
||||||
|
except queue.Empty:
|
||||||
|
time.sleep(1e-5)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if val == sentinel:
|
||||||
|
return
|
||||||
|
results.append(val)
|
||||||
|
|
||||||
|
def consume_timeout(self, q, results, sentinel):
|
||||||
|
while True:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
val = q.get(timeout=1e-5)
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if val == sentinel:
|
||||||
|
return
|
||||||
|
results.append(val)
|
||||||
|
|
||||||
|
def run_threads(self, n_feeders, n_consumers, q, inputs,
|
||||||
|
feed_func, consume_func):
|
||||||
|
results = []
|
||||||
|
sentinel = None
|
||||||
|
seq = inputs + [sentinel] * n_consumers
|
||||||
|
seq.reverse()
|
||||||
|
rnd = random.Random(42)
|
||||||
|
|
||||||
|
exceptions = []
|
||||||
|
def log_exceptions(f):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
f(*args, **kwargs)
|
||||||
|
except BaseException as e:
|
||||||
|
exceptions.append(e)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
feeders = [threading.Thread(target=log_exceptions(feed_func),
|
||||||
|
args=(q, seq, rnd))
|
||||||
|
for i in range(n_feeders)]
|
||||||
|
consumers = [threading.Thread(target=log_exceptions(consume_func),
|
||||||
|
args=(q, results, sentinel))
|
||||||
|
for i in range(n_consumers)]
|
||||||
|
|
||||||
|
with support.start_threads(feeders + consumers):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertFalse(exceptions)
|
||||||
|
self.assertTrue(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 0)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
# Basic tests for get(), put() etc.
|
||||||
|
q = self.q
|
||||||
|
self.assertTrue(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 0)
|
||||||
|
q.put(1)
|
||||||
|
self.assertFalse(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 1)
|
||||||
|
q.put(2)
|
||||||
|
q.put_nowait(3)
|
||||||
|
q.put(4)
|
||||||
|
self.assertFalse(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 4)
|
||||||
|
|
||||||
|
self.assertEqual(q.get(), 1)
|
||||||
|
self.assertEqual(q.qsize(), 3)
|
||||||
|
|
||||||
|
self.assertEqual(q.get_nowait(), 2)
|
||||||
|
self.assertEqual(q.qsize(), 2)
|
||||||
|
|
||||||
|
self.assertEqual(q.get(block=False), 3)
|
||||||
|
self.assertFalse(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 1)
|
||||||
|
|
||||||
|
self.assertEqual(q.get(timeout=0.1), 4)
|
||||||
|
self.assertTrue(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 0)
|
||||||
|
|
||||||
|
with self.assertRaises(queue.Empty):
|
||||||
|
q.get(block=False)
|
||||||
|
with self.assertRaises(queue.Empty):
|
||||||
|
q.get(timeout=1e-3)
|
||||||
|
with self.assertRaises(queue.Empty):
|
||||||
|
q.get_nowait()
|
||||||
|
self.assertTrue(q.empty())
|
||||||
|
self.assertEqual(q.qsize(), 0)
|
||||||
|
|
||||||
|
def test_negative_timeout_raises_exception(self):
|
||||||
|
q = self.q
|
||||||
|
q.put(1)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
q.get(timeout=-1)
|
||||||
|
|
||||||
|
def test_order(self):
|
||||||
|
# Test a pair of concurrent put() and get()
|
||||||
|
q = self.q
|
||||||
|
inputs = list(range(100))
|
||||||
|
results = self.run_threads(1, 1, q, inputs, self.feed, self.consume)
|
||||||
|
|
||||||
|
# One producer, one consumer => results appended in well-defined order
|
||||||
|
self.assertEqual(results, inputs)
|
||||||
|
|
||||||
|
def test_many_threads(self):
|
||||||
|
# Test multiple concurrent put() and get()
|
||||||
|
N = 50
|
||||||
|
q = self.q
|
||||||
|
inputs = list(range(10000))
|
||||||
|
results = self.run_threads(N, N, q, inputs, self.feed, self.consume)
|
||||||
|
|
||||||
|
# Multiple consumers without synchronization append the
|
||||||
|
# results in random order
|
||||||
|
self.assertEqual(sorted(results), inputs)
|
||||||
|
|
||||||
|
def test_many_threads_nonblock(self):
|
||||||
|
# Test multiple concurrent put() and get(block=False)
|
||||||
|
N = 50
|
||||||
|
q = self.q
|
||||||
|
inputs = list(range(10000))
|
||||||
|
results = self.run_threads(N, N, q, inputs,
|
||||||
|
self.feed, self.consume_nonblock)
|
||||||
|
|
||||||
|
self.assertEqual(sorted(results), inputs)
|
||||||
|
|
||||||
|
def test_many_threads_timeout(self):
|
||||||
|
# Test multiple concurrent put() and get(timeout=...)
|
||||||
|
N = 50
|
||||||
|
q = self.q
|
||||||
|
inputs = list(range(1000))
|
||||||
|
results = self.run_threads(N, N, q, inputs,
|
||||||
|
self.feed, self.consume_timeout)
|
||||||
|
|
||||||
|
self.assertEqual(sorted(results), inputs)
|
||||||
|
|
||||||
|
def test_references(self):
|
||||||
|
# The queue should lose references to each item as soon as
|
||||||
|
# it leaves the queue.
|
||||||
|
class C:
|
||||||
|
pass
|
||||||
|
|
||||||
|
N = 20
|
||||||
|
q = self.q
|
||||||
|
for i in range(N):
|
||||||
|
q.put(C())
|
||||||
|
for i in range(N):
|
||||||
|
wr = weakref.ref(q.get())
|
||||||
|
self.assertIsNone(wr())
|
||||||
|
|
||||||
|
|
||||||
|
class PySimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase):
|
||||||
|
type2test = queue._PySimpleQueue
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(_queue is None, "No _queue module found")
|
||||||
|
class CSimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.type2test = _queue.SimpleQueue
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def test_is_default(self):
|
||||||
|
self.assertIs(self.type2test, queue.SimpleQueue)
|
||||||
|
|
||||||
|
def test_reentrancy(self):
|
||||||
|
# bpo-14976: put() may be called reentrantly in an asynchronous
|
||||||
|
# callback.
|
||||||
|
q = self.q
|
||||||
|
gen = itertools.count()
|
||||||
|
N = 10000
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# This test exploits the fact that __del__ in a reference cycle
|
||||||
|
# can be called any time the GC may run.
|
||||||
|
|
||||||
|
class Circular(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.circular = self
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
q.put(next(gen))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
o = Circular()
|
||||||
|
q.put(next(gen))
|
||||||
|
del o
|
||||||
|
results.append(q.get())
|
||||||
|
if results[-1] >= N:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertEqual(results, list(range(N + 1)))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add a queue.SimpleQueue class, an unbounded FIFO queue with a reentrant C
|
||||||
|
implementation of put().
|
|
@ -0,0 +1,400 @@
|
||||||
|
#include "Python.h"
|
||||||
|
#include "structmember.h" /* offsetof */
|
||||||
|
#include "pythread.h"
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
module _queue
|
||||||
|
class _queue.SimpleQueue "simplequeueobject *" "&PySimpleQueueType"
|
||||||
|
[clinic start generated code]*/
|
||||||
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cf49af81bcbbbea6]*/
|
||||||
|
|
||||||
|
extern 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
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PyTypeObject PySimpleQueueType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"_queue.SimpleQueue", /*tp_name*/
|
||||||
|
sizeof(simplequeueobject), /*tp_size*/
|
||||||
|
0, /*tp_itemsize*/
|
||||||
|
/* methods */
|
||||||
|
(destructor)simplequeue_dealloc, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_reserved*/
|
||||||
|
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 (PyType_Ready(&PySimpleQueueType) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_INCREF(&PySimpleQueueType);
|
||||||
|
if (PyModule_AddObject(m, "SimpleQueue", (PyObject *)&PySimpleQueueType) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
/*[clinic input]
|
||||||
|
preserve
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
PyDoc_STRVAR(simplequeue_new__doc__,
|
||||||
|
"SimpleQueue()\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Simple, unbounded, reentrant FIFO queue.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
simplequeue_new_impl(PyTypeObject *type);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
simplequeue_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
|
||||||
|
if ((type == &PySimpleQueueType) &&
|
||||||
|
!_PyArg_NoPositional("SimpleQueue", args)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if ((type == &PySimpleQueueType) &&
|
||||||
|
!_PyArg_NoKeywords("SimpleQueue", kwargs)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = simplequeue_new_impl(type);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_queue_SimpleQueue_put__doc__,
|
||||||
|
"put($self, /, item, block=True, timeout=None)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Put the item on the queue.\n"
|
||||||
|
"\n"
|
||||||
|
"The optional \'block\' and \'timeout\' arguments are ignored, as this method\n"
|
||||||
|
"never blocks. They are provided for compatibility with the Queue class.");
|
||||||
|
|
||||||
|
#define _QUEUE_SIMPLEQUEUE_PUT_METHODDEF \
|
||||||
|
{"put", (PyCFunction)_queue_SimpleQueue_put, METH_FASTCALL|METH_KEYWORDS, _queue_SimpleQueue_put__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
|
||||||
|
int block, PyObject *timeout);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_put(simplequeueobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
static const char * const _keywords[] = {"item", "block", "timeout", NULL};
|
||||||
|
static _PyArg_Parser _parser = {"O|pO:put", _keywords, 0};
|
||||||
|
PyObject *item;
|
||||||
|
int block = 1;
|
||||||
|
PyObject *timeout = Py_None;
|
||||||
|
|
||||||
|
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
|
||||||
|
&item, &block, &timeout)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = _queue_SimpleQueue_put_impl(self, item, block, timeout);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_queue_SimpleQueue_put_nowait__doc__,
|
||||||
|
"put_nowait($self, /, item)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Put an item into the queue without blocking.\n"
|
||||||
|
"\n"
|
||||||
|
"This is exactly equivalent to `put(item)` and is only provided\n"
|
||||||
|
"for compatibility with the Queue class.");
|
||||||
|
|
||||||
|
#define _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF \
|
||||||
|
{"put_nowait", (PyCFunction)_queue_SimpleQueue_put_nowait, METH_FASTCALL|METH_KEYWORDS, _queue_SimpleQueue_put_nowait__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_put_nowait_impl(simplequeueobject *self, PyObject *item);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_put_nowait(simplequeueobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
static const char * const _keywords[] = {"item", NULL};
|
||||||
|
static _PyArg_Parser _parser = {"O:put_nowait", _keywords, 0};
|
||||||
|
PyObject *item;
|
||||||
|
|
||||||
|
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
|
||||||
|
&item)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = _queue_SimpleQueue_put_nowait_impl(self, item);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_queue_SimpleQueue_get__doc__,
|
||||||
|
"get($self, /, block=True, timeout=None)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Remove and return an item from the queue.\n"
|
||||||
|
"\n"
|
||||||
|
"If optional args \'block\' is true and \'timeout\' is None (the default),\n"
|
||||||
|
"block if necessary until an item is available. If \'timeout\' is\n"
|
||||||
|
"a non-negative number, it blocks at most \'timeout\' seconds and raises\n"
|
||||||
|
"the Empty exception if no item was available within that time.\n"
|
||||||
|
"Otherwise (\'block\' is false), return an item if one is immediately\n"
|
||||||
|
"available, else raise the Empty exception (\'timeout\' is ignored\n"
|
||||||
|
"in that case).");
|
||||||
|
|
||||||
|
#define _QUEUE_SIMPLEQUEUE_GET_METHODDEF \
|
||||||
|
{"get", (PyCFunction)_queue_SimpleQueue_get, METH_FASTCALL|METH_KEYWORDS, _queue_SimpleQueue_get__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_get_impl(simplequeueobject *self, int block,
|
||||||
|
PyObject *timeout);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_get(simplequeueobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
static const char * const _keywords[] = {"block", "timeout", NULL};
|
||||||
|
static _PyArg_Parser _parser = {"|pO:get", _keywords, 0};
|
||||||
|
int block = 1;
|
||||||
|
PyObject *timeout = Py_None;
|
||||||
|
|
||||||
|
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
|
||||||
|
&block, &timeout)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = _queue_SimpleQueue_get_impl(self, block, timeout);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_queue_SimpleQueue_get_nowait__doc__,
|
||||||
|
"get_nowait($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Remove and return an item from the queue without blocking.\n"
|
||||||
|
"\n"
|
||||||
|
"Only get an item if one is immediately available. Otherwise\n"
|
||||||
|
"raise the Empty exception.");
|
||||||
|
|
||||||
|
#define _QUEUE_SIMPLEQUEUE_GET_NOWAIT_METHODDEF \
|
||||||
|
{"get_nowait", (PyCFunction)_queue_SimpleQueue_get_nowait, METH_NOARGS, _queue_SimpleQueue_get_nowait__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_get_nowait_impl(simplequeueobject *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_get_nowait(simplequeueobject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
return _queue_SimpleQueue_get_nowait_impl(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_queue_SimpleQueue_empty__doc__,
|
||||||
|
"empty($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Return True if the queue is empty, False otherwise (not reliable!).");
|
||||||
|
|
||||||
|
#define _QUEUE_SIMPLEQUEUE_EMPTY_METHODDEF \
|
||||||
|
{"empty", (PyCFunction)_queue_SimpleQueue_empty, METH_NOARGS, _queue_SimpleQueue_empty__doc__},
|
||||||
|
|
||||||
|
static int
|
||||||
|
_queue_SimpleQueue_empty_impl(simplequeueobject *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_empty(simplequeueobject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
int _return_value;
|
||||||
|
|
||||||
|
_return_value = _queue_SimpleQueue_empty_impl(self);
|
||||||
|
if ((_return_value == -1) && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = PyBool_FromLong((long)_return_value);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_queue_SimpleQueue_qsize__doc__,
|
||||||
|
"qsize($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Return the approximate size of the queue (not reliable!).");
|
||||||
|
|
||||||
|
#define _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF \
|
||||||
|
{"qsize", (PyCFunction)_queue_SimpleQueue_qsize, METH_NOARGS, _queue_SimpleQueue_qsize__doc__},
|
||||||
|
|
||||||
|
static Py_ssize_t
|
||||||
|
_queue_SimpleQueue_qsize_impl(simplequeueobject *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_queue_SimpleQueue_qsize(simplequeueobject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
Py_ssize_t _return_value;
|
||||||
|
|
||||||
|
_return_value = _queue_SimpleQueue_qsize_impl(self);
|
||||||
|
if ((_return_value == -1) && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = PyLong_FromSsize_t(_return_value);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
/*[clinic end generated code: output=8badc3bb85263689 input=a9049054013a1b77]*/
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="PGInstrument|Win32">
|
||||||
|
<Configuration>PGInstrument</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="PGInstrument|x64">
|
||||||
|
<Configuration>PGInstrument</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="PGUpdate|Win32">
|
||||||
|
<Configuration>PGUpdate</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="PGUpdate|x64">
|
||||||
|
<Configuration>PGUpdate</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}</ProjectGuid>
|
||||||
|
<RootNamespace>_queue</RootNamespace>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="python.props" />
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetExt>.pyd</TargetExt>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="pyproject.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\Modules\_queuemodule.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="..\PC\python_nt.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="pythoncore.vcxproj">
|
||||||
|
<Project>{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}</Project>
|
||||||
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="..\PC\python_nt.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{c56a5dd3-7838-48e9-a781-855d8be7370f}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\Modules\_queuemodule.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -49,7 +49,7 @@
|
||||||
<!-- pyshellext.dll -->
|
<!-- pyshellext.dll -->
|
||||||
<Projects Include="pyshellext.vcxproj" />
|
<Projects Include="pyshellext.vcxproj" />
|
||||||
<!-- Extension modules -->
|
<!-- Extension modules -->
|
||||||
<ExtensionModules Include="_asyncio;_ctypes;_decimal;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;select;unicodedata;winsound" />
|
<ExtensionModules Include="_asyncio;_ctypes;_decimal;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" />
|
||||||
<!-- Extension modules that require external sources -->
|
<!-- Extension modules that require external sources -->
|
||||||
<ExternalModules Include="_bz2;_lzma;_sqlite3" />
|
<ExternalModules Include="_bz2;_lzma;_sqlite3" />
|
||||||
<!-- _ssl will build _socket as well, which may cause conflicts in parallel builds -->
|
<!-- _ssl will build _socket as well, which may cause conflicts in parallel builds -->
|
||||||
|
|
|
@ -89,6 +89,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_testconsole", "_testconsol
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_asyncio", "_asyncio.vcxproj", "{384C224A-7474-476E-A01B-750EA7DE918C}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_asyncio", "_asyncio.vcxproj", "{384C224A-7474-476E-A01B-750EA7DE918C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", "{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}"
|
||||||
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
|
@ -659,6 +661,22 @@ Global
|
||||||
{384C224A-7474-476E-A01B-750EA7DE918C}.Release|Win32.Build.0 = Release|Win32
|
{384C224A-7474-476E-A01B-750EA7DE918C}.Release|Win32.Build.0 = Release|Win32
|
||||||
{384C224A-7474-476E-A01B-750EA7DE918C}.Release|x64.ActiveCfg = Release|x64
|
{384C224A-7474-476E-A01B-750EA7DE918C}.Release|x64.ActiveCfg = Release|x64
|
||||||
{384C224A-7474-476E-A01B-750EA7DE918C}.Release|x64.Build.0 = Release|x64
|
{384C224A-7474-476E-A01B-750EA7DE918C}.Release|x64.Build.0 = Release|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGInstrument|Win32.Build.0 = PGInstrument|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGInstrument|x64.ActiveCfg = PGInstrument|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGInstrument|x64.Build.0 = PGInstrument|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGUpdate|Win32.Build.0 = PGUpdate|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.PGUpdate|x64.Build.0 = PGUpdate|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}.Release|x64.Build.0 = Release|x64
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|Win32.ActiveCfg = Debug|Win32
|
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|Win32.Build.0 = Debug|Win32
|
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|x64.ActiveCfg = Debug|x64
|
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
|
|
@ -506,6 +506,9 @@
|
||||||
<ClCompile Include="..\Modules\_pickle.c">
|
<ClCompile Include="..\Modules\_pickle.c">
|
||||||
<Filter>Modules</Filter>
|
<Filter>Modules</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Modules\_queuemodule.c">
|
||||||
|
<Filter>Modules</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Modules\_randommodule.c">
|
<ClCompile Include="..\Modules\_randommodule.c">
|
||||||
<Filter>Modules</Filter>
|
<Filter>Modules</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -699,6 +699,8 @@ class PyBuildExt(build_ext):
|
||||||
exts.append( Extension('_opcode', ['_opcode.c']) )
|
exts.append( Extension('_opcode', ['_opcode.c']) )
|
||||||
# asyncio speedups
|
# asyncio speedups
|
||||||
exts.append( Extension("_asyncio", ["_asynciomodule.c"]) )
|
exts.append( Extension("_asyncio", ["_asynciomodule.c"]) )
|
||||||
|
# _queue module
|
||||||
|
exts.append( Extension("_queue", ["_queuemodule.c"]) )
|
||||||
|
|
||||||
# Modules with some UNIX dependencies -- on by default:
|
# Modules with some UNIX dependencies -- on by default:
|
||||||
# (If you have a really backward UNIX, select and socket may not be
|
# (If you have a really backward UNIX, select and socket may not be
|
||||||
|
|
Loading…
Reference in New Issue