public void Equality() { // mortals are not congruent or equal var s1 = new LifetimeSource(); var s2 = new LifetimeSource(); var a = new Func <Lifetime>[] { () => Lifetime.Immortal, () => Lifetime.Dead, () => s1.Lifetime, () => s2.Lifetime }; for (var i = 0; i < a.Length; i++) { for (var j = 0; j < a.Length; j++) { a[i]().IsCongruentTo(a[j]()).AssertEquals(i == j); a[i]().Equals(a[j]()).AssertEquals(i == j); if (i == j) { a[i]().GetHashCode().AssertEquals(a[j]().GetHashCode()); } } } // but mortals become congruent when set to the same phase s1.EndLifetime(); s2.EndLifetime(); s1.Lifetime.IsCongruentTo(s2.Lifetime).AssertIsTrue(); }
public void AsCancellationToken() { ((CancellationToken)Lifetime.Immortal).CanBeCanceled.AssertIsFalse(); ((CancellationToken)Lifetime.Dead).IsCancellationRequested.AssertIsTrue(); // cancelled on death var doomed = new LifetimeSource(); CancellationToken dt = doomed.Lifetime; dt.CanBeCanceled.AssertIsTrue(); dt.IsCancellationRequested.AssertIsFalse(); doomed.EndLifetime(); dt.IsCancellationRequested.AssertIsTrue(); // already cancelled when already dead ((CancellationToken)doomed.Lifetime).IsCancellationRequested.AssertIsTrue(); // hangs on immortal var blessed = new LifetimeSource(); CancellationToken bt = blessed.Lifetime; bt.CanBeCanceled.AssertIsTrue(); bt.IsCancellationRequested.AssertIsFalse(); blessed.ImmortalizeLifetime(); bt.IsCancellationRequested.AssertIsFalse(); // knows can't be cancelled when already immortal ((CancellationToken)blessed.Lifetime).CanBeCanceled.AssertIsFalse(); // hangs on limbo InvalidCallbackMaker.AssertNotCollectedAfter(action => { var r = new LifetimeSource(); CancellationToken ct = r.Lifetime; ct.Register(action); return(ct); }); }
public void CurrentAndFutureItems() { var source = new LifetimeSource(); var p1 = new Perishable <int>(1, source.Lifetime); var p2 = new Perishable <int>(1, Lifetime.Immortal); var p = new PerishableCollection <int>(); var li0 = new List <Perishable <int> >(); p.CurrentAndFutureItems().Subscribe(e => { li0.Add(e); e.Lifetime.WhenDead(() => li0.Remove(e)); }); li0.AssertSequenceEquals(); p.Add(p1); li0.AssertSequenceEquals(p1); var li1 = new List <Perishable <int> >(); p.CurrentAndFutureItems().Subscribe(e => { li1.Add(e); e.Lifetime.WhenDead(() => li1.Remove(e)); }); li1.AssertSequenceEquals(p1); p.Add(p2.Value, p2.Lifetime); li0.AssertSequenceEquals(p1, p2); li1.AssertSequenceEquals(p1, p2); source.EndLifetime(); li0.AssertSequenceEquals(p2); li1.AssertSequenceEquals(p2); }
public void Max() { Lifetime.Dead.Max(Lifetime.Dead).IsDead.AssertIsTrue(); Lifetime.Dead.Max(Lifetime.Immortal).IsImmortal.AssertIsTrue(); Lifetime.Immortal.Max(Lifetime.Dead).IsImmortal.AssertIsTrue(); Lifetime.Immortal.Max(Lifetime.Immortal).IsImmortal.AssertIsTrue(); var s1 = new LifetimeSource(); var s2 = new LifetimeSource(); var s3 = new LifetimeSource(); var m1 = s1.Lifetime.Max(s2.Lifetime); var m2 = s1.Lifetime.Max(s3.Lifetime); var m3 = s2.Lifetime.Max(s3.Lifetime); var t1 = m1.WhenDeadTask(); var t2 = m2.WhenDeadTask(); var t3 = m3.WhenDeadTask(); // equality optimization s1.Lifetime.Max(s1.Lifetime).AssertEquals(s1.Lifetime); m1.Max(m1).AssertEquals(m1); // dead optimization s1.Lifetime.Max(Lifetime.Dead).AssertEquals(s1.Lifetime); Lifetime.Dead.Max(m1).AssertEquals(m1); // when one becomes immortal, max becomes immortal (m1.IsImmortal || m1.IsDead).AssertIsFalse(); (m2.IsImmortal || m2.IsDead).AssertIsFalse(); s1.ImmortalizeLifetime(); m1.IsImmortal.AssertIsTrue(); m2.IsImmortal.AssertIsTrue(); // when one becomes dead, max is unaffected s2.EndLifetime(); // when both become dead, max is dead (m3.IsImmortal || m3.IsDead).AssertIsFalse(); t3.AssertNotCompleted(); s3.EndLifetime(); m3.IsDead.AssertIsTrue(); t3.AssertRanToCompletion(); Task.WhenAny(t1, t2).AssertNotCompleted(); // limbo allows collection InvalidCallbackMaker.AssertCollectedAfter(a => { var r1 = new LifetimeSource(); var r2 = new LifetimeSource(); var r3 = new LifetimeSource(); var life1 = r1.Lifetime.Max(r2.Lifetime); var life2 = r1.Lifetime.Max(r3.Lifetime); var life3 = r2.Lifetime.Max(r3.Lifetime); life1.WhenDead(a); life2.WhenDead(a); life3.WhenDead(a); r1.EndLifetime(); // one dead has no effect on max going to limbo return(Tuple.Create(life1, life2, life3)); }); }
public void LifetimeDeathReentrancy() { var r = new LifetimeSource(); var r2 = new LifetimeSource(); r.Lifetime.WhenDead(r2.EndLifetime, r2.Lifetime); r.Lifetime.WhenDead(r.EndLifetime); r.EndLifetime(); }
public void ToPerishableCollectionWithLifetime() { var source = new LifetimeSource(); var collectionSource = new LifetimeSource(); var p1 = new Perishable <int>(1, source.Lifetime); var p2 = new Perishable <int>(1, Lifetime.Immortal); var p = new PerishableCollection <int>(); var q = p.CurrentAndFutureItems().ToPerishableCollection(collectionSource.Lifetime); q.CurrentItems().AssertSequenceEquals(); p.Add(p1); q.CurrentItems().AssertSequenceEquals(p1); collectionSource.EndLifetime(); p.Add(p2.Value, p2.Lifetime); q.CurrentItems().AssertSequenceEquals(p1); source.EndLifetime(); source.EndLifetime(); q.CurrentItems().AssertSequenceEquals(); }
public void LifetimeDeathConcurrency() { var repeats = 5; foreach (var _ in Enumerable.Range(0, repeats)) { var n = 0; var source = new LifetimeSource(); var threadCount = 4; var callbackCount = 3000; TestUtil.ConcurrencyTest( threadCount, callbackCount, repeatedWork: (t, i) => source.Lifetime.WhenDead(() => Interlocked.Increment(ref n)), finalWork: t => source.EndLifetime()); n.AssertEquals(callbackCount * threadCount); } }
public void CurrentAndFutureItems_EndingDuringEnumerationDoesNotLockup() { var s = new LifetimeSource(); var p = new PerishableCollection <int>(); for (var i = 0; i < 1000; i++) { p.Add(i, Lifetime.Immortal); } var t = Task.Factory.StartNew( () => p.CurrentAndFutureItems().Subscribe( item => s.EndLifetime(), s.Lifetime)); t.Wait(TimeSpan.FromMilliseconds(100)); t.AssertRanToCompletion(); }
public void CurrentItems() { var source = new LifetimeSource(); var p1 = new Perishable <int>(1, source.Lifetime); var p2 = new Perishable <int>(1, Lifetime.Immortal); var p = new PerishableCollection <int>(); p.CurrentItems().AssertSequenceEquals(); p.Add(p1); p.CurrentItems().AssertSequenceEquals(p1); p.Add(p2.Value, p2.Lifetime); p.CurrentItems().AssertSequenceEquals(p1, p2); source.EndLifetime(); p.CurrentItems().AssertSequenceEquals(p2); }
public void Status() { // status of constants Lifetime.Immortal.IsMortal.AssertIsFalse(); Lifetime.Immortal.IsImmortal.AssertIsTrue(); Lifetime.Immortal.IsDead.AssertIsFalse(); Lifetime.Dead.IsMortal.AssertIsFalse(); Lifetime.Dead.IsImmortal.AssertIsFalse(); Lifetime.Dead.IsDead.AssertIsTrue(); // state before transition var mortal = new LifetimeSource(); mortal.Lifetime.IsMortal.AssertIsTrue(); mortal.Lifetime.IsImmortal.AssertIsFalse(); mortal.Lifetime.IsDead.AssertIsFalse(); // transition to dead var doomed = new LifetimeSource(); doomed.EndLifetime(); doomed.Lifetime.IsMortal.AssertIsFalse(); doomed.Lifetime.IsImmortal.AssertIsFalse(); doomed.Lifetime.IsDead.AssertIsTrue(); // transition to immortal var blessed = new LifetimeSource(); blessed.ImmortalizeLifetime(); blessed.Lifetime.IsMortal.AssertIsFalse(); blessed.Lifetime.IsImmortal.AssertIsTrue(); blessed.Lifetime.IsDead.AssertIsFalse(); // transition to immortal via limbo var limbo = new LimboLife(); limbo.Dispose(); GC.Collect(); limbo.Lifetime.IsMortal.AssertIsFalse(); limbo.Lifetime.IsImmortal.AssertIsTrue(); limbo.Lifetime.IsDead.AssertIsFalse(); }
public void ObserveNonPerishedCount() { var li1 = new List <int>(); new[] { new Perishable <int>(1, Lifetime.Immortal), new Perishable <int>(2, Lifetime.Immortal) } .ToObservable() .ObserveNonPerishedCount(completeWhenSourceCompletes: true) .Subscribe(li1.Add, () => li1.Add(-1)); li1.AssertSequenceEquals(0, 1, 2, -1); var source = new LifetimeSource(); var li2 = new List <int>(); new[] { new Perishable <int>(1, source.Lifetime), new Perishable <int>(2, source.Lifetime) } .ToObservable() .ObserveNonPerishedCount(completeWhenSourceCompletes: false) .Subscribe(li2.Add, () => li2.Add(-1)); li2.AssertSequenceEquals(0, 1, 2); source.EndLifetime(); li2.AssertSequenceEquals(0, 1, 2, 1, 0, -1); }
public void WhenSet() { // called when immortal? var blessed = new LifetimeSource(); var preBlessedLife = blessed.Lifetime.WhenDeadTask(); blessed.ImmortalizeLifetime(); var bt = new[] { Lifetime.Immortal.WhenDeadTask(), blessed.Lifetime.WhenDeadTask(), preBlessedLife }; Task.WhenAny(bt).AssertNotCompleted(); // called when dead? var doomed = new LifetimeSource(); var preDoomedLife = doomed.Lifetime.WhenDeadTask(); doomed.EndLifetime(); var dt = new[] { preDoomedLife, doomed.Lifetime.WhenDeadTask(), Lifetime.Dead.WhenDeadTask() }; Task.WhenAll(dt).AssertRanToCompletion(); // never called from limbo var limboed = new LimboLife(); var preLimboLife = limboed.Lifetime.WhenDeadTask(); limboed.Dispose(); Task.WhenAny( preLimboLife, limboed.Lifetime.WhenDeadTask() ).AssertNotCompleted(); }
public static void Main() { "=== Hit 'Enter' to advance ===".WriteLine(); Break(); /////////////////////////////////////////////////// "Manually inspecting a lifetime as it is killed:".WriteLine(); var lifesource1 = new LifetimeSource(); "Life #1: {0}".WriteLine(lifesource1); "Ending Life #1".WriteLine(); lifesource1.EndLifetime(); "Life #1: {0}".WriteLine(lifesource1); Break(); /////////////////////////////////////////////////// "Manually inspecting a lifetime as it is immortalized:".WriteLine(); var lifesource2 = new LifetimeSource(); "Life #2: {0}".WriteLine(lifesource2); "Immortalizing Life #2".WriteLine(); lifesource2.ImmortalizeLifetime(); "Life #2: {0}".WriteLine(lifesource2); Break(); /////////////////////////////////////////////////// "Using callbacks to inspect a lifetime as it is killed:".WriteLine(); var lifesource3 = new LifetimeSource(); "Life #3: {0}".WriteLine(lifesource3); "Registering WhenDead Life #3 (before)".WriteLine(); lifesource3.Lifetime.WhenDead(() => "WhenDead Life #3 (before)".WriteLine()); "Ending Life #3".WriteLine(); lifesource3.EndLifetime(); "Life #3: {0}".WriteLine(lifesource3); "Registering WhenDead Life #3 (after)".WriteLine(); lifesource3.Lifetime.WhenDead(() => "WhenDead Life #3 (after)".WriteLine()); Break(); /////////////////////////////////////////////////// "Using conditional callbacks:".WriteLine(); var lifesource4 = new LifetimeSource(); var lifesource5 = new LifetimeSource(); "Life #4: {0}".WriteLine(lifesource4); "Life #5: {0}".WriteLine(lifesource5); "Registering WhenDead 4 (requires 5)".WriteLine(); lifesource4.Lifetime.WhenDead( () => "Running WhenDead 4 (requires 5)".WriteLine(), lifesource5.Lifetime); "Registering WhenDead 5 (requires 4)".WriteLine(); lifesource5.Lifetime.WhenDead( () => "Running WhenDead 5 (requires 4)".WriteLine(), lifesource4.Lifetime); "Ending Life #4".WriteLine(); lifesource4.EndLifetime(); "Life #4: {0}".WriteLine(lifesource4); "Ending Life #5".WriteLine(); lifesource5.EndLifetime(); "Life #5: {0}".WriteLine(lifesource5); Break(); /////////////////////////////////////////////////// CallbackGarbageTest( "Immortal Lifetime collectable callback test:", () => new LifetimeSource().Lifetime, (e, a) => e.WhenDead(a)); /////////////////////////////////////////////////// var lifetimeMortal = new LifetimeSource(); CallbackGarbageTest( "Conditioned-on-dying Lifetime collectable callback test:", () => lifetimeMortal.Lifetime, (e, a) => { var r = new LifetimeSource(); e.WhenDead(a, r.Lifetime); r.EndLifetime(); }); GC.KeepAlive(lifetimeMortal); /////////////////////////////////////////////////// CallbackGarbageTest( "Immortal CancellationToken collectable callback test:", () => new CancellationTokenSource().Token, (e, a) => e.Register(a)); /////////////////////////////////////////////////// "Garbage collection of a lifetime's source:".WriteLine(); var lifesource6 = new LifetimeSource(); var life6 = lifesource6.Lifetime; "Life #6: {0}".WriteLine(life6); "Allowing garbage collection of Life #6's source".WriteLine(); lifesource6 = null; GC.Collect(); GC.WaitForPendingFinalizers(); "Life #6: {0}".WriteLine(life6); Break(); /////////////////////////////////////////////////// "=== Hit 'Enter' to close ===".WriteLine(); Break(); }
private void SetupEnergyAndTime(Game game, double energyLossForCutting, double energyGainPerConnectorBroken) { var elapsed = TimeSpan.Zero; var living = new LifetimeSource(); living.Lifetime.WhenDead(() => { if (Best == elapsed) { LabelTitle.Text = "New High Score! Try Again?"; LabelTitle.Foreground = new SolidColorBrush(Colors.Green); } else { LabelTitle.Text = "Game Over! Try Again?"; LabelTitle.Foreground = new SolidColorBrush(Colors.Red); } MenuPanel.Visibility = Visibility.Visible; }, game.Life); // show energy status var gains = 0.0; var loses = 0.0; var energy = 50.0; var maxEnergy = 50.0; game.LoopActions.Add(step => { // energy decays faster and faster over time var t = step.TimeStep.TotalSeconds; energy -= t * Math.Log(elapsed.TotalSeconds / 15 + 1).Max(1); // quickly decrease the size of the red/blue bars that show changes gains -= t * 5; loses -= t * 5; gains *= Math.Pow(0.01, t); loses *= Math.Pow(0.01, t); // keep everything reasonable gains = gains.Clamp(0, 10); loses = loses.Clamp(0, 10); energy = energy.Clamp(0, maxEnergy); // "draw" energy var w = canvas.ActualWidth; BarEnergy.Fill = new SolidColorBrush(Colors.Yellow.LerpTo(Colors.Green, (energy * 2 / maxEnergy).Clamp(0, 1))); BarLoses.Width = ((energy + loses) / maxEnergy * w).Clamp(0, w); BarGains.Width = (energy / maxEnergy * w).Clamp(0, w); BarEnergy.Width = ((energy - gains) / maxEnergy * w).Clamp(0, w); // hit 0 energy? game over if (energy == 0) { living.EndLifetime(); } }, game.Life); // track energy changes due to connectors dying game.Connectors.CurrentAndFutureItems().Subscribe(e => e.Lifetime.WhenDead(() => { // breaking connectors gain you some energy if (living.Lifetime.IsMortal) { energy += energyGainPerConnectorBroken; } gains += energyGainPerConnectorBroken; // but making cuts costs energy if (e.Value.CutPoint != null) { energy -= energyLossForCutting; loses += energyLossForCutting; } }), game.Life); // track times game.LoopActions.Add(step => { // advance time if (living.Lifetime.IsMortal) { elapsed += step.TimeStep; } Best = Best.Max(elapsed); // show time TimeLabel.Text = String.Format("Time: {0:0.0}s", elapsed.TotalSeconds); // show best time (green when making best time) TimeBest.Background = new SolidColorBrush( Best == elapsed ? Color.FromArgb(128, 0, 255, 0) : Colors.Transparent); TimeBest.Text = String.Format("Best: {0:0.0}s", Best.TotalSeconds); }, game.Life); }
public void Dispose() { _source.EndLifetime(); }