Example #1
0
        public void TwoThreadsRemoveAt()
        {
            var       mapper = new Mapper <int>();
            const int Input  = 21;
            var       info   = new CircularBucket <string>(16);

            var startEvent = new ManualResetEvent(false);

            int[] done    = { 0 };
            var   threads = new[]
            {
                new Thread
                (
                    () =>
                {
                    int result;
                    bool isNew;
                    mapper.Set(0, Input, out isNew);
                    info.Add("A: Set - was new: " + isNew);
                    bool removed = mapper.RemoveAt(0, out result);
                    info.Add(removed ? "A: Removed" : "A: Could not remove");
                    Interlocked.Increment(ref done[0]);
                }
                ),
                new Thread
                (
                    () =>
                {
                    int result;
                    bool isNew;
                    mapper.Set(0, Input, out isNew);
                    info.Add("B: Set - was new: " + isNew);
                    bool removed = mapper.RemoveAt(0, out result);
                    info.Add(removed ? "B: Removed" : "B: Could not remove");
                    Interlocked.Increment(ref done[0]);
                }
                )
            };

            threads[0].Start();
            threads[1].Start();
            startEvent.Set();
            while (true)
            {
                if (Thread.VolatileRead(ref done[0]) == 2)
                {
                    break;
                }
                Thread.Sleep(0);
            }
            Assert.AreEqual(0, mapper.Count);
        }
        public void TwoThreadsSet()
        {
            var context = new LockableContext(16);
            var needle = new LockableNeedle<int>(5, context);
            int[] count = {0};

            var info = new CircularBucket<string>(64);

            Assert.AreEqual(5, needle.Value);
            Assert.Throws<InvalidOperationException>(() => needle.Value = 7);

            var threads = new []
            {
                new Thread(() =>
                {
                    using (context.Enter())
                    {
                        try
                        {
                            info.Add("First thread did enter.");
                            var found = needle.Value;
                            info.Add("First thread found: " + found + " will set: " + (found + 2));
                            needle.Value = found + 2;
                            info.Add("First thread set: " + needle.Value);
                            info.Add("First thread set count to: " + Interlocked.Increment(ref count[0]));
                            info.Add("First thread done.");
                        }
                        catch (Exception exc)
                        {
                            info.Add("First thread exception: " + exc.Message);
                            throw;
                        }
                    }
                    info.Add("First thread left.");
                }),
                new Thread(() =>
                {
                    using (context.Enter())
                    {
                        try
                        {
                            info.Add("Second thread did enter.");
                            var found = needle.Value;
                            info.Add("Second thread found: " + found + " will set: " + (found + 3));
                            needle.Value = found + 3;
                            info.Add("Second thread set: " + needle.Value);
                            info.Add("Second thread set count to: " + Interlocked.Increment(ref count[0]));
                            info.Add("Second thread done.");
                        }
                        catch (Exception exc)
                        {
                            info.Add("Second thread exception: " + exc.Message);
                            throw;
                        }
                    }
                    info.Add("Second thread left.");
                })
            };

            threads[0].Start();
            threads[1].Start();
            threads[0].Join();
            threads[1].Join();

            foreach (var item in info)
            {
                Trace.WriteLine(item);
            }

            Trace.WriteLine("Count = " + Thread.VolatileRead(ref count[0]));
            Trace.WriteLine("Found = " + needle.Value);

            Assert.IsTrue(needle.Value == 7 || needle.Value == 8 || needle.Value == 10);
        }
        public void TwoThreadsSet()
        {
            var context = new LockableContext(16);
            var needle  = new LockableNeedle <int>(5, context);

            int[] count = { 0 };

            var info = new CircularBucket <string>(64);

            Assert.AreEqual(5, needle.Value);
            Assert.Throws <InvalidOperationException>(() => needle.Value = 7);

            var threads = new[]
            {
                new Thread(() =>
                {
                    using (context.Enter())
                    {
                        try
                        {
                            info.Add("First thread did enter.");
                            var found = needle.Value;
                            info.Add("First thread found: " + found.ToString() + " will set: " + (found + 2).ToString());
                            needle.Value = found + 2;
                            info.Add("First thread set: " + needle.Value.ToString());
                            info.Add("First thread set count to: " + Interlocked.Increment(ref count[0]).ToString());
                            info.Add("First thread done.");
                        }
                        catch (Exception exc)
                        {
                            info.Add("First thread exception: " + exc.Message);
                            throw;
                        }
                    }
                    info.Add("First thread left.");
                }),
                new Thread(() =>
                {
                    using (context.Enter())
                    {
                        try
                        {
                            info.Add("Second thread did enter.");
                            var found = needle.Value;
                            info.Add("Second thread found: " + found.ToString() + " will set: " + (found + 3).ToString());
                            needle.Value = found + 3;
                            info.Add("Second thread set: " + needle.Value.ToString());
                            info.Add("Second thread set count to: " + Interlocked.Increment(ref count[0]).ToString());
                            info.Add("Second thread done.");
                        }
                        catch (Exception exc)
                        {
                            info.Add("Second thread exception: " + exc.Message);
                            throw;
                        }
                    }
                    info.Add("Second thread left.");
                })
            };

            threads[0].Start();
            threads[1].Start();
            threads[0].Join();
            threads[1].Join();

            foreach (var item in info)
            {
                Trace.WriteLine(item);
            }

            Trace.WriteLine("Count = " + Volatile.Read(ref count[0]).ToString());
            Trace.WriteLine("Found = " + needle.Value.ToString());

            Assert.IsTrue(needle.Value == 7 || needle.Value == 8 || needle.Value == 10);
        }
