def signal_on_next(a1, a2): prev_last_ack = last_ack[0] if isinstance(prev_last_ack, Continue): new_last_ack = raw_on_next(a1, a2) elif isinstance(prev_last_ack, Stop): return stop_ack else: def _(v, _): if isinstance(v, Continue): with self.lock: ack = raw_on_next(a1, a2) return ack else: return stop_ack ack = Ack() prev_last_ack.flat_map(_).subscribe(ack) new_last_ack = ack last_ack[0] = new_last_ack if isinstance(new_last_ack, Continue) or isinstance( new_last_ack, Stop): continue_p[0].on_next(new_last_ack) continue_p[0].on_completed() else: new_last_ack.subscribe(continue_p[0]) # acknowledgment used by input that receives first continue_p[0] = Ack() return new_last_ack
def on_next(self, elem): if not self.is_connected: def __(v, _): if isinstance(v, Continue): ack = self.underlying.on_next(elem) if isinstance(ack, Continue): return rx.Observable.just(ack) elif isinstance(ack, Stop): raise NotImplementedError else: return ack else: return Stop() new_ack = Ack() self.connected_ack \ .observe_on(self.scheduler) \ .flat_map(__) \ .subscribe(new_ack) self.connected_ack = new_ack return self.connected_ack elif not self.was_canceled: ack = self.underlying.on_next(elem) return ack else: return Stop()
def push_on_next(self, elem, last_to_push: int = None): if self.upstream_is_complete or self.downstream_is_complete: return Stop() else: if last_to_push is None: with self.lock: to_push = self.items_to_push self.items_to_push += 1 else: to_push = last_to_push if self.back_pressured is None: if to_push < self.buffer_size: self.queue.put(item=elem) self.push_to_consumer(to_push) return continue_ack else: ack = Ack() with self.lock: self.back_pressured = ack self.queue.put(item=elem) self.push_to_consumer(to_push) return ack else: self.queue.put(item=elem) self.push_to_consumer(to_push) return self.back_pressured
def on_next(self, v): self.received.append(v) if 0 < self.immediate_continue: self.immediate_continue -= 1 return Continue() else: self.ack = Ack() return self.ack
def on_next(self, value): with self.lock: # concurrent situation with acknowledgment in inner subscription or new subscriptions # empty inactive subscriptions; they are added to the list once they reach the top of the buffer again inactive_subsriptions = self.inactive_subsriptions self.inactive_subsriptions = [] # add item to buffer self.buffer.append(OnNext(value)) # current ack is used by subscriptions that weren't inactive, but reached the top of the buffer current_ack = Ack() self.current_ack = current_ack is_done = self.is_done if is_done: return stop_ack def gen_inner_ack(): # send notification to inactive subscriptions for inner_subscription in inactive_subsriptions: inner_ack = inner_subscription.notify_on_next(value) yield inner_ack inner_ack_list = list(gen_inner_ack()) continue_ack = [ ack for ack in inner_ack_list if isinstance(ack, Continue) ] if 0 < len(continue_ack): # return any Continue ack return continue_ack[0] else: # return merged acknowledgments from inner subscriptions ack_list = [current_ack] + inner_ack_list upper_ack = Ack() rx.Observable.merge(*ack_list).first().subscribe(upper_ack) return upper_ack
def on_next(self, inner_left): ack = Ack() has_right_elem = False right_elem = None with source.lock: if isinstance(state[0], source.WaitForRightOrInner): # not much to do state_typed: source.WaitForRightOrInner = state[0] state[0] = source.WaitForRight( inner_left_elem=inner_left, left_ack=state_typed.left_ack, inner_left_ack=ack, left_elem=source.selector_left(left_elem)) elif isinstance(state[0], source.Active): # send zipped item to observer state_typed: source.Active = state[0] has_right_elem = True right_elem = state_typed.right_elem state[0] = source.Active( left_ack=state_typed.left_ack, right_elem=state_typed.right_elem, right_ack=state_typed.right_ack, upper_ack=ack) elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError if has_right_elem: # send triple to observer zipped_elem = source.selector( source.selector_left(left_elem), right_elem, inner_left) upper_ack = observer.on_next(zipped_elem) # race condition with on_completed if isinstance(state[0], source.Active): typed_state: source.Active = state[0] typed_state.upper_ack = upper_ack if isinstance(upper_ack, Stop): with source.lock: state[0] = source.Completed() else: def _(v): if isinstance(v, Stop): with source.lock: state[0] = source.Completed() upper_ack.observe_on(scheduler).subscribe(_) return upper_ack else: return ack
def __init__(self, underlying: Observer, scheduler: SchedulerBase): self.underlying = underlying self.scheduler = scheduler self.root_ack = Ack() self.connected_ack = self.root_ack self.connected_ref = None self.is_connected = False self.is_connected_started = False self.scheduled_done = False self.schedule_error = None self.was_canceled = False self.queue = Queue() self.lock = config['concurrency'].RLock()
def notify_on_next(self, value) -> Ack: # inner subscription gets only notified if all items from buffer are sent and ack received with self.source.lock: # increase current index self.source.current_index[self] += 1 current_index = self.source.current_index[self] ack = self.observer.on_next(value) if isinstance(ack, Continue): self.source.inactive_subsriptions.append(self) return ack elif isinstance(ack, Stop): # with self.source.lock: # del self.source.current_index[self] # if len(self.source.current_ack) == 0: # self.source.is_done = True self.signal_stop() return ack else: inner_ack = Ack() def _(v): if isinstance(v, Continue): with self.source.lock: if current_index + 1 < self.source.buffer.last_idx: has_elem = True else: # no new item has been added since call to 'notify_on_next' has_elem = False self.source.inactive_subsriptions.append(self) if has_elem: disposable = BooleanDisposable() self.fast_loop(current_index, 0, disposable) elif isinstance(v, Stop): self.signal_stop() else: raise Exception( 'no recognized acknowledgment {}'.format(v)) inner_ack.on_next(v) inner_ack.on_completed() ack.observe_on(scheduler=self.scheduler).subscribe(_) return inner_ack
def on_next(v): def action(_, __): inner_ack = observer.on_next(v) if isinstance(inner_ack, Continue): ack.on_next(inner_ack) ack.on_completed() elif isinstance(inner_ack, Stop): ack.on_next(inner_ack) ack.on_completed() else: inner_ack.unsafe_subscribe(ack) self.scheduler.schedule(action) ack = Ack() return ack
def on_next(self, elem): stream_error = True if not is_active[0]: return stop_ack else: async_upstream_ack = Ack() child = source.selector(elem) stream_error = False with source.lock: state[0] = WaitOnActiveChild() child_observer = ChildObserver(observer, scheduler, async_upstream_ack, self) disposable = child.subscribe(child_observer, scheduler, subscribe_scheduler) with source.lock: current_state = state[0] state[0] = Active(disposable) if isinstance(current_state, WaitOnNextChild): with source.lock: state[0] = current_state state_: WaitOnNextChild = current_state return state_.ack elif isinstance(current_state, WaitOnActiveChild): if is_active[0]: return async_upstream_ack else: self.cancel_state() return stop_ack elif isinstance(current_state, Cancelled): self.cancel_state() return stop_ack else: self.report_invalid_state(current_state, 'on_next') return stop_ack
def on_next_right(right): right_elem[0] = right right_ack[0] = Ack() with lock: has_right_elem[0] = True if has_left_elem[0]: has_left = True else: has_left = False if has_left: left = left_elem[0] if right_is_higher(left, right): # right is higher than left # complete inner observable, discard left and request new left; save right with lock: # avoids completing observer twice # discard left element has_left_elem[0] = False left_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: observer.on_completed() return Stop() left_ack[0].on_next(continue_ack) left_ack[0].on_completed() return right_ack[0] if right_is_lower(left, right): # right is lower than left, discard right and request new right # this is possible in initial phase or if is_lower and is_higher are not tight # discard right element has_right_elem[0] = False right_elem[0] = None return continue_ack else: # left is equal to right, send right element, request new right # discard right element has_right_elem[0] = False right_elem[0] = None # send right element ack = observer.on_next(self.selector(left, right)) return ack else: # no left element has been yet received; only possible in initial phase return right_ack[0]
def on_next_left(left_elem): ack = Ack() with self.lock: if isinstance(state[0], self.WaitForLeftOrRight): new_state = self.WaitForRightOrInner(left_ack=ack) elif isinstance(state[0], self.WaitForLeft): state_: source.WaitForLeft = state[0] new_state = self.Active(left_ack=ack, right_elem=state_.right_elem, right_ack=state_.right_ack, upper_ack=Continue()) elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError state[0] = new_state class ChildObserver(Observer): def __init__(self, out: Observer, scheduler): self.out = out self.scheduler = scheduler def on_next(self, inner_left): ack = Ack() has_right_elem = False right_elem = None with source.lock: if isinstance(state[0], source.WaitForRightOrInner): # not much to do state_typed: source.WaitForRightOrInner = state[0] state[0] = source.WaitForRight( inner_left_elem=inner_left, left_ack=state_typed.left_ack, inner_left_ack=ack, left_elem=source.selector_left(left_elem)) elif isinstance(state[0], source.Active): # send zipped item to observer state_typed: source.Active = state[0] has_right_elem = True right_elem = state_typed.right_elem state[0] = source.Active( left_ack=state_typed.left_ack, right_elem=state_typed.right_elem, right_ack=state_typed.right_ack, upper_ack=ack) elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError if has_right_elem: # send triple to observer zipped_elem = source.selector( source.selector_left(left_elem), right_elem, inner_left) upper_ack = observer.on_next(zipped_elem) # race condition with on_completed if isinstance(state[0], source.Active): typed_state: source.Active = state[0] typed_state.upper_ack = upper_ack if isinstance(upper_ack, Stop): with source.lock: state[0] = source.Completed() else: def _(v): if isinstance(v, Stop): with source.lock: state[0] = source.Completed() upper_ack.observe_on(scheduler).subscribe(_) return upper_ack else: return ack def on_error(self, err): with source.lock: state[0] = source.Completed() observer.on_error(err) def on_completed(self): back_pressure_right = False back_pressure_left = False left_ack = None right_ack = None upper_ack = None complete_observer = False with source.lock: if isinstance(state[0], source.Active): # normal complete if right_completed[0]: state[0] = source.Completed() complete_observer = True else: # request new left and new right state_typed: source.Active = state[0] back_pressure_right = True back_pressure_left = True left_ack = state_typed.left_ack right_ack = state_typed.right_ack upper_ack = state_typed.upper_ack state[0] = source.WaitForLeftOrRight() elif isinstance(state[0], source.WaitForRightOrInner): # empty inner observable state_typed: source.WaitForRight = state[0] # count up number of inner completed (without right completed) inner_left_completed[0] += 1 state[0] = source.WaitForLeftOrRight() back_pressure_left = True left_ack = state_typed.left_ack upper_ack = Continue() if complete_observer: observer.on_completed() if back_pressure_left or back_pressure_right: def _(v): if isinstance(v, Stop): with source.lock: state[0] = source.Completed() upper_ack.subscribe(_) if back_pressure_left: # upper_ack should not be Stop if isinstance(upper_ack, Continue): left_ack.on_next(upper_ack) left_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe(left_ack) if back_pressure_right: if isinstance(upper_ack, Continue): right_ack.on_next(upper_ack) right_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe( right_ack) child = self.selector_inner(left_elem) child_observer = ChildObserver(observer, scheduler) disposable = child.subscribe(child_observer, scheduler, CurrentThreadScheduler()) inner_disposable.disposable = disposable return ack
def unsafe_subscribe(self, observer, scheduler, subscribe_scheduler): is_done = [None] last_ack = [continue_ack] elem_a1 = [None] has_elem_a1 = [None] elem_a2 = [None] has_elem_a2 = [None] continue_p = [Ack()] complete_with_next = [False] def raw_on_next(a1, a2): if is_done[0]: return stop_ack else: stream_error = True try: c = self.selector(a1, a2) stream_error = False ack = observer.on_next(c) if complete_with_next[0]: if isinstance(ack, Continue) or isinstance(ack, Stop): signal_on_complete(False) else: ack.observe_on(scheduler).subscribe( on_completed=lambda: signal_on_complete(False)) except Exception as ex: if stream_error: is_done[0] = True observer.on_error(ex) return stop_ack else: raise finally: has_elem_a1[0] = False has_elem_a2[0] = False return ack def signal_on_next(a1, a2): prev_last_ack = last_ack[0] if isinstance(prev_last_ack, Continue): new_last_ack = raw_on_next(a1, a2) elif isinstance(prev_last_ack, Stop): return stop_ack else: def _(v, _): if isinstance(v, Continue): with self.lock: ack = raw_on_next(a1, a2) return ack else: return stop_ack ack = Ack() prev_last_ack.flat_map(_).subscribe(ack) new_last_ack = ack last_ack[0] = new_last_ack if isinstance(new_last_ack, Continue) or isinstance( new_last_ack, Stop): continue_p[0].on_next(new_last_ack) continue_p[0].on_completed() else: new_last_ack.subscribe(continue_p[0]) # acknowledgment used by input that receives first continue_p[0] = Ack() return new_last_ack def signal_on_error(ex): with self.lock: if not is_done[0]: is_done[0] = True observer.on_error(ex) last_ack[0] = stop_ack def signal_on_complete(has_elem): def raw_on_completed(): if not is_done[0]: is_done[0] = True observer.on_completed() with self.lock: if not has_elem: if isinstance(last_ack[0], Continue): raw_on_completed() elif isinstance(last_ack[0], Stop): pass else: def _(v): if isinstance(v, Continue): with self.lock: raw_on_completed() else: pass last_ack[0].observe_on(scheduler).subscribe(on_next=_) continue_p[0].on_next(stop_ack) last_ack[0] = stop_ack else: complete_with_next[0] = True def on_next_left(elem): with self.lock: if is_done[0]: return_ack = stop_ack else: elem_a1[0] = elem if not has_elem_a1[0]: has_elem_a1[0] = True if has_elem_a2[0]: return_ack = signal_on_next(elem_a1[0], elem_a2[0]) else: return_ack = continue_p[0] return return_ack def on_next_right(elem): with self.lock: if is_done[0]: return_ack = stop_ack else: elem_a2[0] = elem if not has_elem_a2[0]: has_elem_a2[0] = True if has_elem_a1[0]: return_ack = signal_on_next(elem_a1[0], elem_a2[0]) else: return_ack = continue_p[0] return return_ack def on_error(ex): signal_on_error(ex) def on_completed_left(): return signal_on_complete(has_elem_a1[0]) def on_completed_right(): return signal_on_complete(has_elem_a2[0]) left_observer = AnonymousObserver(on_next=on_next_left, on_error=on_error, on_completed=on_completed_left) d1 = self.left.unsafe_subscribe(left_observer, scheduler, subscribe_scheduler) right_observer = AnonymousObserver(on_next=on_next_right, on_error=on_error, on_completed=on_completed_right) d2 = self.right.unsafe_subscribe(right_observer, scheduler, subscribe_scheduler) return CompositeDisposable(d1, d2)
def __init__(self, value, initial): self.lock = config["concurrency"].RLock() self.value = value self.counter = initial self.promise = Ack()
def on_next(self, v): self.received.append(v) self.ack = Ack() self.inner_obs = TestObserver() self.inner_selector(v).subscribe(self.inner_obs, self.scheduler, CurrentThreadScheduler()) return self.ack
def on_next_left(left_val): # left has been requested because of initial state, # or because right is higher than left left_elem[0] = left_val left_ack[0] = Ack() publish_subject[0] = PublishSubject() # send next inner observable; subscribe needs to happen immediately outer_ack[0] = left_observer[0].on_next( (left_val, publish_subject[0])) # todo: make this private with lock: has_left_elem[0] = True if has_right_elem[0]: has_right = True else: # race condition: left element is receieved first has_right = False if has_right: right_val = right_elem[0] if left_is_lower(left_val, right_val): # right is higher than left # send (empty) inner observable and request new left; don't discard right element # inner observable is empty, because left has just been received # discard left element has_left_elem[0] = False left_elem[0] = None # complete (empty) inner observable publish_subject[0].on_completed() # continue next left element after outer acknowledgment return outer_ack[0] if left_is_higher(left_val, right_val): # right is lower than left, discard right and request new right # this is possible in initial phase or if is_lower and is_higher are not tight assert right_ack[0] is not None, 'missing acknowledgment' with lock: # avoids completing observer twice # discard right element has_right_elem[0] = False right_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: left_observer[0].on_completed() right_observer[0].on_completed() return Stop() ack = right_observer[0].on_next((False, right_val)) # request new right ack.connect_ack(next_ack=right_ack[0]) else: # left is equal to right, send right element, request new right with lock: # avoids completing observer twice # discard right element has_right_elem[0] = False right_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: left_observer[0].on_completed() right_observer[0].on_completed() return Stop() # send right element ack = publish_subject[0].on_next(right_val) ack2 = right_observer[0].on_next((True, right_val)) # request new right element ack.connect_ack_2(ack2=ack2, next_ack=right_ack[0]) return_ack = left_ack[0].merge_ack(outer_ack[0]) # return left_ack[0] return return_ack
def on_next_right(right_val): right_elem[0] = right_val right_ack[0] = Ack() with lock: has_right_elem[0] = True if has_left_elem[0]: has_left = True else: has_left = False if has_left: left_val = left_elem[0] # print('left={}, right={}'.format(left_val, right_val)) if is_higher(left_val, right_val): # right is higher than left # complete inner observable, discard left and request new left; save right with lock: # avoids completing observer twice # discard left element has_left_elem[0] = False left_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: left_observer[0].on_completed() right_observer[0].on_completed() return Stop() # complete inner observable publish_subject[0].on_completed() left_ack[0].on_next(continue_ack) left_ack[0].on_completed() return right_ack[0] if is_lower(left_val, right_val): # right is lower than left, discard right and request new right # this is possible in initial phase or if is_lower and is_higher are not tight ack = right_observer[0].on_next((False, right_val)) # discard right element has_right_elem[0] = False right_elem[0] = None return ack else: # left is equal to right, send right element, request new right # discard right element has_right_elem[0] = False right_elem[0] = None # send right element ack = publish_subject[0].on_next(right_val) ack2 = right_observer[0].on_next((True, right_val)) return ack.merge_ack(ack2) else: # no left element has been yet received; only possible in initial phase return right_ack[0]
def on_next_left(left): # left has been requested because of initial state, # or because right is higher than left left_elem[0] = left left_ack[0] = Ack() with lock: has_left_elem[0] = True if has_right_elem[0]: has_right = True else: # race condition: left element is receieved first has_right = False if has_right: right = right_elem[0] if left_is_lower(left, right): # right is higher than left # request new left; don't discard right element # discard left element has_left_elem[0] = False left_elem[0] = None return continue_ack if left_is_higher(left, right): # right is lower than left, discard right and request new right # this is possible in initial phase or if is_lower and is_higher are not tight assert right_ack[0] is not None, 'missing acknowledgment' with lock: # avoids completing observer twice # discard right element has_right_elem[0] = False right_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: # left_observer.on_completed() observer.on_completed() return Stop() right_ack[0].on_next(continue_ack) right_ack[0].on_completed() else: # left is equal to right, send right element, request new right with lock: # avoids completing observer twice # discard right element has_right_elem[0] = False right_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: # left_observer.on_completed() observer.on_completed() return Stop() # send right element ack = observer.on_next(self.selector(left, right)) # request new right element ack.connect_ack(right_ack[0]) # # request new left element # return ack return left_ack[0]
def on_next_right(right): ack = Ack() upper_ack = None has_inner_left_elem = False request_left_right = False request_right = False with self.lock: if 0 < inner_left_completed[0]: inner_left_completed[0] -= 1 request_right = True upper_ack = Continue() # new_state = self.WaitForLeftOrRight() new_state = state[0] elif isinstance(state[0], self.WaitForLeftOrRight): new_state = self.WaitForLeft(right_elem=right, right_ack=ack) elif isinstance(state[0], self.WaitForRightOrInner): state_: FlatZipObservable.WaitForRightOrInner = state[0] if inner_left_completed[0] == 0: new_state = self.Active(left_ack=state_.left_ack, right_elem=right, right_ack=ack, upper_ack=Continue()) else: inner_left_completed[0] -= 1 new_state = self.WaitForLeftOrRight() elif isinstance(state[0], self.WaitForRight): state_: FlatZipObservable.WaitForRight = state[0] has_inner_left_elem = True left_elem = state_.left_elem left_ack = state_.left_ack inner_left_elem = state_.inner_left_elem inner_left_ack = state_.inner_left_ack new_state = state[0] elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError state[0] = new_state if has_inner_left_elem: zipped_elem = self.selector(left_elem, right, inner_left_elem) upper_ack = observer.on_next(zipped_elem) if isinstance(upper_ack, Stop): with self.lock: state[0] = self.Completed return upper_ack request_inner_elem = False with self.lock: if 0 < inner_left_completed[0]: # inner left completed, request new left and right new_state = self.WaitForLeftOrRight() request_left_right = True else: # state is the same, change state to active new_state = self.Active(left_ack=left_ack, right_elem=right, right_ack=ack, upper_ack=upper_ack) request_inner_elem = True state[0] = new_state if request_inner_elem: if isinstance(upper_ack, Continue) or isinstance( upper_ack, Stop): inner_left_ack.on_next(upper_ack) inner_left_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe( inner_left_ack) if request_left_right: if isinstance(upper_ack, Continue): left_ack.on_next(upper_ack) left_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe(left_ack) if request_left_right or request_right: if isinstance(upper_ack, Continue): ack.on_next(upper_ack) ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe(ack) return ack
def on_next(self, v): self.received.append(v) self.ack = Ack() return self.ack
def connect(self): with self.lock: if not self.is_connected and not self.is_connected_started: self.is_connected_started = True buffer_was_drained = Ack() def on_next(v): if isinstance(v, Continue): self.root_ack.on_next(Continue()) self.root_ack.on_completed() source.is_connected = True source.queue = None # source.connected_ack = None # todo: fill in elif isinstance(v, Stop): raise NotImplementedError else: raise NotImplementedError buffer_was_drained.subscribe(on_next=on_next) source = self class CustomObserver(Observer): def __init__(self): self.ack = None def on_next(self, v): ack = source.underlying.on_next(v) self.ack = ack def on_next(v): if isinstance(v, Stop): buffer_was_drained.on_next(v) buffer_was_drained.on_completed() ack.subscribe(on_next=on_next) return ack def on_error(self, err): raise NotImplementedError def on_completed(self): if not source.scheduled_done: if self.ack is None: buffer_was_drained.on_next(Continue()) buffer_was_drained.on_completed() else: def on_next(v): if isinstance(v, Continue): buffer_was_drained.on_next(Continue()) buffer_was_drained.on_completed() self.ack.subscribe(on_next) elif source.schedule_error is not None: raise NotImplementedError else: source.underlying.on_completed() class EmptyObject: pass self.queue.put(EmptyObject) disposable = IteratorAsObservable(iter(self.queue.get, EmptyObject)) \ .subscribe(CustomObserver(), self.scheduler, CurrentThreadScheduler()) self.connected_ref = buffer_was_drained, disposable return self.connected_ref