コード例 #1
0
ファイル: dispatch.py プロジェクト: ubolonton/twisted-csp
class TwistedDispatcher:

    TASK_BATCH_SIZE = 1024

    def __init__(self):
        self.tasks = RingBuffer(32)
        self.running = False
        self.queued = False

    def run(self, f):
        # TODO: Should there be some sort of limit?
        self.tasks.unbounded_unshift(f)
        self.queue()

    def queue_delay(self, f, seconds):
        reactor.callLater(seconds, f)

    def process_messages(self):
        self.running = True
        self.queued = False
        count = 0
        while True:
            try:
                task = self.tasks.pop()
            except IndexError:
                break

            task()

            # TODO: Maybe stop after a max amount of time rather than
            # a max number of tasks.
            count += 1
            if count >= self.TASK_BATCH_SIZE:
                break

        self.running = False
        if len(self.tasks) > 0:
            self.queue()

    def queue(self):
        if self.queued and self.running:
            return

        self.queued = True
        reactor.callLater(0, self.process_messages)
コード例 #2
0
ファイル: channels.py プロジェクト: ubolonton/twisted-csp
class ManyToManyChannel:
    """Creates a channel with an optional buffer. If buf_or_n is a number,
    a fixed buffer of that size is created and used.
    """

    implements(IChannel)

    def __init__(self, buf_or_n = None):
        if buf_or_n == 0:
            buf_or_n = None
        if isinstance(buf_or_n, int):
            self.buf = FixedBuffer(buf_or_n)
        else:
            self.buf = buf_or_n

        self.closed = False
        self.takes = RingBuffer(32)
        self.puts = RingBuffer(32)
        # Counters towards next "garbage collections"
        self.dirty_takes = 0
        self.dirty_puts = 0

    def is_closed(self):
        return self.closed

    def put(self, value, handler):
        if value == CLOSED:
            raise Exception("Cannot put csp.CLOSED on a channel.")

        if self.closed or not handler.is_active():
            return Box(not self.closed)

        while True:
            try:
                taker = self.takes.pop()
            except IndexError:
                taker = None
            # There's a pending take...
            if taker is not None:
                # ... that is still interested
                if taker.is_active():
                    # Done, shake hand, here it is
                    callback, _ = taker.commit(), handler.commit()
                    # FIX
                    dispatch.run(lambda: callback(value))
                    return Box(True)
                else:
                    continue
            # No pending takes
            else:
                # Throw the value on the queue and be done with it
                if self.buf is not None and not self.buf.is_full():
                    handler.commit()
                    self.buf.add(value)
                    return Box(True)
                # No more room for waiting
                else:
                    # Periodically remove stale puts
                    if self.dirty_puts > MAX_DIRTY:
                        self.puts.cleanup(keep = lambda putter: putter.handler.is_active())
                        self.dirty_puts = 0
                    else:
                        self.dirty_puts += 1

                    # TODO: Do a last-chance "garbage collection" to
                    # be sure?
                    if len(self.puts) >= MAX_QUEUE_SIZE:
                        raise Exception("No more than %d pending puts are allowed on a single channel." % MAX_QUEUE_SIZE)

                    # Queue this put
                    self.puts.unbounded_unshift(PutBox(handler, value))
            break

    def take(self, handler):
        if not handler.is_active():
            return

        # Waiting values go first
        if self.buf is not None and len(self.buf) > 0:
            handler.commit()
            # We need to check pending puts here, otherwise they won't
            # be able to proceed until their number reaches MAX_DIRTY
            value = self.buf.remove()
            while True:
                try:
                    putter = self.puts.pop()
                except IndexError:
                    break
                else:
                    put_handler = putter.handler
                    if put_handler.is_active():
                        callback = put_handler.commit()
                        dispatch.run(lambda: callback(True))
                        self.buf.add(putter.value)
                        break
                    else:
                        continue
            return Box(value)

        while True:
            try:
                putter = self.puts.pop()
            except IndexError:
                putter = None
            # There's a pending put...
            if putter is not None:
                put_handler = putter.handler
                # ... that is still interested
                if put_handler.is_active():
                    # Done, shake hand, take it
                    callback, _ = put_handler.commit(), handler.commit()
                    dispatch.run(lambda: callback(True))
                    return Box(putter.value)
                else:
                    continue
            # No pending puts
            else:
                if self.closed:
                    handler.commit()
                    return Box(CLOSED)
                else:
                    # Periodically remove stale takes
                    if self.dirty_takes > MAX_DIRTY:
                        self.takes.cleanup(keep = lambda handler: handler.is_active())
                        self.dirty_takes = 0
                    else:
                        self.dirty_takes += 1

                    # TODO: Do a last-chance "garbage collection" to
                    # be sure?
                    if len(self.takes) >= MAX_QUEUE_SIZE:
                        raise Exception("No more than %d pending takes are allowed on a single channel." % MAX_QUEUE_SIZE)

                    # Queue this take
                    self.takes.unbounded_unshift(handler)
            break

    def close(self):
        if self.closed:
            return

        self.closed = True

        # Pending takes get "channel closed"
        while True:
            try:
                taker = self.takes.pop()
            except IndexError:
                break
            if taker.is_active():
                callback = taker.commit()
                dispatch.run(lambda: callback(CLOSED))

        # Pending puts get "channel closed"
        while True:
            try:
                putter = self.puts.pop()
            except IndexError:
                break
            handler = putter.handler
            if handler.is_active():
                callback = handler.commit()
                dispatch.run(lambda: callback(False))