class ScheduledItem(Generic[typing.TState]): # pylint: disable=unsubscriptable-object def __init__(self, scheduler: SchedulerBase, state: Optional[typing.TState], action: typing.ScheduledAction, duetime: typing.AbsoluteTime): self.scheduler = scheduler self.state = state self.action = action self.duetime = duetime self.disposable = SingleAssignmentDisposable() def invoke(self) -> None: ret = self.scheduler.invoke_action(self.action, self.state) self.disposable.disposable = ret def cancel(self) -> None: """Cancels the work item by disposing the resource returned by invoke_core as soon as possible.""" self.disposable.dispose() def is_cancelled(self) -> bool: return self.disposable.is_disposed def __lt__(self, other): return self.duetime < other.duetime def __gt__(self, other): return self.duetime > other.duetime def __eq__(self, other): return self.duetime == other.duetime
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(SampleWithObservable.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.atEnd = False self.hasValue = False self.value = None self.sourceSubscription = SingleAssignmentDisposable() self.sourceSubscription.disposable = self.parent.source.subscribeSafe(self) samplerSubscription = self.parent.sampler.subscribeSafe(self.SamplerObserver(self)) return CompositeDisposable(self.sourceSubscription, samplerSubscription) def onNext(self, value): with self.gate: self.hasValue = True self.value = value def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): with self.gate: self.atEnd = True self.sourceSubscription.dispose() class SamplerObserver(Observer): def __init__(self, parent): self.parent = parent def onNext(self, value): with self.parent.gate: if self.parent.hasValue: self.parent.hasValue = False self.parent.observer.onNext(self.parent.value) if self.parent.atEnd: self.parent.observer.onCompleted() self.parent.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: self.parent.hasValue = False self.parent.observer.onNext(self.parent.value) if self.parent.atEnd: self.parent.observer.onCompleted() self.parent.dispose()
class O(Observer): def __init__(self, parent, sourceObserver): self.parent = parent self.sourceObserver = sourceObserver self.subscription = SingleAssignmentDisposable() def setdisposable(self, value): self.subscription.disposable = value disposable = property(None, setdisposable) def onNext(self, value): with self.parent.gate: self.parent.observer.onCompleted() self.parent.dispose() def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): with self.parent.gate: self.sourceObserver.open = True self.subscription.dispose()
class AutoDetachObserver(typing.Observer): def __init__(self, on_next: Optional[typing.OnNext] = None, on_error: Optional[typing.OnError] = None, on_completed: Optional[typing.OnCompleted] = None ) -> Any: self._on_next = on_next or noop self._on_error = on_error or default_error self._on_completed = on_completed or noop self._subscription = SingleAssignmentDisposable() self.is_stopped = False def on_next(self, value: Any) -> None: if self.is_stopped: return try: self._on_next(value) except Exception: self.dispose() raise def on_error(self, error) -> None: if self.is_stopped: return self.is_stopped = True try: self._on_error(error) finally: self.dispose() def on_completed(self) -> None: if self.is_stopped: return self.is_stopped = True try: self._on_completed() finally: self.dispose() def set_disposable(self, value: typing.Disposable): self._subscription.disposable = value subscription = property(fset=set_disposable) def dispose(self) -> None: self.is_stopped = True self._subscription.dispose() def fail(self, exn: Exception) -> bool: if self.is_stopped: return False self.is_stopped = True self._on_error(exn) return True
class AutoDetachObserver(Observer): def __init__(self, on_next=None, on_error=None, on_completed=None): self._on_next = on_next or noop self._on_error = on_error or default_error self._on_completed = on_completed or noop self._subscription = SingleAssignmentDisposable() self.is_stopped = False def on_next(self, value: Any) -> None: if self.is_stopped: return try: self._on_next(value) except Exception: self.dispose() raise def on_error(self, error) -> None: if self.is_stopped: return self.is_stopped = True try: self._on_error(error) finally: self.dispose() def on_completed(self) -> None: if self.is_stopped: return self.is_stopped = True try: self._on_completed() finally: self.dispose() def set_disposable(self, value): self._subscription.disposable = value subscription = property(fset=set_disposable) def dispose(self) -> None: self.is_stopped = True self._subscription.dispose() def fail(self, exn: Exception) -> bool: if self.is_stopped: return False self.is_stopped = True self._on_error(exn) return True
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(TakeLastCount.Sink, self).__init__(observer, cancel) self.parent = parent self.queue = deque() def run(self): self.subscription = SingleAssignmentDisposable() self.loopDisposable = SingleAssignmentDisposable() self.subscription.disposable = self.parent.source.subscribeSafe( self) return CompositeDisposable(self.subscription, self.loopDisposable) def onNext(self, value): self.queue.append(value) if len(self.queue) > self.parent.count: self.queue.popleft() def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.subscription.dispose() scheduler = self.parent.scheduler if scheduler.isLongRunning: self.loopDisposable.disposable = scheduler.scheduleLongRunning( self.loop) else: self.loopDisposable.disposable = scheduler.scheduleRecursive( self.loopRec) def loopRec(self, recurse): if len(self.queue) > 0: self.observer.onNext(self.queue.popleft()) recurse() else: self.observer.onCompleted() self.dispose() def loop(self, cancel): while not cancel.isDisposed: if len(self.queue) == 0: self.observer.onCompleted() break else: self.observer.onNext(self.queue.popleft()) self.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(TakeLastCount.Sink, self).__init__(observer, cancel) self.parent = parent self.queue = deque() def run(self): self.subscription = SingleAssignmentDisposable() self.loopDisposable = SingleAssignmentDisposable() self.subscription.disposable = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.subscription, self.loopDisposable) def onNext(self, value): self.queue.append(value) if len(self.queue) > self.parent.count: self.queue.popleft() def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.subscription.dispose() scheduler = self.parent.scheduler if scheduler.isLongRunning: self.loopDisposable.disposable = scheduler.scheduleLongRunning(self.loop) else: self.loopDisposable.disposable = scheduler.scheduleRecursive(self.loopRec) def loopRec(self, recurse): if len(self.queue) > 0: self.observer.onNext(self.queue.popleft()) recurse() else: self.observer.onCompleted() self.dispose() def loop(self, cancel): while not cancel.isDisposed: if len(self.queue) == 0: self.observer.onCompleted() break else: self.observer.onNext(self.queue.popleft()) self.dispose()
def test_futuredisposable_disposeafterset(): d = SingleAssignmentDisposable() disposed = [False] def action(): disposed[0] = True dd = Disposable(action) d.disposable = dd assert dd == d.disposable assert not disposed[0] d.dispose() assert disposed[0] d.dispose() assert disposed[0]
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(TakeLastBufferTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.subscription = SingleAssignmentDisposable() self.loop = SingleAssignmentDisposable() self.startTime = self.parent.scheduler.now() self.subscription.disposable = self.parent.source.subscribeSafe( self) return CompositeDisposable(self.subscription, self.loop) def elapsed(self): return self.parent.scheduler.now() - self.startTime def trim(self, now): while len(self.queue) > 0: current = self.queue.popleft() if current.interval < self.parent.duration: self.queue.appendleft(current) break def onNext(self, value): now = self.elapsed() self.queue.append(Struct(value=value, interval=now)) self.trim(now) def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.subscription.dispose() now = self.elapsed() self.trim(now) res = list([x.value for x in self.queue]) self.observer.onNext(res) self.observer.onCompleted() self.dispose()
def test_futuredisposable_disposebeforeset(): disposed = [False] def dispose(): disposed[0] = True d = SingleAssignmentDisposable() dd = Disposable(dispose) assert not disposed[0] d.dispose() assert not disposed[0] d.disposable = dd assert d.disposable == None assert disposed[0] d.dispose() assert disposed[0]
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(TakeLastBufferTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.subscription = SingleAssignmentDisposable() self.loop = SingleAssignmentDisposable() self.startTime = self.parent.scheduler.now() self.subscription.disposable = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.subscription, self.loop) def elapsed(self): return self.parent.scheduler.now() - self.startTime def trim(self, now): while len(self.queue) > 0: current = self.queue.popleft() if current.interval < self.parent.duration: self.queue.appendleft(current) break def onNext(self, value): now = self.elapsed() self.queue.append(Struct(value=value,interval=now)) self.trim(now) def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.subscription.dispose() now = self.elapsed() self.trim(now) res = list([x.value for x in self.queue]) self.observer.onNext(res) self.observer.onCompleted() self.dispose()
class AutoDetachObserver(ObserverBase): def __init__(self, observer, disposable = None): super(AutoDetachObserver, self).__init__() self.observer = observer self.m = SingleAssignmentDisposable() if disposable != None: self.m.disposable = disposable def onNextCore(self, value): noError = False try: self.observer.onNext(value) noError = True finally: if not noError: self.dispose() def onErrorCore(self, ex): try: self.observer.onError(ex) finally: self.dispose() def onCompletedCore(self): try: self.observer.onCompleted() finally: self.dispose() def disposable(): doc = "The disposable property." def fget(self): self.m.disposable def fset(self, value): self.m.disposable = value return locals() disposable = property(**disposable()) def dispose(self): with self.lock: super(AutoDetachObserver, self).dispose() self.m.dispose()
class AutoDetachObserver(ObserverBase): def __init__(self, observer, disposable = None): super(AutoDetachObserver, self).__init__() self.observer = observer self.m = SingleAssignmentDisposable() if disposable != None: self.m.disposable = disposable def onNextCore(self, value): noError = False try: self.observer.onNext(value) noError = True finally: if not noError: self.dispose() def onErrorCore(self, ex): try: self.observer.onError(ex) finally: self.dispose() def onCompletedCore(self): try: self.observer.onCompleted() finally: self.dispose() def disposable(): """The disposable property.""" def fget(self): self.m.disposable def fset(self, value): self.m.disposable = value return locals() disposable = property(**disposable()) def dispose(self): with self.lock: super(AutoDetachObserver, self).dispose() self.m.dispose()
class O(Observer): def __init__(self, parent, sourceObserver): self.parent = parent self.sourceObserver = sourceObserver self.subscription = SingleAssignmentDisposable() def setdisposable(self, value): self.subscription.disposable = value disposable = property(None, setdisposable) def onNext(self, value): self.sourceObserver.observer = self.parent.observer self.subscription.dispose() def onError(self, exception): self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.subscription.dispose()
class T(Observer): def __init__(self, parent): self.parent = parent self.observer = NoopObserver() self.subscription = SingleAssignmentDisposable() def setdisposable(self, value): self.subscription.disposable = value disposable = property(None, setdisposable) def onNext(self, value): self.observer.onNext(value) def onError(self, exception): self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.observer.onCompleted() # We can't cancel the other stream yet, it may be on its way # to dispatch an OnError message and we don't want to have a race. self.subscription.dispose()
class SerialSink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(Merge.SerialSink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.isStopped = False self.group = CompositeDisposable() self.sourceSubscription = SingleAssignmentDisposable() self.group.add(self.sourceSubscription) self.sourceSubscription.disposable = self.parent.sources.subscribeSafe(self) return self.group def onNext(self, value): innerSubscription = SingleAssignmentDisposable() self.group.add(innerSubscription) innerSubscription.disposable = value.subscribeSafe(self.LockObserver(self, innerSubscription)) def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): self.isStopped = True if self.group.length == 1: # # Notice there can be a race between OnCompleted of the source and any # of the inner sequences, where both see _group.Count == 1, and one is # waiting for the lock. There won't be a double OnCompleted observation # though, because the call to Dispose silences the observer by swapping # in a NopObserver<T>. # with self.gate: self.observer.onCompleted() self.dispose() else: self.sourceSubscription.dispose() class LockObserver(Observer): def __init__(self, parent, subscription): self.parent = parent self.subscription = subscription def onNext(self, value): with self.parent.gate: self.parent.observer.onNext(value) def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.parent.group.remove(self.subscription) if self.parent.isStopped and self.parent.group.length == 1: # # Notice there can be a race between OnCompleted of the source and any # of the inner sequences, where both see _group.Count == 1, and one is # waiting for the lock. There won't be a double OnCompleted observation # though, because the call to Dispose silences the observer by swapping # in a NopObserver<T>. # with self.parent.gate: self.parent.observer.onCompleted() self.parent.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(Switch.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.innerSubscription = SerialDisposable() self.isStopped = False self.latest = 0 self.hasLatest = False self.subscription = SingleAssignmentDisposable() self.subscription.disposable = self.parent.sources.subscribeSafe(self) return CompositeDisposable(self.subscription, self.innerSubscription) def onNext(self, value): observerId = 0 with self.gate: self.latest += 1 observerId = self.latest self.hasLatest = True d = SingleAssignmentDisposable() self.innerSubscription.disposable = d d.disposable = value.subscribeSafe(self.IdObserver(self, observerId, d)) def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): with self.gate: self.subscription.dispose() self.isStopped = True if not self.hasLatest: self.observer.onCompleted() self.dispose() class IdObserver(Observer): def __init__(self, parent, observerId, cancelSelf): self.parent = parent self.observerId = observerId self.cancelSelf = cancelSelf def onNext(self, value): with self.parent.gate: if self.parent.latest == self.observerId: self.parent.observer.onNext(value) def onError(self, exception): with self.parent.gate: self.cancelSelf.dispose() if self.parent.latest == self.observerId: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): with self.parent.gate: self.cancelSelf.dispose() if self.parent.latest == self.observerId: self.parent.hasLatest = False if self.parent.isStopped: self.parent.observer.onCompleted() self.parent.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(TakeLastTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.subscription = SingleAssignmentDisposable() self.loop = SingleAssignmentDisposable() self.startTime = self.parent.scheduler.now() self.subscription.disposable = self.parent.source.subscribeSafe( self) return CompositeDisposable(self.subscription, self.loop) def elapsed(self): return self.parent.scheduler.now() - self.startTime def trim(self, now): while len(self.queue) > 0: current = self.queue.popleft() if current.interval < self.parent.duration: self.queue.appendleft(current) break def onNext(self, value): now = self.elapsed() self.queue.append(Struct(value=value, interval=now)) self.trim(now) def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.subscription.dispose() now = self.elapsed() self.trim(now) scheduler = self.parent.scheduler if scheduler.isLongRunning: self.loop.disposable = scheduler.scheduleLongRunning(self.loop) else: self.loop.disposable = scheduler.scheduleRecursive( self.loopRec) def loopRec(self, recurse): if len(self.queue) > 0: self.observer.onNext(self.queue.popleft().value) recurse() else: self.observer.onCompleted() self.dispose() def loop(self, cancel): while not cancel.isDisposed: if len(self.queue) == 0: self.observer.onCompleted() break else: self.observer.onNext(self.queue.popleft().value) self.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(Switch.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.innerSubscription = SerialDisposable() self.isStopped = False self.latest = 0 self.hasLatest = False self.subscription = SingleAssignmentDisposable() self.subscription.disposable = self.parent.sources.subscribeSafe( self) return CompositeDisposable(self.subscription, self.innerSubscription) def onNext(self, value): observerId = 0 with self.gate: self.latest += 1 observerId = self.latest self.hasLatest = True d = SingleAssignmentDisposable() self.innerSubscription.disposable = d d.disposable = value.subscribeSafe( self.IdObserver(self, observerId, d)) def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): with self.gate: self.subscription.dispose() self.isStopped = True if not self.hasLatest: self.observer.onCompleted() self.dispose() class IdObserver(Observer): def __init__(self, parent, observerId, cancelSelf): self.parent = parent self.observerId = observerId self.cancelSelf = cancelSelf def onNext(self, value): with self.parent.gate: if self.parent.latest == self.observerId: self.parent.observer.onNext(value) def onError(self, exception): with self.parent.gate: self.cancelSelf.dispose() if self.parent.latest == self.observerId: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): with self.parent.gate: self.cancelSelf.dispose() if self.parent.latest == self.observerId: self.parent.hasLatest = False if self.parent.isStopped: self.parent.observer.onCompleted() self.parent.dispose()
class LongrunningSink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(DelayTime.LongrunningSink, self).__init__(observer, cancel) self.parent = parent def run(self): self.scheduler = self.parent.scheduler self.cancelTimer = SerialDisposable() self.gate = RLock() self.evt = Semaphore(0) self.stopped = False self.stop = Event() self.queue = deque() self.hasCompleted = False self.completeAt = 0 self.hasFailed = False self.exception = None self.delay = 0 self.startTime = self.scheduler.now() if self.parent.isAbsolute: self.cancelTimer.disposable = self.scheduler.scheduleAbsolute( self.parent.dueTime, self.start ) else: self.delay = Scheduler.normalize(self.parent.dueTime) self.scheduleDrain() self.sourceSubscription = SingleAssignmentDisposable() self.sourceSubscription.disposable = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.sourceSubscription, self.cancelTimer) def elapsed(self): return self.scheduler.now() - self.startTime def start(self): with self.gate: self.delay = self.elapsed() for item in self.queue: item.interval += self.delay self.scheduleDrain() def scheduleDrain(self): def cancel(): self.stopped = True self.stop.set() self.evt.release() self.stop.clear() self.cancelTimer.disposable = Disposable.create(cancel) self.scheduler.scheduleLongRunning(self.drainQueue) def onNext(self, value): next = self.elapsed() + self.delay with self.gate: self.queue.append(Struct(value=value, interval=next)) self.evt.release() def onError(self, exception): self.sourceSubscription.dispose() with self.gate: self.queue.clear() self.exception = exception self.hasFailed = True self.evt.release() def onCompleted(self): self.sourceSubscription.dispose() next = self.elapsed() + self.delay with self.gate: self.completeAt = next self.hasCompleted = True self.evt.release() def drainQueue(self, cancel): while True: self.evt.acquire() if self.stopped: return hasFailed = False error = NotImplementedError hasValue = False value = NotImplementedError hasCompleted = False shouldWait = False waitTime = 0 with self.gate: if self.hasFailed: error = self.exception hasFailed = True else: now = self.elapsed() if len(self.queue) > 0: next = self.queue.popleft() hasValue = True value = next.value nextDue = next.interval if nextDue > now: shouldWait = True waitTime = Scheduler.normalize(nextDue - now) elif self.hasCompleted: hasCompleted = True if self.completeAt > now: shouldWait = True waitTime = Scheduler.normalize(self.completeAt - now) # end with self.gate if shouldWait: timer = Event() self.scheduler.scheduleWithRelative(waitTime, lambda: timer.set()) timer.wait() if hasValue: self.observer.onNext(value) else: if hasCompleted: self.observer.onCompleted() self.dispose() elif hasFailed: self.observer.onError(error) self.dispose() return
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(TakeLastTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.subscription = SingleAssignmentDisposable() self.loop = SingleAssignmentDisposable() self.startTime = self.parent.scheduler.now() self.subscription.disposable = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.subscription, self.loop) def elapsed(self): return self.parent.scheduler.now() - self.startTime def trim(self, now): while len(self.queue) > 0: current = self.queue.popleft() if current.interval < self.parent.duration: self.queue.appendleft(current) break def onNext(self, value): now = self.elapsed() self.queue.append(Struct(value=value, interval=now)) self.trim(now) def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.subscription.dispose() now = self.elapsed() self.trim(now) scheduler = self.parent.scheduler if scheduler.isLongRunning: self.loop.disposable = scheduler.scheduleLongRunning(self.loop) else: self.loop.disposable = scheduler.scheduleRecursive(self.loopRec) def loopRec(self, recurse): if len(self.queue) > 0: self.observer.onNext(self.queue.popleft().value) recurse() else: self.observer.onCompleted() self.dispose() def loop(self, cancel): while not cancel.isDisposed: if len(self.queue) == 0: self.observer.onCompleted() break else: self.observer.onNext(self.queue.popleft().value) self.dispose()
class LongrunningSink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(DelayTime.LongrunningSink, self).__init__(observer, cancel) self.parent = parent def run(self): self.scheduler = self.parent.scheduler self.cancelTimer = SerialDisposable() self.gate = RLock() self.evt = Semaphore(0) self.stopped = False self.stop = Event() self.queue = deque() self.hasCompleted = False self.completeAt = 0 self.hasFailed = False self.exception = None self.delay = 0 self.startTime = self.scheduler.now() if self.parent.isAbsolute: self.cancelTimer.disposable = self.scheduler.scheduleAbsolute( self.parent.dueTime, self.start) else: self.delay = Scheduler.normalize(self.parent.dueTime) self.scheduleDrain() self.sourceSubscription = SingleAssignmentDisposable() self.sourceSubscription.disposable = self.parent.source.subscribeSafe( self) return CompositeDisposable(self.sourceSubscription, self.cancelTimer) def elapsed(self): return self.scheduler.now() - self.startTime def start(self): with self.gate: self.delay = self.elapsed() for item in self.queue: item.interval += self.delay self.scheduleDrain() def scheduleDrain(self): def cancel(): self.stopped = True self.stop.set() self.evt.release() self.stop.clear() self.cancelTimer.disposable = Disposable.create(cancel) self.scheduler.scheduleLongRunning(self.drainQueue) def onNext(self, value): next = self.elapsed() + self.delay with self.gate: self.queue.append(Struct(value=value, interval=next)) self.evt.release() def onError(self, exception): self.sourceSubscription.dispose() with self.gate: self.queue.clear() self.exception = exception self.hasFailed = True self.evt.release() def onCompleted(self): self.sourceSubscription.dispose() next = self.elapsed() + self.delay with self.gate: self.completeAt = next self.hasCompleted = True self.evt.release() def drainQueue(self, cancel): while True: self.evt.acquire() if self.stopped: return hasFailed = False error = NotImplementedError hasValue = False value = NotImplementedError hasCompleted = False shouldWait = False waitTime = 0 with self.gate: if self.hasFailed: error = self.exception hasFailed = True else: now = self.elapsed() if len(self.queue) > 0: next = self.queue.popleft() hasValue = True value = next.value nextDue = next.interval if nextDue > now: shouldWait = True waitTime = Scheduler.normalize(nextDue - now) elif self.hasCompleted: hasCompleted = True if self.completeAt > now: shouldWait = True waitTime = Scheduler.normalize( self.completeAt - now) # end with self.gate if shouldWait: timer = Event() self.scheduler.scheduleWithRelative( waitTime, lambda: timer.set()) timer.wait() if hasValue: self.observer.onNext(value) else: if hasCompleted: self.observer.onCompleted() self.dispose() elif hasFailed: self.observer.onError(error) self.dispose() return
def invokeStart(self, scheduler, state): # # Notice the first call to OnNext will introduce skew if it takes significantly long when # using the following naive implementation: # # Code: base._observer.OnNext(0L); # return self.SchedulePeriodicEmulated(1L, _period, (Func<long, long>)Tick); # # What we're saying here is that Observable.Timer(dueTime, period) is pretty much the same # as writing Observable.Timer(dueTime).Concat(Observable.Interval(period)). # # Expected: dueTime # | # 0--period--1--period--2--period--3--period--4--... # | # +-OnNext(0L)-| # # Actual: dueTime # | # 0------------#--period--1--period--2--period--3--period--4--... # | # +-OnNext(0L)-| # # Different solutions for this behavior have different problems: # # 1. Scheduling the periodic job first and using an AsyncLock to serialize the OnNext calls # has the drawback that InvokeStart may never return. This happens when every callback # doesn't meet the period's deadline, hence the periodic job keeps queueing stuff up. In # this case, InvokeStart stays the owner of the AsyncLock and the call to Wait will never # return, thus not allowing any interleaving of work on this scheduler's logical thread. # # 2. Scheduling the periodic job first and using a (blocking) synchronization primitive to # signal completion of the OnNext(0L) call to the Tick call requires quite a bit of state # and careful handling of the case when OnNext(0L) throws. What's worse is the blocking # behavior inside Tick. # # In order to avoid blocking behavior, we need a scheme much like SchedulePeriodic emulation # where work to dispatch OnNext(n + 1) is delegated to a catch up loop in case OnNext(n) was # still running. Because SchedulePeriodic emulation exhibits such behavior in all cases, we # only need to deal with the overlap of OnNext(0L) with future periodic OnNext(n) dispatch # jobs. In the worst case where every callback takes longer than the deadline implied by the # period, the periodic job will just queue up work that's dispatched by the tail-recursive # catch up loop. In the best case, all work will be dispatched on the periodic scheduler. # # # We start with one tick pending because we're about to start doing OnNext(0L). # self.pendingTickCount.value = 1 d = SingleAssignmentDisposable() self.periodic = d d.disposable = scheduler.schedulePeriodicWithState( 1, self.parent.period, self.tock) try: self.observer.onNext(0) except Exception as e: d.dispose() raise e # # If the periodic scheduling job already ran before we finished dispatching the OnNext(0L) # call, we'll find pendingTickCount to be > 1. In this case, we need to catch up by dispatching # subsequent calls to OnNext as fast as possible, but without running a loop in order to ensure # fair play with the scheduler. So, we run a tail-recursive loop in CatchUp instead. # if self.pendingTickCount.dec() > 0: c = SingleAssignmentDisposable() c.disposable = scheduler.scheduleRecursiveWithState( 1, self.catchUp) return CompositeDisposable(d, c) return d
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(DelayTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.scheduler = self.parent.scheduler self.cancelTimer = SerialDisposable() self.gate = RLock() self.active = False # as soon as a value arrived self.running = False # on relative: True, on absolute: True after absolute time self.queue = deque() self.hasCompleted = False self.completeAt = 0 self.hasFailed = False self.exception = None self.delay = 0 self.startTime = self.scheduler.now() if self.parent.isAbsolute: self.ready = False self.cancelTimer.disposable = self.scheduler.scheduleWithAbsolute( self.parent.dueTime, self.start) else: self.ready = True self.delay = Scheduler.normalize(self.parent.dueTime) self.sourceSubscription = SingleAssignmentDisposable() self.sourceSubscription.disposable = self.parent.source.subscribeSafe( self) return CompositeDisposable(self.sourceSubscription, self.cancelTimer) def elapsed(self): return self.scheduler.now() - self.startTime def start(self): next = 0 shouldRun = False with self.gate: self.delay = self.elapsed() if len(self.queue) > 0: next = self.queue[0].interval for item in self.queue: item.interval += self.delay shouldRun = True self.active = True self.ready = True if shouldRun: self.cancelTimer.disposable = self.scheduler.scheduleRecursiveWithRelative( next, self.drainQueue) def onNext(self, value): next = self.elapsed() + self.delay shouldRun = False with self.gate: self.queue.append(Struct(value=value, interval=next)) shouldRun = self.ready and (not self.active) self.active = True if shouldRun: self.cancelTimer.disposable = self.scheduler.scheduleRecursiveWithRelative( self.delay, self.drainQueue) def onError(self, exception): self.sourceSubscription.dispose() shouldRun = False with self.gate: self.queue.clear() self.exception = exception self.hasFailed = True shouldRun = not self.running if shouldRun: self.observer.onError(exception) self.dispose() def onCompleted(self): self.sourceSubscription.dispose() next = self.elapsed() + self.delay shouldRun = False with self.gate: self.completeAt = next self.hasCompleted = True shouldRun = self.ready and (not self.active) self.active = True if shouldRun: self.cancelTimer.disposable = self.scheduler.scheduleRecursiveWithRelative( self.delay, self.drainQueue) def drainQueue(self, recurse): with self.gate: if self.hasFailed: return self.running = True # # The shouldYield flag was added to address TFS 487881: "Delay can be unfair". In the old # implementation, the loop below kept running while there was work for immediate dispatch, # potentially causing a long running work item on the target scheduler. With the addition # of long-running scheduling in Rx v2.0, we can check whether the scheduler supports this # interface and perform different processing (see LongRunningSink). To reduce the code churn in the old # loop code here, we set the shouldYield flag to true after the first dispatch iteration, # in order to break from the loop and enter the recursive scheduling path. shouldYield = False while True: hasFailed = False error = None value = None hasValue = False hasCompleted = False shouldRecurse = False recurseDueTime = 0 with self.gate: if self.hasFailed: error = self.exception hasFailed = True self.running = False else: now = self.elapsed() if len(self.queue) > 0: nextDue = self.queue[0].interval if nextDue <= now and not shouldYield: value = self.queue.popleft().value hasValue = True else: shouldRecurse = True recurseDueTime = Scheduler.normalize(nextDue - now) self.running = False elif self.hasCompleted: if self.completeAt <= now and not shouldYield: hasCompleted = True else: shouldRecurse = True recurseDueTime = Scheduler.normalize( self.completeAt - now) self.running = False else: self.running = False self.active = False # end with self.gate if hasValue: self.observer.onNext(value) shouldYield = True else: if hasCompleted: self.observer.onCompleted() self.dispose() elif hasFailed: self.observer.onError(error) self.dispose() elif shouldRecurse: recurse(recurseDueTime) return
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(SelectMany.Sink, self).__init__(observer, cancel) self.parent = parent self.index = -1 def run(self): self.gate = RLock() self.isStopped = False self.group = CompositeDisposable() self.sourceSubscription = SingleAssignmentDisposable() self.group.add(self.sourceSubscription) self.sourceSubscription.disposable = self.parent.source.subscribeSafe(self) return self.group def onNext(self, value): inner = None try: if self.parent.withIndex: self.index += 1 inner = self.parent.selectorOnNext(value, self.index) else: inner = self.parent.selectorOnNext(value) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() if isinstance(inner, Observable): self.subscribeInner(inner) return # iterable try: for current in inner: self.observer.onNext(current) except Exception as e: self.observer.onError(e) self.dispose() def onError(self, exception): if self.parent.selectorOnError != None: try: inner = None if self.parent.withIndex: self.index += 1 inner = self.parent.selectorOnError(exception, self.index) else: inner = self.parent.selectorOnError(exception) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() else: self.subscribeInner(inner) self.final() else: with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): if self.parent.selectorOnCompleted != None: try: inner = None if self.parent.withIndex: inner = self.parent.selectorOnCompleted(self.index) else: inner = self.parent.selectorOnCompleted() except Exception as e: with self.gate: self.observer.onError(e) self.dispose() return else: self.subscribeInner(inner) self.final() def final(self): self.isStopped = True if self.group.length == 1: # # Notice there can be a race between OnCompleted of the source and any # of the inner sequences, where both see _group.Count == 1, and one is # waiting for the lock. There won't be a double OnCompleted observation # though, because the call to Dispose silences the observer by swapping # in a NopObserver<T>. # with self.gate: self.observer.onCompleted() self.dispose() else: self.sourceSubscription.dispose() def subscribeInner(self, inner): innerSubscription = SingleAssignmentDisposable() self.group.add(innerSubscription) innerSubscription.disposable = inner.subscribeSafe(self.LockingObserver(self, innerSubscription)) class LockingObserver(Observer): def __init__(self, parent, subscription): self.parent = parent self.subscription = subscription def onNext(self, value): with self.parent.gate: self.parent.observer.onNext(value) def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.parent.group.remove(self.subscription) if self.parent.isStopped and self.parent.group.length == 1: # # Notice there can be a race between OnCompleted of the source and any # of the inner sequences, where both see _group.Count == 1, and one is # waiting for the lock. There won't be a double OnCompleted observation # though, because the call to Dispose silences the observer by swapping # in a NopObserver<T>. # with self.parent.gate: self.parent.observer.onCompleted() self.parent.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(SelectMany.Sink, self).__init__(observer, cancel) self.parent = parent self.index = -1 def run(self): self.gate = RLock() self.isStopped = False self.group = CompositeDisposable() self.sourceSubscription = SingleAssignmentDisposable() self.group.add(self.sourceSubscription) self.sourceSubscription.disposable = self.parent.source.subscribeSafe( self) return self.group def onNext(self, value): inner = None try: if self.parent.withIndex: self.index += 1 inner = self.parent.selectorOnNext(value, self.index) else: inner = self.parent.selectorOnNext(value) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() if isinstance(inner, Observable): self.subscribeInner(inner) return # iterable try: for current in inner: self.observer.onNext(current) except Exception as e: self.observer.onError(e) self.dispose() def onError(self, exception): if self.parent.selectorOnError != None: try: inner = None if self.parent.withIndex: self.index += 1 inner = self.parent.selectorOnError( exception, self.index) else: inner = self.parent.selectorOnError(exception) except Exception as e: with self.gate: self.observer.onError(e) self.dispose() else: self.subscribeInner(inner) self.final() else: with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): if self.parent.selectorOnCompleted != None: try: inner = None if self.parent.withIndex: inner = self.parent.selectorOnCompleted(self.index) else: inner = self.parent.selectorOnCompleted() except Exception as e: with self.gate: self.observer.onError(e) self.dispose() return else: self.subscribeInner(inner) self.final() def final(self): self.isStopped = True if self.group.length == 1: # # Notice there can be a race between OnCompleted of the source and any # of the inner sequences, where both see _group.Count == 1, and one is # waiting for the lock. There won't be a double OnCompleted observation # though, because the call to Dispose silences the observer by swapping # in a NopObserver<T>. # with self.gate: self.observer.onCompleted() self.dispose() else: self.sourceSubscription.dispose() def subscribeInner(self, inner): innerSubscription = SingleAssignmentDisposable() self.group.add(innerSubscription) innerSubscription.disposable = inner.subscribeSafe( self.LockingObserver(self, innerSubscription)) class LockingObserver(Observer): def __init__(self, parent, subscription): self.parent = parent self.subscription = subscription def onNext(self, value): with self.parent.gate: self.parent.observer.onNext(value) def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.parent.group.remove(self.subscription) if self.parent.isStopped and self.parent.group.length == 1: # # Notice there can be a race between OnCompleted of the source and any # of the inner sequences, where both see _group.Count == 1, and one is # waiting for the lock. There won't be a double OnCompleted observation # though, because the call to Dispose silences the observer by swapping # in a NopObserver<T>. # with self.parent.gate: self.parent.observer.onCompleted() self.parent.dispose()
class GetIterator(Observer): def __init__(self): self.queue = Queue() self.gate = Semaphore(0) self.subscription = SingleAssignmentDisposable() self.error = None self.done = False self.disposed = False def run(self, source): # [OK] Use of unsafe Subscribe: non-pretentious exact mirror with the dual GetEnumerator method. self.subscription.disposable = source.subscribe(self) return self def onNext(self, value): self.queue.put(value) self.gate.release() def onError(self, exception): self.error = exception self.subscription.dispose() self.gate.release() def onCompleted(self): self.done = True self.subscription.dispose() self.gate.release() def __iter__(self): return self def __next__(self): self.gate.acquire() if self.disposed: raise DisposedException() try: return self.queue.get(True, 0) except Empty: pass if self.error != None: raise self.error assert self.done self.gate.release() raise StopIteration() def dispose(self): self.subscription.dispose() self.disposed = True self.gate.release() class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(GetIterator.Sink, self).__init__(observer, cancel) self.parent = parent self.currentKey = None self.hasCurrentKey = False def onNext(self, value): try: key = self.parent.keySelector(value) except Exception as e: self.observer.onError(e) self.dispose() else: equal = False if self.hasCurrentKey: try: equal = self.currentKey == key except Exception as e: self.observer.onError(e) self.dispose() if not self.hasCurrentKey or not equal: self.hasCurrentKey = True self.currentKey = key self.observer.onNext(value) def onError(self, exception): self.observer.onError(exception) self.dispose() def onCompleted(self): self.observer.onCompleted() self.dispose()
class ConcurrentSink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(Merge.ConcurrentSink, self).__init__(observer, cancel) self.parent = parent def run(self): self.gate = RLock() self.q = Queue() self.isStopped = False self.activeCount = 0 self.group = CompositeDisposable() self.sourceSubscription = SingleAssignmentDisposable() self.group.add(self.sourceSubscription) self.sourceSubscription.disposable = self.parent.sources.subscribeSafe(self) return self.group def onNext(self, value): with self.gate: if self.activeCount < self.parent.maxConcurrency: self.activeCount += 1 self.subscribe(value) else: self.q.put_nowait(value) def onError(self, exception): with self.gate: self.observer.onError(exception) self.dispose() def onCompleted(self): with self.gate: self.isStopped = True if self.activeCount == 0: self.observer.onCompleted() self.dispose() else: self.sourceSubscription.dispose() def subscribe(self, innerSource): subscription = SingleAssignmentDisposable() self.group.add(subscription) subscription.disposable = innerSource.subscribeSafe(self.LockObserver(self, subscription)) class LockObserver(Observer): def __init__(self, parent, subscription): self.parent = parent self.subscription = subscription def onNext(self, value): with self.parent.gate: self.parent.observer.onNext(value) def onError(self, exception): with self.parent.gate: self.parent.observer.onError(exception) self.parent.dispose() def onCompleted(self): self.parent.group.remove(self.subscription) with self.parent.gate: if self.parent.q.qsize() > 0: s = self.q.get() self.parent.subscribe(s) else: self.parent.activeCount -= 1 if self.parent.isStopped and self.parent.activeCount == 0: self.parent.observer.onCompleted() self.parent.dispose()
class Sink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(DelayTime.Sink, self).__init__(observer, cancel) self.parent = parent def run(self): self.scheduler = self.parent.scheduler self.cancelTimer = SerialDisposable() self.gate = RLock() self.active = False # as soon as a value arrived self.running = False # on relative: True, on absolute: True after absolute time self.queue = deque() self.hasCompleted = False self.completeAt = 0 self.hasFailed = False self.exception = None self.delay = 0 self.startTime = self.scheduler.now() if self.parent.isAbsolute: self.ready = False self.cancelTimer.disposable = self.scheduler.scheduleWithAbsolute( self.parent.dueTime, self.start ) else: self.ready = True self.delay = Scheduler.normalize(self.parent.dueTime) self.sourceSubscription = SingleAssignmentDisposable() self.sourceSubscription.disposable = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.sourceSubscription, self.cancelTimer) def elapsed(self): return self.scheduler.now() - self.startTime def start(self): next = 0 shouldRun = False with self.gate: self.delay = self.elapsed() if len(self.queue) > 0: next = self.queue[0].interval for item in self.queue: item.interval += self.delay shouldRun = True self.active = True self.ready = True if shouldRun: self.cancelTimer.disposable = self.scheduler.scheduleRecursiveWithRelative( next, self.drainQueue ) def onNext(self, value): next = self.elapsed() + self.delay shouldRun = False with self.gate: self.queue.append(Struct(value=value, interval=next)) shouldRun = self.ready and (not self.active) self.active = True if shouldRun: self.cancelTimer.disposable = self.scheduler.scheduleRecursiveWithRelative( self.delay, self.drainQueue ) def onError(self, exception): self.sourceSubscription.dispose() shouldRun = False with self.gate: self.queue.clear() self.exception = exception self.hasFailed = True shouldRun = not self.running if shouldRun: self.observer.onError(exception) self.dispose() def onCompleted(self): self.sourceSubscription.dispose() next = self.elapsed() + self.delay shouldRun = False with self.gate: self.completeAt = next self.hasCompleted = True shouldRun = self.ready and (not self.active) self.active = True if shouldRun: self.cancelTimer.disposable = self.scheduler.scheduleRecursiveWithRelative( self.delay, self.drainQueue ) def drainQueue(self, recurse): with self.gate: if self.hasFailed: return self.running = True # # The shouldYield flag was added to address TFS 487881: "Delay can be unfair". In the old # implementation, the loop below kept running while there was work for immediate dispatch, # potentially causing a long running work item on the target scheduler. With the addition # of long-running scheduling in Rx v2.0, we can check whether the scheduler supports this # interface and perform different processing (see LongRunningSink). To reduce the code churn in the old # loop code here, we set the shouldYield flag to true after the first dispatch iteration, # in order to break from the loop and enter the recursive scheduling path. shouldYield = False while True: hasFailed = False error = None value = None hasValue = False hasCompleted = False shouldRecurse = False recurseDueTime = 0 with self.gate: if self.hasFailed: error = self.exception hasFailed = True self.running = False else: now = self.elapsed() if len(self.queue) > 0: nextDue = self.queue[0].interval if nextDue <= now and not shouldYield: value = self.queue.popleft().value hasValue = True else: shouldRecurse = True recurseDueTime = Scheduler.normalize(nextDue - now) self.running = False elif self.hasCompleted: if self.completeAt <= now and not shouldYield: hasCompleted = True else: shouldRecurse = True recurseDueTime = Scheduler.normalize(self.completeAt - now) self.running = False else: self.running = False self.active = False # end with self.gate if hasValue: self.observer.onNext(value) shouldYield = True else: if hasCompleted: self.observer.onCompleted() self.dispose() elif hasFailed: self.observer.onError(error) self.dispose() elif shouldRecurse: recurse(recurseDueTime) return