public void ConcurrentWriteAndAdviseTest()
        {
            const int threadsCount = 10;

            for (int i = 0; i < 200; i++)
            {
                using var def = new LifetimeDefinition();
                var lifetime = def.Lifetime;

                var prop = new WriteOnceProperty <int>();

                var value1 = new AtomicValue();
                var count  = 0;

                var tasks = Enumerable.Range(0, threadsCount).Select(j => Task.Factory.StartNew(() =>
                {
                    Interlocked.Increment(ref count);
                    SpinWait.SpinUntil(() => Memory.VolatileRead(ref count) == threadsCount); // sync threads

                    if (!prop.SetIfEmpty(j))
                    {
                        return;
                    }

                    if (!value1.SetIfDefault(j))
                    {
                        Assert.Fail("Value must be written once");
                    }
                }, lifetime)).ToArray();

                var values = Enumerable.Range(0, i).Select(j =>
                {
                    var localValue = new AtomicValue();

                    prop.Advise(lifetime, v =>
                    {
                        if (!localValue.SetIfDefault(v))
                        {
                            Assert.Fail("Handled must not be called twice");
                        }
                    });
                    return(localValue);
                }).ToArray();

                Assert.IsTrue(Task.WaitAll(tasks, TimeSpan.FromMinutes(1)), "Task.WaitAll(tasks, TimeSpan.FromMinutes(1))");

                value1.AssertNonDefault();

                if (values.Length != 0)
                {
                    var value = values.Select(x => x.Value).Distinct().Single();
                    Assert.AreEqual(value, value1.Value);
                }

                Assert.AreEqual(value1.Value, prop.Value);

                prop.fireInternal(10000);
            }
        }
        public void SimpleTest()
        {
            using var def = new LifetimeDefinition();
            var lifetime = def.Lifetime;
            var prop     = new WriteOnceProperty <int>();

            Assert.Throws <InvalidOperationException>(() => { var _ = prop.Value; });

            var v1 = 0;

            prop.Advise(lifetime, value => v1 = value);

            Assert.AreEqual(0, v1);
            Assert.IsFalse(prop.Maybe.HasValue);

            Assert.IsTrue(prop.SetIfEmpty(1));
            Assert.AreEqual(1, v1);
            Assert.AreEqual(1, prop.Value);

            Assert.IsFalse(prop.SetIfEmpty(2));
            Assert.AreEqual(1, v1);
            Assert.AreEqual(1, prop.Value);

            Assert.Throws <InvalidOperationException>(() => prop.Value = 2);

            Assert.AreEqual(1, v1);
            Assert.AreEqual(1, prop.Value);


            var v3 = 0;

            prop.Advise(lifetime, value => v3 = value);

            Assert.AreEqual(1, v1);
            Assert.AreEqual(1, v3);
            Assert.AreEqual(1, prop.Value);

            Assert.AreEqual(1, v1);
            Assert.AreEqual(1, v3);
            Assert.AreEqual(1, prop.Value);

            prop.fireInternal(3);

            Assert.AreEqual(1, v1);
            Assert.AreEqual(1, v3);
            Assert.AreEqual(1, prop.Value);
        }