"""A multi-producer, multi-consumer stack."""

class Empty(Exception):
    "Exception raised by Stack.get(block=0)/get_nowait()."
    pass

class Full(Exception):
    "Exception raised by Stack.put(block=0)/put_nowait()."
    pass

class Stack:
    def __init__(self, maxsize=0):
        """Initialize a stack object with a given maximum size.

        If maxsize is <= 0, the stack size is infinite.
        """
        import thread
        self.maxsize = maxsize
        self.stack = []
        self.mutex = thread.allocate_lock()
        self.esema = thread.allocate_lock()
        self.esema.acquire()
        self.fsema = thread.allocate_lock()

        ##shortcut name-bindings
        self.mutexAcquire = self.mutex.acquire
        self.mutexRelease = self.mutex.release
        self.fsemaAcquire = self.fsema.acquire
        self.fsemaRelease = self.fsema.release
        self.esemaAcquire = self.esema.acquire 
        self.esemaRelease = self.esema.release

        self._put = self.stack.append
        
    def qsize(self):
        """Return the approximate size of the stack (not reliable!)."""
        self.mutexAcquire()
        n = len(self.stack)
        self.mutexRelease()
        return n

    def empty(self):
        """Return 1 if the stack is empty, 0 otherwise (not reliable!)."""
        self.mutexAcquire()
        n = not self.stack
        self.mutexRelease()
        return n

    def full(self):
        """Return 1 if the stack is full, 0 otherwise (not reliable!)."""
        self.mutexAcquire()
        n = ( self.maxsize > 0 and len(self.stack) == self.maxsize )
        self.mutexRelease()
        return n

    def put(self, item, block=1):
        """Put an item into the stack.

        If optional arg 'block' is 1 (the default), block if
        necessary until a free slot is available.  Otherwise (block
        is 0), put an item on the stack if a free slot is immediately
        available, else raise the Full exception.
        """
        theStack = self.stack

        if block:
            self.fsemaAcquire()
        elif not self.fsemaAcquire(0):
            raise Full
        self.mutexAcquire()
        was_empty = not theStack # ~= self._empty()
        self._put(item)
        if was_empty:
            self.esemaRelease()
        if not (self.maxsize > 0 and len(theStack) == self.maxsize): # ~= not 
self._full():
            self.fsemaRelease()
        self.mutexRelease()

    def put_nowait(self, item):
        """Put an item into the stack without blocking.

        Only enqueue the item if a free slot is immediately available.
        Otherwise raise the Full exception.
        """
        return self.put(item, 0)

    def get(self, block=1):
        """Remove and return an item from the stack.

        If optional arg 'block' is 1 (the default), block if
        necessary until an item is available.  Otherwise (block is 0),
        return an item if one is immediately available, else raise the
        Empty exception.
        """
        theStack = self.stack
        
        if block:
            self.esemaAcquire()
        elif not self.esemaAcquire(0):
            raise Empty
        self.mutexAcquire()
        was_full = (self.maxsize > 0 and len(theStack) == self.maxsize) # ~= 
self._full()

        item = theStack.pop()
        
        if was_full:
            self.fsemaRelease()
        if theStack: # ~= not self._empty():
            self.esemaRelease()
        self.mutexRelease()
        return item

    def get_nowait(self):
        """Remove and return an item from the stack without blocking.

        Only get an item if one is immediately available.  Otherwise
        raise the Empty exception.
        """
        return self.get(0)

Reply via email to