def test_mutabledisposable_dispose(): disp = [False] m = SerialDisposable() def action(): disp[0] = True d = Disposable(action) m.disposable = d assert d == m.disposable assert not disp[0] m.dispose() assert disp[0] assert m.disposable == None
def test_mutabledisposable_replaceafterdispose(): disp1 = [False] disp2 = [False] m = SerialDisposable() m.dispose() def action1(): disp1[0] = True d1 = Disposable(action1) m.disposable = d1 assert m.disposable == None assert disp1[0] def action2(): disp2[0] = True d2 = Disposable(action2) m.disposable = d2 assert m.disposable == None assert disp2[0]
class PeriodicTimer(object): """A timer that runs every interval seconds, can shift in time""" def __init__(self, interval, action): super(PeriodicTimer, self).__init__() self.interval = interval self.action = action self.timerDisposable = SerialDisposable() def start(self): timer = Timer(self.interval, self._execute) self.timerDisposable.disposable = Disposable.create(timer.cancel) timer.start() return self.timerDisposable def cancel(self): self.timerDisposable.dispose() def _execute(self): self.action() self.run()
class ScheduledObserver(ObserverBase): STOPPED = 0 RUNNING = 1 PENDING = 2 FAULTED = 9 def __init__(self, scheduler, observer): super(ScheduledObserver, self).__init__() self.scheduler = scheduler self.observer = observer self.state = Atomic(ScheduledObserver.STOPPED, self.lock) self.disposable = SerialDisposable() self.failed = False self.exception = None self.completed = False self.queue = Queue() self.dispatcherJob = None self.dispatcherEvent = Semaphore(0) def ensureDispatcher(self): if self.dispatcherJob != None: return with self.lock: if self.dispatcherJob == None: self.dispatcherJob = self.scheduler.scheduleLongRunning(self.dispatch) self.disposable.disposable = CompositeDisposable( self.dispatcherJob, Disposable.create(self.dispatcherEvent.release) ) def dispatch(self, cancel): while True: self.dispatcherEvent.acquire() if cancel.isDisposed: return while True: next = self.queue.get() try: self.observer.onNext(next) except Exception as e: self.clearQueue() raise e self.dispatcherEvent.acquire() if cancel.isDisposed: return if self.failed: self.observer.onError(self.exception) self.dispose() return if self.completed: self.observer.onCompleted() self.dispose() return def ensureActive(self, n = 1): if self.scheduler.isLongRunning: while n > 0: self.dispatcherEvent.release() n -= 1 self.ensureDispatcher() else: self.ensureActiveSlow() def ensureActiveSlow(self): isOwner = False while True: old = self.state.compareExchange( ScheduledObserver.RUNNING, ScheduledObserver.STOPPED ) if old == ScheduledObserver.STOPPED: isOwner = True break elif old == ScheduledObserver.FAULTED: return elif ( (old == ScheduledObserver.PENDING or old == ScheduledObserver.RUNNING) and self.state.compareExchange(ScheduledObserver.PENDING, ScheduledObserver.RUNNING) == ScheduledObserver.RUNNING ): break if isOwner: self.disposable = self.scheduler.scheduleRecursiveWithState(None, self.run) def run(self, state, continuation): next = None while True: try: next = self.queue.get_nowait() except Empty: next = None if next != None: break if self.failed: # wait until the queue is drained if not self.queue.empty(): continue self.state.value = ScheduledObserver.STOPPED self.observer.onError(self.exception) self.dispose() return if self.completed: # wait until the queue is drained if not self.queue.empty(): continue self.state.value = ScheduledObserver.STOPPED self.observer.onCompleted() self.dispose() return old = self.state.compareExchange( ScheduledObserver.STOPPED, ScheduledObserver.RUNNING ) if old == ScheduledObserver.RUNNING or old == ScheduledObserver.FAULTED: return # assert(old == ScheduledObserver.PENDING) self.state.value = ScheduledObserver.RUNNING #end while # we found an item, so next != None self.state.value = ScheduledObserver.RUNNING try: self.observer.onNext(next) except Exception as e: self.state.value = ScheduledObserver.FAULTED self.clearQueue() raise e continuation(state) def onNextCore(self, value): self.queue.put(value) def onErrorCore(self, exception): self.exception = exception self.failed = True def onCompletedCore(self): self.completed = True def clearQueue(self): try: while True: self.queue.get() except Empty: pass def dispose(self): super(ScheduledObserver, self).dispose() self.disposable.dispose()
class ScheduledObserver(ObserverBase): def __init__(self, scheduler: abc.Scheduler, observer: abc.Observer) -> None: super().__init__() self.scheduler = scheduler self.observer = observer self.lock = threading.RLock() self.is_acquired = False self.has_faulted = False self.queue: List[Action] = [] self.disposable = SerialDisposable() # Note to self: list append is thread safe # http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm def _on_next_core(self, value: Any) -> None: def action(): self.observer.on_next(value) self.queue.append(action) def _on_error_core(self, error: Exception) -> None: def action(): self.observer.on_error(error) self.queue.append(action) def _on_completed_core(self) -> None: def action(): self.observer.on_completed() self.queue.append(action) def ensure_active(self) -> None: is_owner = False with self.lock: if not self.has_faulted and self.queue: is_owner = not self.is_acquired self.is_acquired = True if is_owner: self.disposable.disposable = self.scheduler.schedule(self.run) def run(self, scheduler: abc.Scheduler, state: Any) -> None: parent = self with self.lock: if parent.queue: work = parent.queue.pop(0) else: parent.is_acquired = False return try: work() except Exception: with self.lock: parent.queue = [] parent.has_faulted = True raise self.scheduler.schedule(self.run) def dispose(self) -> None: super().dispose() self.disposable.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(DelayObservable.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.delays = CompositeDisposable() self.gate = RLock() self.atEnd = False self.subscription = SerialDisposable() if self.parent.subscriptionDelay == None: self.start() else: self.subscription.disposable = self.parent.subscriptionDelay.subscribeSafe( self.Sigma(self)) return CompositeDisposable(self.subscription, self.delays) def start(self): self.subscription.disposable = self.parent.source.subscribeSafe( self) def onNext(self, value): try: delay = self.parent.delaySelector(value) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() else: d = SingleAssignmentDisposable() self.delays.add(d) d.disposable = delay.subscribeSafe(self.Delta(self, value, d)) def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): with self.gate: self.atEnd = True self.subscription.dispose() self.checkDone() def checkDone(self): if self.atEnd and self.delays.length == 0: self.observer.onCompleted() self.dispose() class Sigma(Observer): def __init__(self, parent): self.parent = parent def onNext(self, value): self.parent.start() def onError(self, exception): self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.parent.start() class Delta(Observer): def __init__(self, parent, value, cancelSelf): self.parent = parent self.value = value self.cancelSelf = cancelSelf def onNext(self, delay): with self.parent.gate: self.parent.observer.onNext(self.value) self.parent.delays.remove(self.cancelSelf) self.parent.checkDone() def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): with self.parent.gate: self.parent.observer.onNext(self.value) self.parent.delays.remove(self.cancelSelf) self.parent.checkDone()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(ThrottleObservable.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.value = None self.hasValue = False self.throttleDisposable = SerialDisposable() self.resourceId = 0 subscription = self.parent.source.subscribeSafe(self) return CompositeDisposable(subscription, self.throttleDisposable) def onNext(self, value): throttle = None try: throttle = self.parent.throttleSelector(value) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() return currentId = 0 with self.gate: self.hasValue = True self.value = value self.resourceId += 1 currentId = self.resourceId d = SingleAssignmentDisposable() self.throttleDisposable.disposable = d d.disposable = throttle.subscribeSafe(self.Delta(self, value, currentId, d)) def onError(self, exception): self.throttleDisposable.dispose() with self.gate: self.observer.onError(exception) self.dispose() self.hasValue = False self.resourceId += 1 def onCompleted(self): self.throttleDisposable.dispose() with self.gate: if self.hasValue: self.observer.onNext(self.value) self.observer.onCompleted() self.dispose() self.hasValue = False self.resourceId += 1 class Delta(Observer): def __init__(self, parent, value, currentId, cancelSelf): self.parent = parent self.value = value self.currentId = currentId self.cancelSelf = cancelSelf def onNext(self, value): with self.parent.gate: if self.parent.hasValue and self.parent.resourceId == self.currentId: self.parent.observer.onNext(self.value) self.parent.hasValue = False self.cancelSelf.dispose() def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): with self.parent.gate: if self.parent.hasValue and self.parent.resourceId == self.currentId: self.parent.observer.onNext(self.value) self.parent.hasValue = False self.cancelSelf.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(ThrottleTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.value = None self.hasValue = False self.propagatorDisposable = SerialDisposable() self.resourceId = 0 subscription = self.parent.source.subscribeSafe(self) return CompositeDisposable(subscription, self.propagatorDisposable) def onNext(self, value): currentId = 0 with self.gate: self.hasValue = True self.value = value self.resourceId += 1 currentId = self.resourceId d = SingleAssignmentDisposable() self.propagatorDisposable.disposable = d d.disposable = self.parent.scheduler.scheduleWithRelativeAndState( currentId, self.parent.dueTime, self.propagate ) def propagate(self, scheduler, currentId): with self.gate: if self.hasValue and self.resourceId == currentId: self.observer.onNext(self.value) self.hasValue = False return Disposable.empty() def onError(self, exception): self.propagatorDisposable.dispose() with self.gate: self.observer.onError(exception) self.dispose() self.hasValue = False self.resourceId += 1 def onCompleted(self): self.propagatorDisposable.dispose() with self.gate: if self.hasValue: self.observer.onNext(self.value) self.observer.onCompleted() self.dispose() self.hasValue = False self.resourceId += 1
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(DelayObservable.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.delays = CompositeDisposable() self.gate = RLock() self.atEnd = False self.subscription = SerialDisposable() if self.parent.subscriptionDelay == None: self.start() else: self.subscription.disposable = self.parent.subscriptionDelay.subscribeSafe(self.Sigma(self)) return CompositeDisposable(self.subscription, self.delays) def start(self): self.subscription.disposable = self.parent.source.subscribeSafe(self) def onNext(self, value): try: delay = self.parent.delaySelector(value) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() else: d = SingleAssignmentDisposable() self.delays.add(d) d.disposable = delay.subscribeSafe(self.Delta(self, value, d)) def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): with self.gate: self.atEnd = True self.subscription.dispose() self.checkDone() def checkDone(self): if self.atEnd and self.delays.length == 0: self.observer.onCompleted() self.dispose() class Sigma(Observer): def __init__(self, parent): self.parent = parent def onNext(self, value): self.parent.start() def onError(self, exception): self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.parent.start() class Delta(Observer): def __init__(self, parent, value, cancelSelf): self.parent = parent self.value = value self.cancelSelf = cancelSelf def onNext(self, delay): with self.parent.gate: self.parent.observer.onNext(self.value) self.parent.delays.remove(self.cancelSelf) self.parent.checkDone() def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): with self.parent.gate: self.parent.observer.onNext(self.value) self.parent.delays.remove(self.cancelSelf) self.parent.checkDone()
class ScheduledSingle(Single): def __init__(self, scheduler: typing.Scheduler, single: Single) -> None: super().__init__() self.scheduler = scheduler self.single = single self.lock = threading.RLock() self.is_acquired = False self.has_faulted = False self.queue: List[typing.Action] = [] self.disposable = SerialDisposable() def on_next(self, value): def action(): self.single.on_next(value) self.queue.append(action) self.ensure_active() def on_error(self, error: Exception): def action(): self.single.on_error(error) self.queue.append(action) self.ensure_active() def ensure_active(self) -> None: is_owner = False with self.lock: if not self.has_faulted and self.queue: is_owner = not self.is_acquired self.is_acquired = True if is_owner: self.disposable.disposable = self.scheduler.schedule(self.run) def run(self, _, __) -> None: parent = self with self.lock: if parent.queue: work = parent.queue.pop(0) else: parent.is_acquired = False return try: work() except Exception: with self.lock: parent.queue = [] parent.has_faulted = True raise self.scheduler.schedule(self.run) def dispose(self) -> None: self.is_stopped = True self.disposable.dispose()