def __init__(self, thread_factory=None, exit_if_empty=False): super(EventLoopScheduler, self).__init__() def default_factory(target): return threading.Thread(target=target) self.thread_factory = thread_factory or default_factory self.thread = None self.timer = None self.condition = threading.Condition(self.lock) self.queue = PriorityQueue() self.ready_list = [] self.next_item = None self.exit_if_empty = exit_if_empty
def __init__(self, thread_factory=None, exit_if_empty=False): super(EventLoopScheduler, self).__init__() self.is_disposed = False def default_factory(target): t = threading.Thread(target=target) t.setDaemon(True) return t self.lock = config["concurrency"].RLock() self.thread_factory = thread_factory or default_factory self.thread = None self.timer = None self.condition = config["concurrency"].Condition(self.lock) self.queue = PriorityQueue() self.ready_list = [] self.next_item = None self.exit_if_empty = exit_if_empty
def __init__(self, thread_factory=None, exit_if_empty=False) -> None: super(EventLoopScheduler, self).__init__() self.is_disposed = False def default_factory(target): t = threading.Thread(target=target) t.setDaemon(True) return t self.lock = threading.RLock() self.thread_factory = thread_factory or default_factory self.thread: Optional[threading.Thread] = None self.timer: Optional[threading.Timer] = None self.condition = threading.Condition(self.lock) self.queue = PriorityQueue() self.ready_list: List[ScheduledItem] = [] self.next_item = None self.exit_if_empty = exit_if_empty
def __init__(self, thread_factory: Optional[typing.StartableFactory] = None, exit_if_empty: bool = False) -> None: super().__init__() self._is_disposed = False self._thread_factory: typing.StartableFactory = thread_factory or default_thread_factory self._thread: Optional[typing.Startable] = None self._condition = threading.Condition(threading.Lock()) self._queue: PriorityQueue[ScheduledItem] = PriorityQueue() self._ready_list: Deque[ScheduledItem] = deque() self._exit_if_empty = exit_if_empty
class EventLoopScheduler(Scheduler, Disposable): """Creates an object that schedules units of work on a designated thread.""" def __init__(self, thread_factory=None, exit_if_empty=False): super(EventLoopScheduler, self).__init__() def default_factory(target): return threading.Thread(target=target) self.thread_factory = thread_factory or default_factory self.thread = None self.timer = None self.condition = threading.Condition(self.lock) self.queue = PriorityQueue() self.ready_list = [] self.next_item = None self.exit_if_empty = exit_if_empty def schedule(self, action, state=None): """Schedules an action to be executed.""" if self.is_disposed: raise DisposedException() si = ScheduledItem(self, state, action, None) with self.condition: self.ready_list.append(si) self.condition.notify() # signal that a new item is available self.ensure_thread() return Disposable(si.cancel) def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed after duetime.""" return self.schedule_absolute(duetime + self.now(), action, state=None) def schedule_absolute(self, duetime, action, state=None): """Schedules an action to be executed at duetime.""" if self.is_disposed: raise DisposedException() dt = self.to_datetime(duetime) si = ScheduledItem(self, state, action, dt) with self.condition: if dt < self.now(): self.ready_list.append(si) else: self.queue.enqueue(si) self.condition.notify() # signal that a new item is available self.ensure_thread() return Disposable(si.cancel) def ensure_thread(self): """Ensures there is an event loop thread running. Should be called under the gate.""" if not self.thread: self.thread = self.thread_factory(self.run) self.thread.start() def run(self): """Event loop scheduled on the designated event loop thread. The loop is suspended/resumed using the event which gets set by calls to Schedule, the next item timer, or calls to dispose.""" while True: ready = [] with self.condition: # The event could have been set by a call to dispose. This takes # priority over anything else. We quit the loop immediately. # Subsequent calls to Schedule won't ever create a new thread. if self.is_disposed: return while len(self.queue ) and self.queue.peek().duetime <= self.now(): item = self.queue.dequeue() self.ready_list.append(item) if len(self.queue): _next = self.queue.peek() if self.next_item is None or _next != self.next_item: self.next_item = _next due = _next.duetime - self.now() seconds = due.total_seconds() log.debug("timeout: %s", seconds) #print("run(), Starting timer ...") self.timer = Timer(seconds, self.tick, args=(_next, )) self.timer.start() if len(self.ready_list): ready = self.ready_list[:] self.ready_list = [] else: #print("run(), waiting ....") self.condition.wait() #print("run(), wakeup") for item in ready: #print("run(), foreach") if not item.is_cancelled(): #print("run(), invoking ...") item.invoke() if self.exit_if_empty: with self.condition: if not len(self.ready_list) and not len(self.queue): self.thread = None return def dipose(self): """Ends the thread associated with this scheduler. All remaining work in the scheduler queue is abandoned.""" with self.condition: if not self.is_disposed: self.is_disposed = True def tick(self, item): with self.condition: if not self.is_disposed: if self.queue.remove(item): self.ready_list.append(item) self.condition.notify() # signal that a new item is available
class EventLoopScheduler(SchedulerBase, Disposable): """Creates an object that schedules units of work on a designated thread. """ def __init__(self, thread_factory=None, exit_if_empty=False): super(EventLoopScheduler, self).__init__() self.is_disposed = False def default_factory(target): t = threading.Thread(target=target) t.setDaemon(True) return t self.lock = config["concurrency"].RLock() self.thread_factory = thread_factory or default_factory self.thread = None self.timer = None self.condition = config["concurrency"].Condition(self.lock) self.queue = PriorityQueue() self.ready_list = [] self.next_item = None self.exit_if_empty = exit_if_empty def schedule(self, action, state=None): """Schedules an action to be executed.""" if self.is_disposed: raise DisposedException() si = ScheduledItem(self, state, action, self.now) with self.condition: self.ready_list.append(si) self.condition.notify() # signal that a new item is available self.ensure_thread() return Disposable.create(si.cancel) def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed after duetime.""" dt = self.to_timedelta(duetime) return self.schedule_absolute(dt + self.now, action, state) def schedule_absolute(self, duetime, action, state=None): """Schedules an action to be executed at duetime.""" if self.is_disposed: raise DisposedException() dt = self.to_datetime(duetime) si = ScheduledItem(self, state, action, dt) with self.condition: if dt < self.now: self.ready_list.append(si) else: self.queue.enqueue(si) self.condition.notify() # signal that a new item is available self.ensure_thread() return Disposable.create(si.cancel) def schedule_periodic(self, period, action, state=None): """Schedule a periodic piece of work.""" disposed = [] s = [state] def tick(scheduler, state): if disposed: return self.schedule_relative(period, tick) new_state = action(s[0]) if new_state is not None: s[0] = new_state self.schedule_relative(period, tick) def dispose(): disposed.append(True) return Disposable.create(dispose) def ensure_thread(self): """Ensures there is an event loop thread running. Should be called under the gate.""" if not self.thread: self.thread = self.thread_factory(self.run) self.thread.start() def run(self): """Event loop scheduled on the designated event loop thread. The loop is suspended/resumed using the event which gets set by calls to Schedule, the next item timer, or calls to dispose.""" while True: ready = [] with self.condition: # The event could have been set by a call to dispose. This # takes priority over anything else. We quit the loop # immediately. Subsequent calls to Schedule won't ever create a # new thread. if self.is_disposed: return while len(self.queue) and self.queue.peek().duetime <= self.now: item = self.queue.dequeue() self.ready_list.append(item) if len(self.queue): _next = self.queue.peek() if self.next_item is None or _next != self.next_item: self.next_item = _next due = _next.duetime - self.now seconds = due.total_seconds() log.debug("timeout: %s", seconds) self.timer = Timer(seconds, self.tick, args=(_next,)) self.timer.setDaemon(True) self.timer.start() if len(self.ready_list): ready = self.ready_list[:] self.ready_list = [] else: self.condition.wait() for item in ready: if not item.is_cancelled(): item.invoke() if self.exit_if_empty: with self.condition: if not len(self.ready_list) and not len(self.queue): self.thread = None return def dispose(self): """Ends the thread associated with this scheduler. All remaining work in the scheduler queue is abandoned. """ with self.condition: if self.timer: self.timer.cancel() if not self.is_disposed: self.is_disposed = True def tick(self, item): with self.condition: if not self.is_disposed: if self.queue.remove(item): self.ready_list.append(item) self.condition.notify() # signal that a new item is available
class EventLoopScheduler(SchedulerBase, typing.Disposable): """Creates an object that schedules units of work on a designated thread.""" def __init__(self, thread_factory=None, exit_if_empty=False) -> None: super(EventLoopScheduler, self).__init__() self.is_disposed = False def default_factory(target): t = threading.Thread(target=target) t.setDaemon(True) return t self.lock = threading.RLock() self.thread_factory = thread_factory or default_factory self.thread: Optional[threading.Thread] = None self.timer: Optional[threading.Timer] = None self.condition = threading.Condition(self.lock) self.queue = PriorityQueue() self.ready_list: List[ScheduledItem] = [] self.next_item = None self.exit_if_empty = exit_if_empty def schedule(self, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed.""" if self.is_disposed: raise DisposedException() si: ScheduledItem[typing.TState] = ScheduledItem( self, state, action, self.now) with self.condition: self.ready_list.append(si) self.condition.notify() # signal that a new item is available self.ensure_thread() return Disposable(si.cancel) def schedule_relative(self, duetime: typing.RelativeTime, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed after duetime.""" dt: datetime = self.now + self.to_timedelta(duetime) return self.schedule_absolute(dt, action, state) def schedule_absolute(self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed at duetime.""" if self.is_disposed: raise DisposedException() dt = self.to_datetime(duetime) si: ScheduledItem[typing.TState] = ScheduledItem( self, state, action, dt) with self.condition: if dt < self.now: self.ready_list.append(si) else: self.queue.enqueue(si) self.condition.notify() # signal that a new item is available self.ensure_thread() return Disposable(si.cancel) def schedule_periodic(self, period: typing.RelativeTime, action: typing.ScheduledPeriodicAction, state: Any = None) -> typing.Disposable: """Schedule a periodic piece of work.""" disposed: List[bool] = [] s = [state] def tick(scheduler, state): if disposed: return self.schedule_relative(period, tick) new_state = action(s[0]) if new_state is not None: s[0] = new_state self.schedule_relative(period, tick) def dispose(): disposed.append(True) return Disposable(dispose) def ensure_thread(self) -> None: """Ensures there is an event loop thread running. Should be called under the gate.""" if not self.thread: thread = self.thread_factory(self.run) self.thread = thread thread.start() def run(self) -> None: """Event loop scheduled on the designated event loop thread. The loop is suspended/resumed using the event which gets set by calls to Schedule, the next item timer, or calls to dispose.""" while True: ready: List[ScheduledItem] = [] with self.condition: # The event could have been set by a call to dispose. This # takes priority over anything else. We quit the loop # immediately. Subsequent calls to Schedule won't ever create a # new thread. if self.is_disposed: return while self.queue and self.queue.peek().duetime <= self.now: item = self.queue.dequeue() self.ready_list.append(item) if self.queue: _next = self.queue.peek() if self.next_item is None or _next != self.next_item: self.next_item = _next due = _next.duetime - self.now seconds = due.total_seconds() log.debug("timeout: %s", seconds) self.timer = threading.Timer(seconds, self.tick, args=[_next]) self.timer.setDaemon(True) self.timer.start() if self.ready_list: ready = self.ready_list[:] self.ready_list = [] else: self.condition.wait() for item in ready: if not item.is_cancelled(): item.invoke() if self.exit_if_empty: with self.condition: if not self.ready_list and not self.queue: self.thread = None return def dispose(self) -> None: """Ends the thread associated with this scheduler. All remaining work in the scheduler queue is abandoned. """ with self.condition: if self.timer: self.timer.cancel() if not self.is_disposed: self.is_disposed = True def tick(self, item) -> None: with self.condition: if not self.is_disposed: if self.queue.remove(item): self.ready_list.append(item) self.condition.notify() # signal that a new item is available
def __init__(self): self._idle: bool = True self._queue: PriorityQueue[ScheduledItem] = PriorityQueue() self._lock: Lock = Lock() self._condition: Condition = Condition(self._lock)