Example #4
0
        public void TransactionalDataStructure()
        {
            var  info   = new CircularBucket <string>(32);
            var  bucket = new NeedleBucket <int, Transact.Needle <int> >(index => index, value => new Transact.Needle <int>(value), 5);
            var  didA   = false;
            bool didB;

            using (var enteredWorkA = new ManualResetEvent(false))
            {
                using (var enteredWorkB = new ManualResetEvent(false))
                {
                    using (var done = new ManualResetEvent(false))
                    {
                        ManualResetEvent[] handles = { enteredWorkA, enteredWorkB, done };
                        ThreadPool.QueueUserWorkItem
                        (
                            _ =>
                        {
                            info.Add("Work A - start");
                            using (var transact = new Transact())
                            {
                                info.Add("Work A - enter");
                                handles[0].Set();
                                info.Add("Work A - reported, waiting");
                                handles[1].WaitOne();
                                info.Add("Work A - going");
                                // foreach will not trigger the creation of items
                                var got = new int[5];
                                var set = new int[5];
                                for (var index = 0; index < 5; index++)
                                {
                                    got[index] = bucket.GetNeedle(index).Value;
                                    set[index] = got[index] + 1;
                                    bucket.GetNeedle(index).Value = set[index];
                                }
                                info.Add(string.Format("Work A - Got: [{0}, {1}, {2}, {3}, {4}] - Set: [{5}, {6}, {7}, {8}, {9}]", got[0], got[1], got[2], got[3], got[4], set[0], set[1], set[2], set[3], set[4]));
                                if (!bucket.SequenceEqual(set))
                                {
                                    info.Add("Work A - ??");
                                }
                                info.Add("Work A - before commit");
                                didA = transact.Commit();
                                info.Add("Work A - after commit: " + didA);
                                if (didA != bucket.SequenceEqual(set))
                                {
                                    info.Add("Work A - ???");
                                }
                                info.Add("Work A - report");
                                handles[2].Set();
                                info.Add("Work A - done");
                            }
                        }
                        );
                        {
                            info.Add("Work B - start");
                            using (var transact = new Transact())
                            {
                                info.Add("Work B - waiting A to enter");
                                handles[0].WaitOne();
                                info.Add("Work B - telling Work A to go");
                                handles[1].Set();
                                info.Add("Work B - going");
                                // foreach will not trigger the creation of items
                                var got = new int[5];
                                var set = new int[5];
                                for (var index = 0; index < 5; index++)
                                {
                                    got[index] = bucket.GetNeedle(index).Value;
                                    set[index] = got[index] * 2;
                                    bucket.GetNeedle(index).Value = set[index];
                                }
                                info.Add(string.Format("Work B - Got: [{0}, {1}, {2}, {3}, {4}] - Set: [{5}, {6}, {7}, {8}, {9}]", got[0], got[1], got[2], got[3], got[4], set[0], set[1], set[2], set[3], set[4]));
                                if (!bucket.SequenceEqual(set))
                                {
                                    info.Add("Work B - ??");
                                }
                                info.Add("Work B - before commit");
                                didB = transact.Commit();
                                info.Add("Work B - after commit: " + didB);
                                if (didB != bucket.SequenceEqual(set))
                                {
                                    info.Add("Work B - ???");
                                }
                                info.Add("Work B - waiting report");
                                handles[2].WaitOne();
                                info.Add("Work B - done");
                            }
                        }
                        var result = bucket;
                        // These are more likely in debug mode
                        // (+1)
                        if (result.SequenceEqual(new[] { 1, 2, 3, 4, 5 }))
                        {
                            Assert.IsTrue(didA);
                            Assert.IsFalse(didB);
                            return;
                        }
                        // (*2)
                        if (result.SequenceEqual(new[] { 0, 2, 4, 6, 8 }))
                        {
                            Assert.IsFalse(didA);
                            Assert.IsTrue(didB);
                            return;
                        }
                        // This are more likely with optimization enabled
                        // (+1) and then (*2)
                        if (result.SequenceEqual(new[] { 2, 4, 6, 8, 10 }))
                        {
                            Assert.IsTrue(didA);
                            Assert.IsTrue(didB);
                            return;
                        }
                        // (*2) and then (+1)
                        if (result.SequenceEqual(new[] { 1, 3, 5, 7, 9 }))
                        {
                            Assert.IsTrue(didA);
                            Assert.IsTrue(didB);
                            return;
                        }
                        //---
                        if (result.SequenceEqual(new[] { 0, 1, 2, 3, 4 }))
                        {
                            Assert.IsFalse(didA);
                            Assert.IsFalse(didB);
                            return;
                        }
                        var found = result.ToArray();
                        Trace.WriteLine(" --- REPORT --- ");
                        foreach (var msg in info)
                        {
                            Trace.WriteLine(msg);
                        }
                        Assert.Fail("T_T - This is what was found: [{0}, {1}, {2}, {3}, {4}]", found[0], found[1], found[2], found[3], found[4]);
                    }
                }
            }
        }
