internal Behavior(Stream <T> stream, T initialValue) { this.stream = stream; this.valueProperty = initialValue; this.UsingInitialValue = true; this.streamListener = TransactionInternal.Apply( (trans1, _) => this.stream.Listen( Node <T> .Null, trans1, (trans2, a) => { this.valueUpdate.MatchNone( () => { trans2.Last( () => { this.valueUpdate.MatchSome(v => this.ValueProperty = v); this.valueUpdate = MaybeInternal.None; }); }); this.valueUpdate = MaybeInternal.Some(a); }, false), false); }
internal LazyBehavior(TransactionInternal trans, Stream <T> stream, Lazy <T> lazyInitialValue) : base(stream, default(T)) { this.LazyInitialValue = new Lazy <T>(() => GuardAgainstSend(lazyInitialValue)); trans.Sample(this.EnsureValueIsCreated); }
public void Loop(Cell <T> c) => TransactionInternal.Apply( (trans, _) => { lock (this.isLoopedLock) { if (this.isLooped) { throw new InvalidOperationException("Loop was looped more than once."); } this.isLooped = true; } if (trans != this.transaction) { this.transaction = null; throw new InvalidOperationException( "Loop must be looped in the same transaction that it was created in."); } this.transaction = null; this.Loop(trans, c); return(UnitInternal.Value); }, false);
public static void RunVoid(Action action) => TransactionInternal.RunImpl( () => { action(); return(Unit.Value); });
internal static Behavior <TResult> LiftBehaviorsImpl <T, T2, TResult>( this IReadOnlyCollection <T2> b, Func <IReadOnlyList <T>, TResult> f) where T2 : Behavior <T> { return(TransactionInternal.Apply( (trans1, _) => { Stream <Action <T[]> > @out = new Stream <Action <T[]> >( new FanOutKeepListenersAlive(b.Select(behavior => behavior.KeepListenersAlive))); Lazy <TResult> initialValue = new Lazy <TResult>(() => f(b.Select(behavior => behavior.SampleNoTransaction()).ToArray())); IReadOnlyList <IListener> listeners = b.Select( (behavior, i) => behavior.Updates() .Listen( @out.Node, trans1, (trans2, v) => @out.Send(trans2, vv => vv[i] = v), false)) .ToArray(); return @out.Coalesce(trans1, (x, y) => x + y) .MapImpl( a => { T[] values = b.Select(behavior => behavior.SampleNoTransaction()).ToArray(); a(values); return f(values); }) .UnsafeAttachListener(ListenerInternal.CreateCompositeImpl(listeners)) .HoldLazyInternal(trans1, initialValue); }, false)); }
private static Stream <T> Merge <T>( TransactionInternal trans, IReadOnlyList <Stream <T> > e, int start, int end, Func <T, T, T> f) { int n = end - start; if (n == 0) { return(new Stream <T>()); } if (n == 1) { return(e[start]); } if (n == 2) { return(e[start].Merge(trans, e[start + 1], f)); } int mid = (start + end) / 2; return(Merge(trans, e, start, mid, f).Merge(trans, Merge(trans, e, mid, end, f), f)); }
internal Cell(Behavior <T> behavior) { this.BehaviorImpl = behavior; this.updates = new Lazy <Stream <T> >(() => TransactionInternal.Apply( (trans, _) => this.BehaviorImpl.Updates().Coalesce(trans, (left, right) => right), false)); }
internal Stream <T> Coalesce(TransactionInternal trans1, Func <T, T, T> f) { Stream <T> @out = new Stream <T>(this.KeepListenersAlive); Action <TransactionInternal, T> h = CoalesceHandler.Create(f, @out); IListener l = this.Listen(@out.Node, trans1, h, false); return(@out.UnsafeAttachListener(l)); }
private static void EnsureElevated(TransactionInternal transaction) { if (transaction != null && !transaction.isElevated) { transaction.isElevated = true; if (!transaction.hasParentTransaction) { Monitor.Enter(TransactionLock); } RunStartHooks(); } }
internal void Loop(TransactionInternal trans, Stream <T> stream) { lock (this.isAssignedLock) { if (this.isAssigned) { throw new InvalidOperationException("Loop was looped more than once."); } this.isAssigned = true; } this.AttachListenerImpl(stream.Listen(this.Node, this.Send)); lock (stream.KeepListenersAlive) { stream.KeepListenersAlive.Use(this.KeepListenersAlive); } }
internal IWeakListener Listen( Node target, TransactionInternal trans, Action <TransactionInternal, T> action, bool suppressEarlierFirings) { (bool changed, Node <T> .Target nodeTarget) = this.Node.Link(trans, action, target); if (changed) { trans.SetNeedsRegenerating(); } // ReSharper disable once LocalVariableHidesMember List <T> firings = this.firings.ToList(); if (!suppressEarlierFirings && firings.Count > 0) { trans.Prioritized( target, trans2 => { // Anything sent already in this transaction must be sent now so that // there's no order dependency between send and listen. foreach (T a in firings) { TransactionInternal.InCallback++; try { // Don't allow transactions to interfere with Sodium // internals. action(trans2, a); } finally { TransactionInternal.InCallback--; } } }); } return(new ListenerImplementation(this, action, nodeTarget)); }
public CellLoop() { this.transaction = TransactionInternal.GetCurrentTransaction(); if (this.transaction == null) { throw new InvalidOperationException("Loop must be created within an explicit transaction."); } this.transaction.Last( () => { if (this.transaction != null) { this.transaction = null; throw new InvalidOperationException("Loop was not looped."); } }); }
private Stream <T> Merge(TransactionInternal trans, Stream <T> s) { Stream <T> @out = new Stream <T>(this.KeepListenersAlive); Node <T> left = new Node <T>(); Node <T> right = @out.Node; (bool changed, Node <T> .Target nodeTarget) = left.Link(trans, (t, v) => { }, right); if (changed) { trans.SetNeedsRegenerating(); } Action <TransactionInternal, T> h = @out.Send; IListener l1 = this.Listen(left, h); IListener l2 = s.Listen(right, h); return(@out.UnsafeAttachListener(l1) .UnsafeAttachListener(l2) .UnsafeAttachListener(ListenerInternal.CreateFromNodeAndTarget(left, nodeTarget))); }
internal Cell <T> HoldLazyImpl(Lazy <T> initialValue) => TransactionInternal.Apply((trans, _) => new Cell <T>(this.HoldLazyInternal(trans, initialValue)), false);
internal Stream <T> MergeImpl(Stream <T> s, Func <T, T, T> f) => TransactionInternal.Apply((trans, _) => this.Merge(trans, s, f), false);
internal Stream <T> Merge(TransactionInternal trans, Stream <T> s, Func <T, T, T> f) => this.Merge(trans, s).Coalesce(trans, f);
public static void Post(Action action) => TransactionInternal.PostImpl(action);
public static void OnStart(Action action) => TransactionInternal.OnStartImpl(action);
public static T Run <T>(Func <T> f) => TransactionInternal.RunImpl(f);
/// <summary> /// Clean up the output by discarding any firing other than the last one. /// </summary> /// <param name="trans">The transaction to get the last firing from.</param> /// <returns>A stream containing only the last event firing from the specified transaction.</returns> internal Stream <T> LastFiringOnly(TransactionInternal trans) => this.Coalesce(trans, (first, second) => second);
public static bool IsActive() => TransactionInternal.HasCurrentTransaction();
internal static Cell <T> ConstantLazyImpl <T>(Lazy <T> value) => TransactionInternal.Apply((trans, _) => new Cell <T>(StreamInternal.NeverImpl <T>().HoldLazyInternal(trans, value)), false);
internal IWeakListener ListenWeakImpl(Action <T> handler) => TransactionInternal.Apply( (trans, _) => this.BehaviorImpl.Value(trans).ListenWeakImpl(handler), false);
internal static Stream <T> ValueImpl <T>(Behavior <T> b) => TransactionInternal.Apply((trans, _) => b.Value(trans), false);
internal static Stream <T> UpdatesImpl <T>(Behavior <T> b) => TransactionInternal.Apply( (trans, _) => b.Updates().Coalesce(trans, (left, right) => right), false);
internal void Close() { EnsureElevated(this); foreach (Node.Target target in this.TargetsToActivate) { target.IsActivated = true; } this.ActivatedTargets = true; // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < this.sendQueue.Count; i++) { this.sendQueue[i](this); } this.sendQueue.Clear(); while (this.prioritizedQueue.Count > 0 || this.sampleQueue.Count > 0) { while (this.prioritizedQueue.Count > 0) { this.CheckRegen(); Entry e = this.prioritizedQueue.Dequeue(); e.IsRemoved = true; e.Action(this); } List <Action> sq = this.sampleQueue; this.sampleQueue = new List <Action>(); foreach (Action s in sq) { s(); } } while (this.lastQueue.Count > 0) { this.lastQueue.Dequeue()(); } if (!this.hasParentTransaction) { void ExecuteInNewTransaction(Action <TransactionInternal> action, bool runStartHooks) { try { TransactionInternal transaction = new TransactionInternal(this.postQueue, this.splitQueue); if (!runStartHooks) { // this will ensure we don't run start hooks transaction.isElevated = true; } LocalTransaction.Value = transaction; try { action(transaction); } finally { transaction.Close(); } } finally { LocalTransaction.Value = this; } } while (this.postQueue.Count > 0 || this.splitQueue.Count > 0) { while (this.postQueue.Count > 0) { ExecuteInNewTransaction(this.postQueue.Dequeue(), true); } Dictionary <int, Action <TransactionInternal> > sq = this.splitQueue; this.splitQueue = new Dictionary <int, Action <TransactionInternal> >(); foreach (int n in sq.Keys.OrderBy(n => n)) { ExecuteInNewTransaction(sq[n], false); } } } }
internal static Stream <T> MergeImpl <T, T2>(this IEnumerable <T2> s, Func <T, T, T> f) where T2 : Stream <T> { IReadOnlyList <Stream <T> > v = s.ToArray(); return(TransactionInternal.Apply((trans, _) => Merge(trans, v, 0, v.Count, f), false)); }
internal static T Apply <T>(Func <TransactionInternal, bool, T> code, bool ensureElevated) { TransactionInternal transaction = LocalTransaction.Value; T returnValue = default(T); Exception exception = null; TransactionInternal newTransaction = transaction; try { bool createdNewTransaction = newTransaction == null; if (newTransaction == null) { newTransaction = new TransactionInternal(); LocalTransaction.Value = newTransaction; } if (ensureElevated) { EnsureElevated(newTransaction); } returnValue = code(newTransaction, createdNewTransaction); } catch (Exception e) { exception = e; } try { try { if (transaction == null) { newTransaction?.Close(); } } catch (Exception e) { if (exception == null) { throw; } throw new AggregateException(exception, e); } if (exception != null) { ExceptionDispatchInfo.Capture(exception).Throw(); } return(returnValue); } finally { if (transaction == null) { if (newTransaction != null && newTransaction.isElevated && !newTransaction.hasParentTransaction) { Monitor.Exit(TransactionLock); } LocalTransaction.Value = null; } } }
internal static Behavior <T> ConstantLazyImpl <T>(Lazy <T> value) => TransactionInternal.Apply((trans, _) => StreamInternal.NeverImpl <T>().HoldLazyInternal(trans, value), false);
internal Behavior <T> HoldLazyInternal(TransactionInternal trans, Lazy <T> initialValue) => new LazyBehavior <T>(trans, this, initialValue);