public void OnNext(TSource value) { TKey key; try { key = _parent._keySelector(value); } catch (Exception exception) { Error(exception); return; } var fireNewMapEntry = false; ISubject <TElement> writer; try { if (!_map.TryGetValue(key, out writer)) { writer = new Subject <TElement>(); _map.Add(key, writer); fireNewMapEntry = true; } } catch (Exception exception) { Error(exception); return; } if (fireNewMapEntry) { var group = new GroupedObservable <TKey, TElement>(key, writer, _parent._refCountDisposable); _observer.OnNext(group); } TElement element; try { element = _parent._elementSelector(value); } catch (Exception exception) { Error(exception); return; } writer.OnNext(element); }
public void OnNext(TSource value) { var key = this.keySelector(value); if (!this.groupedObservables.TryGetValue(key, out var groupedObservable)) { groupedObservable = new GroupedObservable(key); this.groupedObservables.Add(key, groupedObservable); this.observer.OnNext(groupedObservable); } groupedObservable.Subject.OnNext(value); }
/// <summary> /// Projects each element of an observable sequence into the corresponding flow. /// <para/> /// This method implements the operator directly without the use of GroupBy. The performance is similar. /// </summary> /// <typeparam name="TFlowKey">The type of flow key.</typeparam> /// <typeparam name="TSource">The packet type.</typeparam> /// <param name="observable">The source sequence of packets.</param> /// <param name="getFlowKey">The function to get flow key from the element.</param> /// <returns>An observable sequence of flows.</returns> public static IObservable <IGroupedObservable <TFlowKey, TSource> > GroupFlowsDictionary <TFlowKey, TSource>(this IObservable <TSource> source, Func <TSource, TFlowKey> getFlowKey) { return(System.Reactive.Linq.Observable.Create <IGroupedObservable <TFlowKey, TSource> >(observer => { var flowDictionary = new Dictionary <TFlowKey, GroupedObservable <TFlowKey, TSource> >(); return source.Subscribe(value => { var flowKey = getFlowKey(value); if (!flowDictionary.TryGetValue(flowKey, out var groupedObservable)) { groupedObservable = new GroupedObservable <TFlowKey, TSource>(flowKey); flowDictionary.Add(flowKey, groupedObservable); observer.OnNext(groupedObservable); } groupedObservable.OnNext(value); }, observer.OnError, () => { foreach (var c in flowDictionary) { c.Value.OnComplete(); } observer.OnCompleted(); }); })); }
public void OnNext(TSource value) { var key = default(TKey); try { key = _parent._keySelector(value); } catch (Exception exception) { Error(exception); return; } var fireNewMapEntry = false; var writer = default(ISubject <TElement>); try { writer = _map.GetOrAdd(key, () => new Subject <TElement>(), out fireNewMapEntry); } catch (Exception exception) { Error(exception); return; } if (fireNewMapEntry) { var group = new GroupedObservable <TKey, TElement>(key, writer, _parent._refCountDisposable); var duration = default(IObservable <TDuration>); var durationGroup = new GroupedObservable <TKey, TElement>(key, writer); try { duration = _parent._durationSelector(durationGroup); } catch (Exception exception) { Error(exception); return; } lock (base._observer) base._observer.OnNext(group); var md = new SingleAssignmentDisposable(); _parent._groupDisposable.Add(md); md.Disposable = duration.SubscribeSafe(new δ(this, key, writer, md)); } var element = default(TElement); try { element = _parent._elementSelector(value); } catch (Exception exception) { Error(exception); return; } writer.OnNext(element); }
public override void OnNext(TSource value) { TKey key; try { key = _keySelector(value); } catch (Exception exception) { Error(exception); return; } var fireNewMapEntry = false; ISubject <TElement> writer; try { // // Note: The box instruction in the IL will be erased by the JIT in case T is // a value type. In fact, the whole if block will go away and we'll end // up with nothing but the GetOrAdd call below. // // See GroupBy for more information and confirmation of this fact using // the SOS debugger extension. // if (key == null) { lock (_nullGate) { if (_null == null) { _null = NewSubject(); fireNewMapEntry = true; } writer = _null; } } else { writer = _map.GetOrAdd(key, NewSubject, out fireNewMapEntry); } } catch (Exception exception) { Error(exception); return; } if (fireNewMapEntry) { var group = new GroupedObservable <TKey, TElement>(key, writer, _refCountDisposable); var durationGroup = new GroupedObservable <TKey, TElement>(key, writer); IObservable <TDuration> duration; try { duration = _durationSelector(durationGroup); } catch (Exception exception) { Error(exception); return; } lock (_gate) { ForwardOnNext(group); } var durationObserver = new DurationObserver(this, key, writer); _groupDisposable.Add(durationObserver); durationObserver.SetResource(duration.SubscribeSafe(durationObserver)); } TElement element; try { element = _elementSelector(value); } catch (Exception exception) { Error(exception); return; } // // ISSUE: Rx v1.x shipped without proper handling of the case where the duration // sequence fires concurrently with the OnNext code path here. In such a // case, the subject can be completed before we get a chance to send out // a new element. However, a resurrected group for the same key won't get // to see the element either. To guard against this case, we'd have to // check whether the OnNext call below lost the race, and resurrect a new // group if needed. Unfortunately, this complicates matters when the // duration selector triggers synchronously (e.g. Return or Empty), which // causes the group to terminate immediately. We should not get stuck in // this case, repeatedly trying to resurrect a group that always ends // before we can send the element into it. Also, users may expect this // base case to mean no elements will ever be produced, so sending the // element into the group before starting the duration sequence may not // be a good idea either. For the time being, we'll leave this as-is and // revisit the behavior for vNext. Nonetheless, we'll add synchronization // to ensure no concurrent calls to the subject are made. // writer.OnNext(element); }
public override void OnNext(TSource value) { var key = default(TKey); try { key = _keySelector(value); } catch (Exception exception) { Error(exception); return; } var fireNewMapEntry = false; var writer = default(Subject <TElement>); try { // // Note: The box instruction in the IL will be erased by the JIT in case T is // a value type. In fact, the whole if block will go away and we'll end // up with nothing but the TryGetValue check below. // // // var fireNewMapEntry = false; // C:\Projects\Rx\Rx\Experimental\Main\Source\Rx\System.Reactive.Linq\Reactive\Linq\Observable\GroupBy.cs @ 67: // 000007fb`6d544b80 48c7452800000000 mov qword ptr [rbp+28h],0 // // // var writer = default(ISubject<TElement>); // C:\Projects\Rx\Rx\Experimental\Main\Source\Rx\System.Reactive.Linq\Reactive\Linq\Observable\GroupBy.cs @ 66: // 000007fb`6d544b88 c6453400 mov byte ptr [rbp+34h],0 // // // if (!_map.TryGetValue(key, out writer)) // C:\Projects\Rx\Rx\Experimental\Main\Source\Rx\System.Reactive.Linq\Reactive\Linq\Observable\GroupBy.cs @ 86: // 000007fb`6d544b8c 488b4560 mov rax,qword ptr [rbp+60h] // ... // if (key == null) { if (_null == null) { _null = new Subject <TElement>(); fireNewMapEntry = true; } writer = _null; } else { if (!_map.TryGetValue(key, out writer)) { writer = new Subject <TElement>(); _map.Add(key, writer); fireNewMapEntry = true; } } } catch (Exception exception) { Error(exception); return; } if (fireNewMapEntry) { var group = new GroupedObservable <TKey, TElement>(key, writer, _refCountDisposable); ForwardOnNext(group); } var element = default(TElement); try { element = _elementSelector(value); } catch (Exception exception) { Error(exception); return; } writer.OnNext(element); }
public override void OnNext(TSource value) { var key = default(TKey); try { key = parent.keySelector(value); } catch (Exception exception) { Error(exception); return; } var fireNewMapEntry = false; var writer = default(ISubject <TElement>); try { if (key == null) { if (nullKeySubject == null) { nullKeySubject = new Subject <TElement>(); fireNewMapEntry = true; } writer = nullKeySubject; } else { if (!map.TryGetValue(key, out writer)) { writer = new Subject <TElement>(); map.Add(key, writer); fireNewMapEntry = true; } } } catch (Exception exception) { Error(exception); return; } if (fireNewMapEntry) { var group = new GroupedObservable <TKey, TElement>(key, writer, refCountDisposable); observer.OnNext(group); } var element = default(TElement); try { element = parent.elementSelector(value); } catch (Exception exception) { Error(exception); return; } writer.OnNext(element); }