Example #5
0
        private static bool WaitAsyncWaitCorrectlyExtractedExtracted(int maxCount, int maxTasks)
        {
            // Note: if WaitAsync takes to long, "x" can happen before the chunk of "a" has completed.
            var log      = new CircularBucket <string>((maxTasks * 4) + 2);
            var logCount = new CircularBucket <int>((maxTasks * 2) + 2);
            var source   = new CancellationTokenSource[1];

            using (source[0] = new CancellationTokenSource())
            {
                source[0].CancelAfter(TimeSpan.FromSeconds(100));
                var semaphore = new SemaphoreSlim[1];
                using (semaphore[0] = new SemaphoreSlim(0, maxCount))
                {
                    // No task should be able to enter semaphore at this point.
                    // Thus semaphore.CurrentCount should be 0
                    // We can directly check
                    Assert.AreEqual(0, semaphore[0].CurrentCount);
                    var padding = 0;
                    var tasks   = Enumerable.Range(0, maxTasks)
                                  .Select
                                  (
                        _ =>
                    {
                        return(Task.Factory.StartNew
                               (
                                   async() =>
                        {
                            log.Add("a");
                            await semaphore[0].WaitAsync
                            (
                                source[0].Token
                            ).ConfigureAwait(false);
                            Interlocked.Add(ref padding, 100);
                            logCount.Add(-1);
                            log.Add("b");
                            Thread.Sleep(1000 + padding);

                            // Calling release should give increasing results per chunk
                            log.Add("c");
                            var count = semaphore[0].Release();
                            logCount.Add(count);
                            log.Add("d");
                        }

                               ).Unwrap());
                    }

                                  ).ToArray();
                    Thread.Sleep(TimeSpan.FromMilliseconds(500));
                    log.Add("x");
                    var tmp = semaphore[0].Release(maxCount);
                    logCount.Add(-1);
                    logCount.Add(tmp);
                    Task.WaitAll(tasks, source[0].Token);
                    log.Add("z");
                }
            }

            // We should see:
            // maxTask a
            // 1 x
            // chunks of at most maxCount b, separated by chunks of c
            var sb = new StringBuilder(log.Capacity);

            foreach (var entry in log)
            {
                sb.Append(entry);
            }

            var str = sb.ToString();

            Console.WriteLine(str);

            // Make sure that threads have not sneaked in the ordering
            // If this has happen, it would have been a false failure
            // So, we will retry until it does not happen
            if (new Regex("c[bc]+d").IsMatch(str))
            {
                Console.WriteLine("...");
                return(false);
            }

            var regexSuccess = $"a{{{maxTasks}}}x(b{{0,{maxCount}}}(cd)+)+z";

            Assert.IsTrue(new Regex(regexSuccess).IsMatch(str));

            // The results of release increase *per chunk of c*.
            var last  = -1;
            var first = true;

            foreach (var entry in logCount)
            {
                Console.WriteLine(entry.ToString());
                if (entry == -1)
                {
                    first = true;
                    continue;
                }

                if (first)
                {
                    first = false;
                }
                else if (entry < last)
                {
                    Assert.Fail();
                }

                last = entry;
            }

            return(true);
        }
