public void Equality() {
     var source = new LifetimeSource();
     var p = new Perishable<int>(2, source.Lifetime);
     p.AssertEquals(new Perishable<int>(2, source.Lifetime));
     p.Equals(new Perishable<int>(3, source.Lifetime)).AssertIsFalse();
     p.Equals(new Perishable<int>(2, Lifetime.Immortal)).AssertIsFalse();
 }
    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);
    }
示例#3
0
        /// <summary>
        /// Returns a new lifetime source that automatically kills its exposed lifetime if the given lifetime dies.
        /// Note: If the given lifetimes has died or will die then using ImmortalizeLifetime on the result will eventually cause an InvalidOperationException.
        /// </summary>
        public static LifetimeSource CreateDependentSource(this Lifetime lifetime)
        {
            var dependentResult = new LifetimeSource();

            lifetime.WhenDead(dependentResult.EndLifetime, dependentResult.Lifetime);
            return(dependentResult);
        }
 public void IsResultThreadSafe() {
     foreach (var repeat in Enumerable.Range(0, 5)) {
         var n = 5000;
         var nt = 4;
         var p = new PerishableCollection<int>();
         var queues = Enumerable.Range(0, 4).Select(e => new Queue<LifetimeSource>()).ToArray();
         TestUtil.ConcurrencyTest(
             threadCount: nt,
             callbackCount: n,
             repeatedWork: (t, i) => {
                 var r = new LifetimeSource();
                 p.Add(i, r.Lifetime);
                 if (i % 2 == 0) queues[t].Enqueue(r);
                 if (queues[t].Count > 20)
                     queues[t].Dequeue().EndLifetime();
             }, 
             finalWork: t => {
                 while (queues[t].Count > 0)
                     queues[t].Dequeue().EndLifetime();
             });
         
         var expected = Enumerable.Range(0, n).Where(e => e%2 != 0).SelectMany(e => Enumerable.Repeat(e, nt));
         p.CurrentItems().OrderBy(e => e.Value).Select(e => e.Value).AssertSequenceEquals(expected);
     }
 }
    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;
        });
    }
示例#6
0
        ///<summary>Returns a newly created mortal lifetime after giving eternal life to the previously created lifetime (if any).</summary>
        public Lifetime StartNextAndImmortalizePreviousLifetime()
        {
            var next = new LifetimeSource();
            var prev = Interlocked.Exchange(ref _active, next);

            prev?.ImmortalizeLifetime();
            return(next.Lifetime);
        }
    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);
        });
    }
示例#8
0
        ///<summary>Returns a newly created mortal lifetime after killing the previously created lifetime (if any).</summary>
        public Lifetime StartNextAndEndPreviousLifetime()
        {
            var next = new LifetimeSource();
            var prev = Interlocked.Exchange(ref _active, next);

            if (prev != null)
            {
                prev.EndLifetime();
            }
            return(next.Lifetime);
        }
 ///<summary>A lifetime that ends after the given duration has elapsed, in game time.</summary>
 public static Lifetime Delay(this Animation animation, TimeSpan duration)
 {
     var remaining = duration;
     var life = new LifetimeSource();
     animation.StepActions.Add(
         step => {
             remaining -= step.TimeStep;
             if (remaining < TimeSpan.Zero) life.EndLifetime();
         },
         life.Lifetime);
     return life.Lifetime;
 }
    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 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 IsEnumeratingWhileMutatingThreadSafe() {
        foreach (var repeat in Enumerable.Range(0, 5)) {
            var n = 5000;
            var nt = 4;
            var p = new PerishableCollection<int>();
            var queues = Enumerable.Range(0, 4).Select(e => new Queue<LifetimeSource>()).ToArray();
            TestUtil.ConcurrencyTest(
                threadCount: nt,
                callbackCount: n,
                repeatedWork: (t, i) => {
                    if (t%2 == 0) {
                        if (i%500 != 0) return;
                        var r = p.CurrentItems().OrderBy(e => e.Value).ToList();
                        (r.Count(e => e.Value%2 == 0 && !e.Lifetime.IsDead) < nt/2 * 20).AssertIsTrue();

                    } else {
                        var r = new LifetimeSource();
                        p.Add(i, r.Lifetime);
                        if (i % 2 == 0) queues[t].Enqueue(r);
                        if (queues[t].Count > 20)
                            queues[t].Dequeue().EndLifetime();
                    }
                },
                finalWork: t => {
                    while (queues[t].Count > 0)
                        queues[t].Dequeue().EndLifetime();
                });
        }
    }
 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 LifetimeImmortalityConcurrency() {
     var repeats = 5;
     foreach (var _ in Enumerable.Range(0, repeats)) {
         var n = 0;
         var source = new LifetimeSource();
         TestUtil.ConcurrencyTest(
             threadCount: 4,
             callbackCount: 3000,
             repeatedWork: (t, i) => source.Lifetime.WhenDead(() => Interlocked.Increment(ref n)),
             finalWork: t => source.ImmortalizeLifetime());
         n.AssertEquals(0);
     }
 }
 public void Dispose() {
     _source = null;
     GC.Collect();
     GC.WaitForPendingFinalizers();
 }
 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 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 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 void AllowsForGarbageCollection() {
        var mortal = new LifetimeSource();
        var lifes = new[] {
            Lifetime.Immortal,
            Lifetime.Dead,
            mortal.Lifetime
        };

        // callbacks are not kept when the lifetime is not mortal
        var f = ValidCallbackMaker;
        foreach (var life in lifes) {
            // pre-finished
            f.AssertCollectedAfter(e => Lifetime.Immortal.WhenDead(e, life));
            f.AssertCollectedAfter(e => Lifetime.Dead.WhenDead(e, life));
            f.AssertCollectedAfter(e => LimboedLifetime.WhenDead(e, life));
                
            // post-finished
            f.AssertCollectedAfter(e => { using (var limbod = new LimboLife()) limbod.Lifetime.WhenDead(e, life); });
            f.AssertCollectedAfter(e => { using (var blessed = new BlessedLife()) blessed.Lifetime.WhenDead(e, life); });
            f.AssertCollectedAfter(e => { using (var doomed = new DoomedLife()) doomed.Lifetime.WhenDead(e, life); });
        }

        // callbacks are not kept when the subscription lifetime is dead or dies
        f.AssertCollectedAfter(e => { using (var doomed = new DoomedLife()) mortal.Lifetime.WhenDead(e, doomed.Lifetime); });
        f.AssertCollectedAfter(e => mortal.Lifetime.WhenDead(e, Lifetime.Dead));

        // callbacks are kept when the lifetime is mortal and the subscription lifetime does not die
        f.AssertNotCollectedAfter(e => mortal.Lifetime.WhenDead(e, Lifetime.Immortal));
        f.AssertNotCollectedAfter(e => mortal.Lifetime.WhenDead(e, mortal.Lifetime));

        GC.KeepAlive(mortal);
    }
