def subscribe(observer, scheduler=None): scheduler = scheduler or current_thread_scheduler subscription = SerialDisposable() cancelable = SerialDisposable() last_exception = [None] is_disposed = [] def action(action1, state=None): def on_error(exn): last_exception[0] = exn cancelable.disposable = scheduler.schedule(action) if is_disposed: return try: current = next(sources) except StopIteration: if last_exception[0]: observer.on_error(last_exception[0]) else: observer.on_completed() except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) else: d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = current.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler) cancelable.disposable = scheduler.schedule(action) def dispose(): is_disposed.append(True) return CompositeDisposable(subscription, cancelable, Disposable(dispose))
def test_map_with_index_dispose_inside_mapper(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(100, 4), on_next(200, 3), on_next(500, 2), on_next(600, 1)) invoked = [0] results = scheduler.create_observer() d = SerialDisposable() def projection(x, index): invoked[0] += 1 if scheduler.clock > 400: d.dispose() return x + index * 10 d.disposable = xs.pipe(map_indexed(projection)).subscribe(results) def action(scheduler, state): return d.dispose() scheduler.schedule_absolute(disposed, action) scheduler.start() assert results.messages == [on_next(100, 4), on_next(200, 13)] assert xs.subscriptions == [subscribe(0, 500)] assert invoked[0] == 3
def test_starmap_dispose_inside_mapper(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( # 100 create on_next(110, (1, 10)), # 200 subscribe on_next(210, (2, 20)), on_next(310, (3, 30)), on_next(410, (4, 40))) results = scheduler.create_observer() d = SerialDisposable() invoked = [0] def mapper(x, y): invoked[0] += 1 if scheduler.clock > 250: d.dispose() return x + y d.disposable = xs.pipe(ops.starmap(mapper)).subscribe( results, scheduler) def action(scheduler, state): return d.dispose() scheduler.schedule_absolute(ReactiveTest.disposed, action) scheduler.start() assert results.messages == [on_next(110, 11), on_next(210, 22)] assert xs.subscriptions == [ReactiveTest.subscribe(0, 310)] assert invoked[0] == 3
def subscribe(observer, scheduler=None): scheduler = scheduler or current_thread_scheduler subscription = SerialDisposable() cancelable = SerialDisposable() def action(scheduler, state=None): try: source = next(sources) except StopIteration: observer.on_completed() return # Allow source to be a factory method taking an error source = source(state) if callable(source) else source current = rx.from_future(source) if is_future(source) else source d = SingleAssignmentDisposable() subscription.disposable = d def on_resume(state=None): scheduler.schedule(action, state) d.disposable = current.subscribe_(observer.on_next, on_resume, on_resume, scheduler) cancelable.disposable = scheduler.schedule(action) return CompositeDisposable(subscription, cancelable)
def subscribe(observer, scheduler=None): scheduler = scheduler or current_thread_scheduler subscription = SerialDisposable() cancelable = SerialDisposable() last_exception = [None] is_disposed = [] def action(action1, state=None): def on_error(exn): last_exception[0] = exn cancelable.disposable = scheduler.schedule(action) if is_disposed: return try: current = next(sources_) except StopIteration: if last_exception[0]: observer.on_error(last_exception[0]) else: observer.on_completed() except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) else: d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = current.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler) cancelable.disposable = scheduler.schedule(action) def dispose(): is_disposed.append(True) return CompositeDisposable(subscription, cancelable, Disposable(dispose))
def subscribe(observer, scheduler=None): d1 = SingleAssignmentDisposable() subscription = SerialDisposable() subscription.disposable = d1 def on_error(exception): try: result = handler(exception, source) except Exception as ex: # By design. pylint: disable=W0703 observer.on_error(ex) return result = rx.from_future(result) if is_future(result) else result d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = result.subscribe(observer, scheduler=scheduler) d1.disposable = source.subscribe_( observer.on_next, on_error, observer.on_completed, scheduler ) return subscription
def subscribe(observer, scheduler=None): scheduler = scheduler or current_thread_scheduler subscription = SerialDisposable() cancelable = SerialDisposable() is_disposed = False def action(action1, state=None): nonlocal is_disposed if is_disposed: return def on_completed(): cancelable.disposable = scheduler.schedule(action) try: current = next(sources_) except StopIteration: observer.on_completed() except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) else: d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = current.subscribe_(observer.on_next, observer.on_error, on_completed, scheduler) cancelable.disposable = scheduler.schedule(action) def dispose(): nonlocal is_disposed is_disposed = True return CompositeDisposable(subscription, cancelable, Disposable(dispose))
def test_map_disposeinsidemapper(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(100, 1), on_next(200, 2), on_next(500, 3), on_next(600, 4)) results = scheduler.create_observer() d = SerialDisposable() invoked = [0] def projection(x, *args, **kw): invoked[0] += 1 if scheduler.clock > 400: d.dispose() return x d.disposable = xs.pipe( map(projection) ).subscribe(results, scheduler) def action(scheduler, state): return d.dispose() scheduler.schedule_absolute(ReactiveTest.disposed, action) scheduler.start() assert results.messages == [on_next(100, 1), on_next(200, 2)] assert xs.subscriptions == [ReactiveTest.subscribe(0, 500)] assert invoked[0] == 3
def test_map_disposeinsidemapper(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(100, 1), on_next(200, 2), on_next(500, 3), on_next(600, 4)) results = scheduler.create_observer() d = SerialDisposable() invoked = [0] def projection(x, *args, **kw): invoked[0] += 1 if scheduler.clock > 400: d.dispose() return x d.disposable = xs.pipe(map(projection)).subscribe(results, scheduler) def action(scheduler, state): return d.dispose() scheduler.schedule_absolute(ReactiveTest.disposed, action) scheduler.start() assert results.messages == [on_next(100, 1), on_next(200, 2)] assert xs.subscriptions == [ReactiveTest.subscribe(0, 500)] assert invoked[0] == 3
def subscribe(observer, scheduler=None): scheduler = scheduler or current_thread_scheduler subscription = SerialDisposable() cancelable = SerialDisposable() def action(scheduler, state=None): try: source = next(sources_) except StopIteration: observer.on_completed() return # Allow source to be a factory method taking an error source = source(state) if callable(source) else source current = rx.from_future(source) if is_future(source) else source d = SingleAssignmentDisposable() subscription.disposable = d def on_resume(state=None): scheduler.schedule(action, state) d.disposable = current.subscribe_(observer.on_next, on_resume, on_resume, scheduler) cancelable.disposable = scheduler.schedule(action) return CompositeDisposable(subscription, cancelable)
def subscribe(observer, _=None): m = SingleAssignmentDisposable() d = SerialDisposable() d.disposable = m def action(scheduler, state): d.disposable = ScheduledDisposable(scheduler, source.subscribe(observer)) m.disposable = scheduler.schedule(action) return d
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 __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 subscribe(observer): m = SingleAssignmentDisposable() d = SerialDisposable() d.disposable = m def action(): d.disposable = SchedulerDisposable(scheduler, self.subscribeSafe(observer)) m.disposable = scheduler.schedule(action) return d
def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton( ) if isinstance(duetime, datetime): scheduler_method = _scheduler.schedule_absolute else: scheduler_method = _scheduler.schedule_relative switched = [False] _id = [0] original = SingleAssignmentDisposable() subscription = SerialDisposable() timer = SerialDisposable() subscription.disposable = original def create_timer(): my_id = _id[0] def action(scheduler, state=None): switched[0] = (_id[0] == my_id) timer_wins = switched[0] if timer_wins: subscription.disposable = obs.subscribe( observer, scheduler=scheduler) timer.disposable = scheduler_method(duetime, action) create_timer() def on_next(value): send_wins = not switched[0] if send_wins: _id[0] += 1 observer.on_next(value) create_timer() def on_error(error): on_error_wins = not switched[0] if on_error_wins: _id[0] += 1 observer.on_error(error) def on_completed(): on_completed_wins = not switched[0] if on_completed_wins: _id[0] += 1 observer.on_completed() original.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler_) return CompositeDisposable(subscription, timer)
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()
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 subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or timeout_scheduler if isinstance(duetime, datetime): scheduler_method = _scheduler.schedule_absolute else: scheduler_method = _scheduler.schedule_relative switched = [False] _id = [0] original = SingleAssignmentDisposable() subscription = SerialDisposable() timer = SerialDisposable() subscription.disposable = original def create_timer(): my_id = _id[0] def action(scheduler, state=None): switched[0] = (_id[0] == my_id) timer_wins = switched[0] if timer_wins: subscription.disposable = other.subscribe(observer, scheduler=scheduler) timer.disposable = scheduler_method(duetime, action) create_timer() def on_next(value): send_wins = not switched[0] if send_wins: _id[0] += 1 observer.on_next(value) create_timer() def on_error(error): on_error_wins = not switched[0] if on_error_wins: _id[0] += 1 observer.on_error(error) def on_completed(): on_completed_wins = not switched[0] if on_completed_wins: _id[0] += 1 observer.on_completed() original.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler_) return CompositeDisposable(subscription, timer)
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 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 subscribe(observer, scheduler=None): delays = CompositeDisposable() at_end = [False] def done(): if (at_end[0] and delays.length == 0): observer.on_completed() subscription = SerialDisposable() def start(): def on_next(x): try: delay = mapper(x) except Exception as error: observer.on_error(error) return d = SingleAssignmentDisposable() delays.add(d) def on_next(_): observer.on_next(x) delays.remove(d) done() def on_completed(): observer.on_next(x) delays.remove(d) done() d.disposable = delay.subscribe_(on_next, observer.on_error, on_completed, scheduler) def on_completed(): at_end[0] = True subscription.dispose() done() subscription.disposable = source.subscribe_(on_next, observer.on_error, on_completed, scheduler) if not sub_delay: start() else: subscription.disposable(sub_delay.subscribe_( lambda _: start(), observer.on_error, start)) return CompositeDisposable(subscription, delays)
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 run(self, sources): self.isDisposed = False self.subscription = SerialDisposable() self.gate = AsyncLock() self.stack = [] self.length = [] self.stack.append(iter(sources)) try: length = len(sources) except TypeError: self.length.append(-1) else: self.length.append(length) def scheduled(continuation): self.recurse = continuation self.gate.wait(self.moveNext) cancel = Scheduler.tailRecursion.scheduleRecursive(scheduled) return CompositeDisposable( self.subscription, cancel, Disposable.create(lambda: self.gate.wait(self.dispose)))
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 subscribe(observer, scheduler=None) -> Disposable: cancelable = SerialDisposable() has_value = [False] value = [None] _id = [0] def on_next(x): throttle = None try: throttle = throttle_duration_mapper(x) except Exception as e: # pylint: disable=broad-except observer.on_error(e) return has_value[0] = True value[0] = x _id[0] += 1 current_id = _id[0] d = SingleAssignmentDisposable() cancelable.disposable = d def on_next(x: Any) -> None: if has_value[0] and _id[0] == current_id: observer.on_next(value[0]) has_value[0] = False d.dispose() def on_completed() -> None: if has_value[0] and _id[0] == current_id: observer.on_next(value[0]) has_value[0] = False d.dispose() d.disposable = throttle.subscribe_(on_next, observer.on_error, on_completed, scheduler=scheduler) def on_error(e) -> None: cancelable.dispose() observer.on_error(e) has_value[0] = False _id[0] += 1 def on_completed() -> None: cancelable.dispose() if has_value[0]: observer.on_next(value[0]) observer.on_completed() has_value[0] = False _id[0] += 1 subscription = source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) return CompositeDisposable(subscription, cancelable)
def test_return_disposed_after_next(self): scheduler = TestScheduler() d = SerialDisposable() xs = rx.return_value(42) results = scheduler.create_observer() def action(scheduler, state): def on_next(x): d.dispose() results.on_next(x) def on_error(e): results.on_error(e) def on_completed(): results.on_completed() d.disposable = xs.subscribe(on_next, on_error, on_completed, scheduler=scheduler) return d.disposable scheduler.schedule_absolute(100, action) scheduler.start() assert results.messages == [on_next(100, 42)]
def run(self): self.subscription = SerialDisposable() self.timer = SerialDisposable() original = SingleAssignmentDisposable() self.subscription.disposable = original self.gate = RLock() self.currentId = 0 self.switched = False self.createTimer() original.disposable = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.subscription, self.timer)
def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() n = [0] s = [None] timer_d = SerialDisposable() window_id = [0] group_disposable = CompositeDisposable(timer_d) ref_count_disposable = RefCountDisposable(group_disposable) def create_timer(_id): m = SingleAssignmentDisposable() timer_d.disposable = m def action(scheduler, state): if _id != window_id[0]: return n[0] = 0 window_id[0] += 1 new_id = window_id[0] s[0].on_completed() s[0] = Subject() observer.on_next(add_ref(s[0], ref_count_disposable)) create_timer(new_id) m.disposable = _scheduler.schedule_relative(timespan, action) s[0] = Subject() observer.on_next(add_ref(s[0], ref_count_disposable)) create_timer(0) def on_next(x): new_window = False new_id = 0 s[0].on_next(x) n[0] += 1 if n[0] == count: new_window = True n[0] = 0 window_id[0] += 1 new_id = window_id[0] s[0].on_completed() s[0] = Subject() observer.on_next(add_ref(s[0], ref_count_disposable)) if new_window: create_timer(new_id) def on_error(e): s[0].on_error(e) observer.on_error(e) def on_completed(): s[0].on_completed() observer.on_completed() group_disposable.add(source.subscribe_(on_next, on_error, on_completed, scheduler_)) return ref_count_disposable
def run(self): self.subscription = SerialDisposable() d = SingleAssignmentDisposable() self.subscription.disposable = d d.disposable = self.parent.source.subscribeSafe(self) return self.subscription
def subscribe(observer, scheduler=None): scheduler = scheduler or immediate_scheduler queue = [] m = SerialDisposable() d = CompositeDisposable(m) active_count = [0] is_acquired = [False] def ensure_active(): is_owner = False if queue: is_owner = not is_acquired[0] is_acquired[0] = True def action(scheduler, state): if queue: work = queue.pop(0) else: is_acquired[0] = False return sad = SingleAssignmentDisposable() d.add(sad) def on_next(value): observer.on_next(value) result = None try: result = mapper(value) except Exception as ex: observer.on_error(ex) return queue.append(result) active_count[0] += 1 ensure_active() def on_complete(): d.remove(sad) active_count[0] -= 1 if active_count[0] == 0: observer.on_completed() sad.disposable = work.subscribe_(on_next, observer.on_error, on_complete, scheduler) m.disposable = scheduler.schedule(action) if is_owner: m.disposable = scheduler.schedule(action) queue.append(source) active_count[0] += 1 ensure_active() return d
def schedule_periodic(self, period: typing.RelativeTime, action: ScheduledPeriodicAction, state: TState = None) -> typing.Disposable: """Schedules a periodic piece of work. Args: period: Period in seconds or timedelta for running the work periodically. action: Action to be executed. state: [Optional] Initial state passed to the action upon the first iteration. Returns: The disposable object used to cancel the scheduled recurring action (best effort).""" disp = SerialDisposable() def invoke_action(scheduler: typing.Scheduler, _: TState) -> Optional[Disposable]: nonlocal state if disp.is_disposed: return None disp.disposable = self.schedule_relative(period, invoke_action, None) try: new_state = action(state) except Exception: disp.dispose() raise if state is not None: state = new_state return None disp.disposable = self.schedule_relative(period, invoke_action, state) return disp
def subscribe(observer, scheduler_=None): _scheduler = scheduler_ or CurrentThreadScheduler.singleton() subscription = SerialDisposable() cancelable = SerialDisposable() last_exception = None is_disposed = False def action(action1, state=None): def on_error(exn): nonlocal last_exception last_exception = exn cancelable.disposable = _scheduler.schedule(action) if is_disposed: return try: current = next(sources_) except StopIteration: if last_exception: observer.on_error(last_exception) else: observer.on_completed() except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) else: d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = current.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler_) cancelable.disposable = _scheduler.schedule(action) def dispose(): nonlocal is_disposed is_disposed = True return CompositeDisposable(subscription, cancelable, Disposable(dispose))
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]
def test_mutabledisposable_replacebeforedispose(): disp1 = [False] disp2 = [False] m = SerialDisposable() def action1(): disp1[0] = True d1 = Disposable(action1) m.disposable = d1 assert d1 == m.disposable assert not disp1[0] def action2(): disp2[0] = True d2 = Disposable(action2) m.disposable = d2 assert d2 == m.disposable assert disp1[0] assert not disp2[0]
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 run(self): self.gate = RLock() self.list = [] self.n = 0 self.windowId = 0 self.timerDisposable = SerialDisposable() self.createTimer(0) subscription = self.parent.source.subscribeSafe(self) return CompositeDisposable(self.timerDisposable, subscription)
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 subscribe(observer, scheduler=None): inner_subscription = SerialDisposable() has_latest = [False] is_stopped = [False] latest = [0] def on_next(inner_source: Union[Observable, Future]): nonlocal source d = SingleAssignmentDisposable() with source.lock: latest[0] += 1 _id = latest[0] has_latest[0] = True inner_subscription.disposable = d # Check if Future or Observable if is_future(inner_source): obs = from_future(cast(Future, inner_source)) else: obs = cast(Observable, inner_source) def on_next(x: Any) -> None: if latest[0] == _id: observer.on_next(x) def on_error(e: Exception) -> None: if latest[0] == _id: observer.on_error(e) def on_completed() -> None: if latest[0] == _id: has_latest[0] = False if is_stopped[0]: observer.on_completed() d.disposable = obs.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) def on_completed() -> None: is_stopped[0] = True if not has_latest[0]: observer.on_completed() subscription = source.subscribe_(on_next, observer.on_error, on_completed, scheduler=scheduler) return CompositeDisposable(subscription, inner_subscription)
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()
def test_starmap_dispose_inside_mapper(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( # 100 create on_next(110, (1, 10)), # 200 subscribe on_next(210, (2, 20)), on_next(310, (3, 30)), on_next(410, (4, 40))) results = scheduler.create_observer() d = SerialDisposable() invoked = [0] def mapper(x, y): invoked[0] += 1 if scheduler._clock > 250: d.dispose() return x + y d.disposable = xs.pipe( ops.starmap(mapper) ).subscribe(results, scheduler) def action(scheduler, state): return d.dispose() scheduler.schedule_absolute(ReactiveTest.disposed, action) scheduler.start() assert results.messages == [ on_next(110, 11), on_next(210, 22)] assert xs.subscriptions == [ReactiveTest.subscribe(0, 310)] assert invoked[0] == 3
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 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 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 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()
def subscribe(observer, scheduler=None): subscription = SerialDisposable() timer = SerialDisposable() original = SingleAssignmentDisposable() subscription.disposable = original switched = False _id = [0] def set_timer(timeout: Observable) -> None: my_id = _id[0] def timer_wins(): return _id[0] == my_id d = SingleAssignmentDisposable() timer.disposable = d def on_next(x): if timer_wins(): subscription.disposable = other.subscribe(observer, scheduler=scheduler) d.dispose() def on_error(e): if timer_wins(): observer.on_error(e) def on_completed(): if timer_wins(): subscription.disposable = other.subscribe(observer) d.disposable = timeout.subscribe_(on_next, on_error, on_completed, scheduler) set_timer(first_timeout) def observer_wins(): res = not switched if res: _id[0] += 1 return res def on_next(x): if observer_wins(): observer.on_next(x) timeout = None try: timeout = timeout_duration_mapper(x) except Exception as e: observer.on_error(e) return set_timer(timeout) def on_error(error): if observer_wins(): observer.on_error(error) def on_completed(): if observer_wins(): observer.on_completed() original.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler) return CompositeDisposable(subscription, timer)
def __init__(self, interval, action): super(PeriodicTimer, self).__init__() self.interval = interval self.action = action self.timerDisposable = SerialDisposable()
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