Example #6
0
        private static CircularBucket <int> WaitAsyncWaitCorrectlyExtractedCore(int maxCount, int maxTasks, out string str)
        {
            var log        = new CircularBucket <string>((maxTasks * 4) + 2);
            var logCount   = new CircularBucket <int>((maxTasks * 2) + 2);
            var semaphores = new SemaphoreSlim[1];
            var sources    = new CancellationTokenSource[1];

            using (sources[0] = new CancellationTokenSource(TimeSpan.FromSeconds(100)))
            {
                using (semaphores[0] = new SemaphoreSlim(0, maxCount))
                {
                    // No task should be able to enter semaphore at this point.
                    // Thus semaphore.CurrentCount should be 0
                    // We can directly check
                    Assert.AreEqual(0, semaphores[0].CurrentCount);
                    var padding = 0;
                    var tasks   =
                        Enumerable.Range(0, maxTasks).Select
                        (
                            _ => Task.Factory.StartNew
                            (
                                async() =>
                    {
                        log.Add("a");
                        await semaphores[0].WaitAsync(sources[0].Token);
                        Interlocked.Add(ref padding, 100);
                        logCount.Add(-1);
                        log.Add("b");
                        Thread.Sleep(1000 + padding);
                        // Calling release should give increasing results per chunk
                        log.Add("c");
                        var count = semaphores[0].Release();
                        logCount.Add(count);
                        log.Add("d");
                    }
                            ).Unwrap()
                        ).ToArray();
                    Thread.Sleep(TimeSpan.FromMilliseconds(500));
                    log.Add("x");
                    var tmp = semaphores[0].Release(maxCount);
                    logCount.Add(-1);
                    logCount.Add(tmp);
                    Task.WaitAll(tasks, sources[0].Token);
                    log.Add("z");
                }
            }

            // We should see:
            // maxTask a
            // 1 x
            // chunks of at most maxCount b, separated by chunks of c
            var sb = new StringBuilder(log.Capacity);

            foreach (var entry in log)
            {
                sb.Append(entry);
            }

            str = sb.ToString();
            return(logCount);
        }
        public void EditWhileIteratingThreaded()
        {
            var d = new ConcurrentDictionary <string, string>();

            Assert.IsTrue(d.TryAdd("0", "1"));
            Assert.IsTrue(d.TryAdd("a", "b"));
            int[] expectedCount = { 2 };
            Assert.AreEqual(expectedCount[0], d.Count);
            string a          = null;
            var    foundCount = 0;
            var    didAdd     = 0;
            var    didRemove  = 0;
            var    found      = new CircularBucket <string>(16);

            ThreadStart remover = () =>
            {
                var removed = d.TryRemove("a", out a);
                if (Thread.VolatileRead(ref didRemove) == 0 && removed)
                {
                    expectedCount[0]--;
                }
                if (removed)
                {
                    Interlocked.CompareExchange(ref didRemove, 1, 0);
                }
            };

            ThreadStart adder = () =>
            {
                var added = d.TryAdd("c", "d");
                if (Thread.VolatileRead(ref didAdd) == 0 && added)
                {
                    expectedCount[0]++;
                }
                if (added)
                {
                    Interlocked.CompareExchange(ref didAdd, 1, 0);
                }
            };

            // MSDN says: "it does not represent a moment-in-time snapshot of the dictionary."
            // And also "The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called."
            foreach (var item in d)
            {
                found.Add(item.Key);
                foundCount++;
                var old = expectedCount[0];
                Assert.AreEqual(expectedCount[0], d.Count);
                {
                    var t = new Thread(remover);
                    t.Start();
                    t.Join();
                }
                if (foundCount == 1)
                {
                    Assert.AreNotEqual(old, expectedCount[0]);
                }
                else
                {
                    Assert.AreEqual(old, expectedCount[0]);
                }
                Assert.AreEqual(expectedCount[0], d.Count);
                old = expectedCount[0];
                {
                    var t = new Thread(adder);
                    t.Start();
                    t.Join();
                }
                if (foundCount == 1)
                {
                    Assert.AreNotEqual(old, expectedCount[0]);
                }
                else
                {
                    Assert.AreEqual(old, expectedCount[0]);
                }
                Assert.AreEqual(expectedCount[0], d.Count);
            }
            Assert.IsNull(a);
            var array = found.ToArray();

            if (!array.IsSupersetOf(new[] { "0", "c" }))
            {
                foreach (var item in array)
                {
                    Console.WriteLine(item);
                }
                Assert.Fail();
            }
            Assert.AreEqual(2, expectedCount[0]);
            Assert.AreEqual(1, didAdd);
            Assert.AreEqual(1, didRemove);
            Assert.IsTrue(foundCount - expectedCount[0] < 2, "foundCount: {0}, expectedCount:{1}", foundCount, expectedCount[0]);
            Assert.AreEqual(expectedCount[0], d.Count);
        }
        public void EditWhileIterating()
        {
            var d = new ConcurrentDictionary <string, string>();

            Assert.IsTrue(d.TryAdd("0", "1"));
            Assert.IsTrue(d.TryAdd("a", "b"));
            var expectedCount = 2;

            Assert.AreEqual(expectedCount, d.Count);
            string a          = null;
            var    foundCount = 0;
            var    didAdd     = false;
            var    didRemove  = false;
            var    found      = new CircularBucket <string>(16);

            // MSDN says: "it does not represent a moment-in-time snapshot of the dictionary."
            // And also "The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called."
            // Note: There is no guarantee the items are in insert order
            foreach (var item in d)
            {
                found.Add(item.Key);
                foundCount++;
                Assert.AreEqual(expectedCount, d.Count);
                var removed = d.TryRemove("a", out a);
                if (didRemove)
                {
                    Assert.IsFalse(removed);
                }
                else
                {
                    Assert.IsTrue(removed);
                    expectedCount--;
                }
                didRemove = didRemove | removed;
                Assert.IsTrue(didRemove);
                Assert.AreEqual(expectedCount, d.Count);
                var added = d.TryAdd("c", "d");
                if (didAdd)
                {
                    Assert.IsFalse(added);
                }
                else
                {
                    Assert.IsTrue(added);
                    expectedCount++;
                }
                didAdd = didAdd | added;
                Assert.IsTrue(didAdd);
                Assert.AreEqual(expectedCount, d.Count);
            }
            Assert.IsNull(a);
            var array = found.ToArray();

            if (!array.IsSupersetOf(new[] { "0", "c" }))
            {
                foreach (var item in array)
                {
                    Console.WriteLine(item);
                }
                Assert.Fail();
            }
            Assert.AreEqual(2, expectedCount);
            Assert.AreEqual(true, didAdd);
            Assert.AreEqual(true, didRemove);
            Assert.IsTrue(foundCount - expectedCount < 2, "foundCount: {0}, expectedCount:{1}", foundCount, expectedCount);
            Assert.AreEqual(expectedCount, d.Count);
        }
        public void TwoThreadsRemoveAt()
        {
            var mapper = new Mapper<int>();
            const int Input = 21;
            var info = new CircularBucket<string>(16);

            var startEvent = new ManualResetEvent(false);
            int[] done = {0};
            var threads = new[]
            {
                new Thread
                (
                    () =>
                    {
                        int result;
                        bool isNew;
                        mapper.Set(0, Input, out isNew);
                        info.Add("A: Set - was new: " + isNew);
                        bool removed = mapper.RemoveAt(0, out result);
                        info.Add(removed ? "A: Removed" : "A: Could not remove");
                        Interlocked.Increment(ref done[0]);
                    }
                ),
                new Thread
                (
                    () =>
                    {
                        int result;
                        bool isNew;
                        mapper.Set(0, Input, out isNew);
                        info.Add("B: Set - was new: " + isNew);
                        bool removed = mapper.RemoveAt(0, out result);
                        info.Add(removed ? "B: Removed" : "B: Could not remove");
                        Interlocked.Increment(ref done[0]);
                    }
                )
            };
            threads[0].Start();
            threads[1].Start();
            startEvent.Set();
            while (true)
            {
                if (Thread.VolatileRead(ref done[0]) == 2)
                {
                    break;
                }
                Thread.Sleep(0);
            }
            Assert.AreEqual(0, mapper.Count);
        }