示例#21
0
 /// <summary>
 /// Returns a new lifetime source that automatically kills its exposed lifetime if the given lifetime dies.
 /// Note: If the given lifetimes has died or will die then using ImmortalizeLifetime on the result will eventually cause an InvalidOperationException.
 /// </summary>
 public static LifetimeSource CreateDependentSource(this Lifetime lifetime) {
     var dependentResult = new LifetimeSource();
     lifetime.WhenDead(dependentResult.EndLifetime, dependentResult.Lifetime);
     return dependentResult;
 }
    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();
    }
 ///<summary>A lifetime then ends after the lifetime created by a function (triggered when the given lifetime ends) ends.</summary>
 public static Lifetime ThenResurrect(this Lifetime lifetime, Func<Lifetime> resurrectedLifetimeFunc)
 {
     var r = new LifetimeSource();
     lifetime.WhenDead(() => resurrectedLifetimeFunc().WhenDead(r.EndLifetime));
     return r.Lifetime;
 }
示例#24
0
 ///<summary>Returns a newly created mortal lifetime after giving eternal life to the previously created lifetime (if any).</summary>
 public Lifetime StartNextAndImmortalizePreviousLifetime() {
     var next = new LifetimeSource();
     var prev = Interlocked.Exchange(ref _active, next);
     if (prev != null) prev.ImmortalizeLifetime();
     return next.Lifetime;
 }
    public void CreateDependentSource_Death() {
        var d1 = new LifetimeSource();
        var d2 = d1.Lifetime.CreateDependentSource();
        var d3 = d2.Lifetime.CreateDependentSource();
        d2.Lifetime.IsMortal.AssertIsTrue();
        d3.Lifetime.IsMortal.AssertIsTrue();

        d2.EndLifetime();
        d2.Lifetime.IsDead.AssertIsTrue();
        d3.Lifetime.IsDead.AssertIsTrue();

        d1.Lifetime.IsMortal.AssertIsTrue();
    }
    public void CreateDependentSource_Immortality() {
        var i1 = new LifetimeSource();
        var i2 = i1.Lifetime.CreateDependentSource();
        var i3 = i2.Lifetime.CreateDependentSource();

        i3.Lifetime.IsMortal.AssertIsTrue();
        i2.ImmortalizeLifetime();
        i2.Lifetime.IsImmortal.AssertIsTrue();

        i3.Lifetime.IsMortal.AssertIsTrue();
        i3.ImmortalizeLifetime();
        i3.Lifetime.IsImmortal.AssertIsTrue();

        i1.Lifetime.IsMortal.AssertIsTrue();
        TestUtil.ExpectException<InvalidOperationException>(i1.EndLifetime);
        i1.Lifetime.IsDead.AssertIsTrue();
    }
    public void CreateDependentSource_Garbage() {
        // an independent source will keep dependents alive
        var independent = new LifetimeSource();
        new Func<LifetimeSource>(() => independent.Lifetime.CreateDependentSource())
            .AssertNotCollectedAfter(e => e);
        independent.Lifetime.IsMortal.AssertIsTrue();

        // a dependent source does not keep its dependency alive
        LifetimeSource dependent = null;
        new Func<LifetimeSource>(() => new LifetimeSource())
            .AssertCollectedAfter(e => dependent = e.Lifetime.CreateDependentSource());
        dependent.Lifetime.IsMortal.AssertIsTrue();
    }
 public void ConstructionProperties() {
     var source = new LifetimeSource();
     var p = new Perishable<int>(2, source.Lifetime);
     p.Value.AssertEquals(2);
     p.Lifetime.AssertEquals(source.Lifetime);
 }
        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);
        }