class EnumeratingSink(Observer): def __init__(self, onNext, done): self.onNextAction = onNext self.doneAction = done self.index = 0 self.stopped = Atomic(False) self.exception = None def onNext(self, value): if not self.stopped.value: try: self.onNextAction(value, self.index) self.index += 1 except Exception as e: self.onError(e) def onError(self, exception): if not self.stopped.exchange(True): self.exception = exception self.doneAction() def onCompleted(self): if not self.stopped.exchange(True): self.doneAction()
def __init__(self, onNext, done): self.onNextAction = onNext self.doneAction = done self.index = 0 self.stopped = Atomic(False) self.exception = None
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)
class Sink(Disposable): """Base class for implementation of query operators, providing a lightweight sink that can be disposed to mute the outgoing observer.""" def __init__(self, observer, cancel): super(Sink, self).__init__() self.observer = observer self.cancel = Atomic(cancel) def dispose(self): self.observer = NoopObserver.instance cancel = self.cancel.exchange(None) if cancel != None: cancel.dispose() class Forewarder(Observer): def __init__(self, foreward): self.foreward = foreward def onNext(self, value): self.foreward.observer.onNext(value) def onError(self, exception): self.foreward.observer.onError(exception) self.foreward.dispose() def onCompleted(self): self.foreward.observer.onCompleted() self.foreward.dispose() def getForewarder(self): return self.Forewarder(self)
class InnerDisposable(Disposable): def __init__(self, parent): self.parent = Atomic(parent) def dispose(self): parent = self.parent.exchange(None) if parent != None: parent.release()
class ObserverBase(Cancelable, Observer): """Abstract base class for implementations of the IObserver interface. This base class enforces the grammar of observers where OnError and OnCompleted are terminal messages.""" def __init__(self): super(ObserverBase, self).__init__() self.isStopped = Atomic(False, self.lock) def onNext(self, value): with self.lock: if self.isStopped.value: return self.onNextCore(value) def onError(self, exception): if not self.isStopped.exchange(True): self.onErrorCore(exception) def onCompleted(self): if not self.isStopped.exchange(True): self.onCompletedCore() def dispose(self): self.isStopped.value = True def fail(self, exception): if self.isStopped.exchange(True): # isStopped was already true return False else: self.onErrorCore(exception) return True def onNextCore(self, value): raise NotImplementedError() def onErrorCore(self, exception): raise NotImplementedError() def onCompletedCore(self): raise NotImplementedError()
class Subscription(Disposable): def __init__(self, subject, observer): self.subject = subject self.observer = Atomic(observer) def dispose(self): old = self.observer.exchange(None) if old != None: self.subject.unsubscribe(old) self.subject = None
class CheckedObserver(Observer): IDLE = 0 BUSY = 1 DONE = 2 def __init__(self, observer): super(CheckedObserver, self).__init__() self.observer = observer self.state = Atomic(CheckedObserver.IDLE) def onNext(self, value): self.checkAccess() try: self.observer.onNext(value) finally: self.state.value = CheckedObserver.IDLE def onError(self, exception): self.checkAccess() try: self.observer.onError(exception) finally: self.state.value = CheckedObserver.DONE def onCompleted(self): self.checkAccess() try: self.observer.onCompleted() finally: self.state.value = CheckedObserver.DONE def checkAccess(self): old = self.state.compareExchange(CheckedObserver.BUSY, CheckedObserver.IDLE) if old == CheckedObserver.BUSY: raise Exception("This observer is currently busy") elif old == CheckedObserver.DONE: raise Exception("This observer already terminated")
class ObserveOnObserver(ScheduledObserver): def __init__(self, scheduler, observer, cancel): super(ObserveOnObserver, self).__init__(scheduler, observer) self.cancel = Atomic(cancel, self.lock) def onNextCore(self, value): super(ObserveOnObserver, self).onNextCore(value) self.ensureActive() def onErrorCore(self, exception): super(ObserveOnObserver, self).onErrorCore(exception) self.ensureActive() def onCompletedCore(self): super(ObserveOnObserver, self).onCompletedCore() self.ensureActive() def dispose(self): super(ObserveOnObserver, self).dispose() old = self.cancel.exchange(None) if old != None: old.dispose()
def __init__(self, observer): super(CheckedObserver, self).__init__() self.observer = observer self.state = Atomic(CheckedObserver.IDLE)
class Subject(Observable, Observer): def __init__(self): super(Subject, self).__init__() self.isDisposed = False self.isStopped = False self.exception = None self.observer = Atomic(NoopObserver.instance) def onCompleted(self): old = None new = DoneObserver.completed while True: old = self.observer.value if old is DoneObserver.completed or isinstance(old, DoneObserver): break current = self.observer.compareExchange(new, old) if old is current: break old.onCompleted() def onError(self, exception): old = None new = DoneObserver(exception) while True: old = self.observer.value if old is DoneObserver.completed or isinstance(old, DoneObserver): break current = self.observer.compareExchange(new, old) if old is current: break old.onError(exception) def onNext(self, value): self.observer.value.onNext(value) class Subscription(Disposable): def __init__(self, subject, observer): self.subject = subject self.observer = Atomic(observer) def dispose(self): old = self.observer.exchange(None) if old != None: self.subject.unsubscribe(old) self.subject = None def subscribeCore(self, observer): old = None new = None while True: old = self.observer.value if old is DisposedObserver.instance: raise Exception("Object has been disposed") if old is DoneObserver.completed: observer.onCompleted() return Disposable.empty() if isinstance(old, DoneObserver): observer.onError(old.exception) return Disposable.empty() if old is NoopObserver.instance: new = observer else: if isinstance(old, ListObserver): new = old.add(observer) else: new = ListObserver((old, observer)) current = self.observer.compareExchange(new, old) if old is current: break return self.Subscription(self, observer) def unsubscribe(self, observer): old = None new = None while True: old = self.observer.value if old is DisposedObserver.instance or isinstance(old, DoneObserver): return if isinstance(old, ListObserver): new = old.remove(observer) elif observer is not old: return else: new = NoopObserver.instance current = self.observer.compareExchange(new, old) if old is current: return def dispose(self): self.observer.exchange(NoopObserver.instance) @staticmethod def create(observer, observable): return AnonymousSubject(observer, observable) @staticmethod def synchronize(subject, scheduler=None): if scheduler == None: return AnonymousSubject(Observer.synchronize(subject), subject) else: return AnonymousSubject(Observer.synchronize(subject), subject.observeOn(scheduler))
def __init__(self, parent, observer, cancel): super(SkipUntilTime.Sink, self).__init__(observer, cancel) self.parent = parent self.open = Atomic(False)
def __init__(self): super(Subject, self).__init__() self.isDisposed = False self.isStopped = False self.exception = None self.observer = Atomic(NoopObserver.instance)
def __init__(self, parent): self.parent = Atomic(parent)
def __init__(self): super(ObserverBase, self).__init__() self.isStopped = Atomic(False, self.lock)
class Subject(Observable, Observer): def __init__(self): super(Subject, self).__init__() self.isDisposed = False self.isStopped = False self.exception = None self.observer = Atomic(NoopObserver.instance) def onCompleted(self): old = None new = DoneObserver.completed while True: old = self.observer.value if old is DoneObserver.completed or isinstance(old, DoneObserver): break current = self.observer.compareExchange(new, old) if old is current: break old.onCompleted() def onError(self, exception): old = None new = DoneObserver(exception) while True: old = self.observer.value if old is DoneObserver.completed or isinstance(old, DoneObserver): break current = self.observer.compareExchange(new, old) if old is current: break old.onError(exception) def onNext(self, value): self.observer.value.onNext(value) class Subscription(Disposable): def __init__(self, subject, observer): self.subject = subject self.observer = Atomic(observer) def dispose(self): old = self.observer.exchange(None) if old != None: self.subject.unsubscribe(old) self.subject = None def subscribeCore(self, observer): old = None new = None while True: old = self.observer.value if old is DisposedObserver.instance: raise Exception("Object has been disposed") if old is DoneObserver.completed: observer.onCompleted() return Disposable.empty() if isinstance(old, DoneObserver): observer.onError(old.exception) return Disposable.empty() if old is NoopObserver.instance: new = observer else: if isinstance(old, ListObserver): new = old.add(observer) else: new = ListObserver((old, observer)) current = self.observer.compareExchange(new, old) if old is current: break return self.Subscription(self, observer) def unsubscribe(self, observer): old = None new = None while True: old = self.observer.value if old is DisposedObserver.instance or isinstance( old, DoneObserver): return if isinstance(old, ListObserver): new = old.remove(observer) elif observer is not old: return else: new = NoopObserver.instance current = self.observer.compareExchange(new, old) if old is current: return def dispose(self): self.observer.exchange(NoopObserver.instance) @staticmethod def create(observer, observable): return AnonymousSubject(observer, observable) @staticmethod def synchronize(subject, scheduler=None): if scheduler == None: return AnonymousSubject(Observer.synchronize(subject), subject) else: return AnonymousSubject(Observer.synchronize(subject), subject.observeOn(scheduler))
def __init__(self, parent, observer, cancel): super(Timer.PeriodSink, self).__init__(observer, cancel) self.parent = parent self.pendingTickCount = Atomic(0)
def __init__(self, observer, cancel): super(Sink, self).__init__() self.observer = observer self.cancel = Atomic(cancel)
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 PeriodSink(rx.linq.sink.Sink): def __init__(self, parent, observer, cancel): super(Timer.PeriodSink, self).__init__(observer, cancel) self.parent = parent self.pendingTickCount = Atomic(0) def run(self): if self.parent.isAbsolute: return self.parent.scheduler.scheduleWithAbsoluteAndState( None, self.parent.dueTime, self.invokeStart) else: dueTime = self.parent.dueTime if dueTime == self.parent.period: return self.parent.scheduler.schedulePeriodicWithState( 0, self.parent.period, self.tick) return self.parent.scheduler.scheduleWithRelativeAndState( None, dueTime, self.invokeStart) def tick(self, count): self.observer.onNext(count) return count + 1 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 def tock(self, count): if self.pendingTickCount.inc() == 1: self.observer.onNext(count) self.pendingTickCount.dec() return count + 1 def catchUp(self, count, recurse): try: self.observer.onNext(count) except Exception as e: self.periodic.dispose() raise e # # We can simply bail out if we decreased the tick count to 0. In that case, the Tock # method will take over when it sees the 0 -> 1 transition. # if self.pendingTickCount.dec() > 0: recurse(count + 1)
def __init__(self): super(Cancelable, self).__init__() self._isDisposed = Atomic(False)
def __init__(self, subject, observer): self.subject = subject self.observer = Atomic(observer)
def __init__(self, scheduler, observer, cancel): super(ObserveOnObserver, self).__init__(scheduler, observer) self.cancel = Atomic(cancel, self.lock)