diff options
Diffstat (limited to 'Rx.NET/Tests.System.Reactive/Stress/Core/Disposables')
4 files changed, 541 insertions, 0 deletions
diff --git a/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/Composite.cs b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/Composite.cs new file mode 100644 index 0000000..eccb08f --- /dev/null +++ b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/Composite.cs @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if STRESS +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Reflection; +using System.Threading; + +namespace ReactiveTests.Stress.Disposables +{ + public class Composite + { + /// <summary> + /// Allocates a CompositeDisposable and performs random Add and Remove operations. Checks that all contained disposables get properly disposed. + /// The CompositeDisposable is disposed either at the start, at the end, or at a random time. + /// </summary> + public static void Potpourri() + { + Console.Title = MethodInfo.GetCurrentMethod().Name + " - 0% complete"; + + for (int i = 1; i <= 100; i++) + { + for (int j = 0; j < 10; j++) + { + DisposeBeforeAddRemove(); + DisposeDuringAddRemove(); + DisposeDuringAddRemove(); + DisposeDuringAddRemove(); + DisposeAfterAddRemove(); + } + + Console.Title = MethodInfo.GetCurrentMethod().Name + " - " + i + "% complete"; + } + } + + /// <summary> + /// Allocates a CompositeDisposable and performs random Add and Remove operations. Checks that all contained disposables get properly disposed. + /// The CompositeDisposable is disposed at the start. + /// </summary> + public static void DisposeBeforeAddRemove() + { + Impl(0); + } + + /// <summary> + /// Allocates a CompositeDisposable and performs random Add and Remove operations. Checks that all contained disposables get properly disposed. + /// The CompositeDisposable is disposed at a random time. + /// </summary> + public static void DisposeDuringAddRemove() + { + Impl(1); + } + + /// <summary> + /// Allocates a CompositeDisposable and performs random Add and Remove operations. Checks that all contained disposables get properly disposed. + /// The CompositeDisposable is disposed at the end. + /// </summary> + public static void DisposeAfterAddRemove() + { + Impl(2); + } + + static void Impl(int disposeAt) + { + var rand = new Random(); + + var g = new CompositeDisposable(); + + Console.Write("Dispose @ = {0} - ", disposeAt); + + if (disposeAt == 0) + { + g.Dispose(); + Console.Write("{GD} "); + } + + if (disposeAt == 1) + { + var sleep = rand.Next(0, 5) > 1 /* 60% chance */ ? rand.Next(2, 1000) : 0; + + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep); + g.Dispose(); + Console.Write("{GD} "); + }); + } + + var n = rand.Next(0, 1000); + var cd = new CountdownEvent(n); + + var ds = Enumerable.Range(0, n).Select(_ => Disposable.Create(() => cd.Signal())).ToArray(); + + var m = rand.Next(1, 100); + var jobs = ds.GroupBy(_ => rand.Next() % m).Select(Enumerable.ToList).ToList(); + + Console.Write("N = {0}, M = {1} - ", n, m); + + var done = new CountdownEvent(jobs.Count); + + foreach (var job in jobs) + { + var sleep = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + var sleepAt = Enumerable.Range(0, rand.Next(0, job.Count) / rand.Next(1, 100)).ToArray(); + var sleeps = sleepAt.Select(_ => rand.Next(0, 50)).ToArray(); + + var rem = rand.Next(0, 3) == 0; /* 33% chance */ + var remAt = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + + var mine = job; + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep); + + var j = 0; + foreach (var d in mine) + { + var dd = d; + + if (sleepAt.Contains(j)) + Helpers.SleepOrSpin(sleeps[j]); + + g.Add(dd); + Console.Write("+"); + + if (rem) + { + ThreadPool.QueueUserWorkItem(__ => + { + Helpers.SleepOrSpin(remAt); + g.Remove(dd); + Console.Write("-"); + }); + } + + j++; + } + + done.Signal(); + }); + } + + done.Wait(); + + if (disposeAt == 2) + { + g.Dispose(); + Console.Write("{GD} "); + } + + cd.Wait(); + + Console.WriteLine("."); + } + } +} +#endif
\ No newline at end of file diff --git a/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/RefCount.cs b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/RefCount.cs new file mode 100644 index 0000000..f2364a8 --- /dev/null +++ b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/RefCount.cs @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if STRESS +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reactive.Disposables; +using System.Reflection; +using System.Threading; + +namespace ReactiveTests.Stress.Disposables +{ + public class RefCount + { + /// <summary> + /// Disposes the primary disposable first, allocates a number of dependents on different threads, and disposes them on different threads. + /// Ref count should reach zero, and the inner disposable should be called. + /// </summary> + public static void PrimaryFirst_DependentsTrigger() + { + Console.Title = MethodInfo.GetCurrentMethod().Name + " - 0% complete"; + + var rnd = new Random(); + + for (int i = 1; i <= 100; i++) + { + Impl(true, false, new[] { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }); + Impl(true, false, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + Impl(true, false, Enumerable.Range(0, 10).Select(_ => rnd.Next(0, 1000))); + + Console.Title = MethodInfo.GetCurrentMethod().Name + " - " + i + "% complete"; + } + } + + /// <summary> + /// Allocates a number of dependents on different threads, disposes them on different threads, and disposes the primary disposable last. + /// Ref count should reach zero, and the inner disposable should be called. + /// </summary> + public static void DependentsFirst_PrimaryTrigger() + { + Console.Title = MethodInfo.GetCurrentMethod().Name + " - 0% complete"; + + var rnd = new Random(); + + for (int i = 1; i <= 100; i++) + { + Impl(false, false, new[] { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }); + Impl(false, false, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + Impl(false, false, Enumerable.Range(0, 10).Select(_ => rnd.Next(0, 1000))); + + Console.Title = MethodInfo.GetCurrentMethod().Name + " - " + i + "% complete"; + } + } + + /// <summary> + /// Allocates a number of dependents on different threads, disposes them on different threads, and disposes the primary disposable at a random time. + /// Ref count should reach zero, and the inner disposable should be called. + /// </summary> + public static void DependentsFirst_PrimaryRandom() + { + Console.Title = MethodInfo.GetCurrentMethod().Name + " - 0% complete"; + + var rnd = new Random(); + + for (int i = 1; i <= 100; i++) + { + Impl(false, true, new[] { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }); + Impl(false, true, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + Impl(false, true, Enumerable.Range(0, 10).Select(_ => rnd.Next(0, 1000))); + + Console.Title = MethodInfo.GetCurrentMethod().Name + " - " + i + "% complete"; + } + } + + private static void Impl(bool primaryFirst, bool primaryRandom, IEnumerable<int> nDependents) + { + var rand = new Random(); + + foreach (var n in nDependents) + { + var e = new ManualResetEvent(false); + var hasDependent = new ManualResetEvent(false); + var r = new RefCountDisposable(Disposable.Create(() => { e.Set(); })); + + var d = default(IDisposable); + if (primaryFirst) + { + d = r.GetDisposable(); + r.Dispose(); + } + else if (primaryRandom) + { + var sleep = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + + ThreadPool.QueueUserWorkItem(_ => + { + hasDependent.WaitOne(); + Helpers.SleepOrSpin(sleep); + r.Dispose(); + }); + + if (n == 0) + hasDependent.Set(); + } + + Console.Write(n + " - "); + + var cd = new CountdownEvent(n * 2); + for (int i = 0; i < n; i++) + { + var j = i; + + var sleep1 = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + var sleep2 = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + var sleep3 = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep1); + + Console.Write("+"); + + var f = r.GetDisposable(); + + if (j == 0) + hasDependent.Set(); + + Helpers.SleepOrSpin(sleep2); + + ThreadPool.QueueUserWorkItem(__ => + { + Helpers.SleepOrSpin(sleep3); + + f.Dispose(); + + Console.Write("-"); + + cd.Signal(); + }); + + cd.Signal(); + }); + } + + cd.Wait(); + + if (primaryFirst) + d.Dispose(); + else if (!primaryRandom) + r.Dispose(); + + e.WaitOne(); + + Console.WriteLine("."); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/Serial.cs b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/Serial.cs new file mode 100644 index 0000000..a9b87d1 --- /dev/null +++ b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/Serial.cs @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if STRESS +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Reflection; +using System.Threading; + +namespace ReactiveTests.Stress.Disposables +{ + public class Serial + { + /// <summary> + /// Allocates a SerialDisposable and performs random assignment operations. Checks that all contained disposables get properly disposed. + /// The SerialDisposable is disposed either at the start, at the end, or at a random time. + /// </summary> + public static void RandomAssignAndDispose() + { + Console.Title = MethodInfo.GetCurrentMethod().Name + " - 0% complete"; + + for (int i = 1; i <= 100; i++) + { + for (int j = 0; j < 10; j++) + { + DisposeBeforeAssign(); + DisposeDuringAssign(); + DisposeDuringAssign(); + DisposeDuringAssign(); + DisposeAfterAssign(); + } + + Console.Title = MethodInfo.GetCurrentMethod().Name + " - " + i + "% complete"; + } + } + + /// <summary> + /// Allocates a SerialDisposable and performs random assignment operations. Checks that all contained disposables get properly disposed. + /// The SerialDisposable is disposed at the start. + /// </summary> + public static void DisposeBeforeAssign() + { + Impl(0); + } + + /// <summary> + /// Allocates a SerialDisposable and performs random assignment operations. Checks that all contained disposables get properly disposed. + /// The SerialDisposable is disposed at a random time. + /// </summary> + public static void DisposeDuringAssign() + { + Impl(1); + } + + /// <summary> + /// Allocates a SerialDisposable and performs random assignment operations. Checks that all contained disposables get properly disposed. + /// The SerialDisposable is disposed at the end. + /// </summary> + public static void DisposeAfterAssign() + { + Impl(2); + } + + static void Impl(int disposeAt) + { + var rand = new Random(); + + var s = new SerialDisposable(); + + Console.Write("Dispose @ = {0} - ", disposeAt); + + if (disposeAt == 0) + { + s.Dispose(); + Console.Write("{SD} "); + } + + if (disposeAt == 1) + { + var sleep = rand.Next(0, 5) > 1 /* 60% chance */ ? rand.Next(2, 1000) : 0; + + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep); + s.Dispose(); + Console.Write("{SD} "); + }); + } + + var n = rand.Next(0, 1000); + var cd = new CountdownEvent(n); + + var ds = Enumerable.Range(0, n).Select(_ => Disposable.Create(() => cd.Signal())).ToArray(); + + var m = rand.Next(1, 100); + var jobs = ds.GroupBy(_ => rand.Next() % m).Select(Enumerable.ToList).ToList(); + + Console.Write("N = {0}, M = {1} - ", n, m); + + var done = new CountdownEvent(jobs.Count); + + foreach (var job in jobs) + { + var sleep = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + var sleepAt = Enumerable.Range(0, rand.Next(0, job.Count) / rand.Next(1, 100)).ToArray(); + var sleeps = sleepAt.Select(_ => rand.Next(0, 50)).ToArray(); + + var rem = rand.Next(0, 3) == 0; /* 33% chance */ + var remAt = rand.Next(0, 10) == 0 /* 10% chance */ ? rand.Next(2, 100) : 0; + + var mine = job; + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep); + + var j = 0; + foreach (var d in mine) + { + if (sleepAt.Contains(j)) + Helpers.SleepOrSpin(sleeps[j]); + + s.Disposable = d; + Console.Write("+"); + + j++; + } + + done.Signal(); + }); + } + + done.Wait(); + + if (disposeAt == 2) + { + s.Dispose(); + Console.Write("{SD} "); + } + + cd.Wait(); + + Console.WriteLine("."); + } + } +} +#endif
\ No newline at end of file diff --git a/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/SingleAssignment.cs b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/SingleAssignment.cs new file mode 100644 index 0000000..b558a98 --- /dev/null +++ b/Rx.NET/Tests.System.Reactive/Stress/Core/Disposables/SingleAssignment.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if STRESS +using System; +using System.Reactive.Disposables; +using System.Reflection; +using System.Threading; + +namespace ReactiveTests.Stress.Disposables +{ + public class SingleAssignment + { + /// <summary> + /// Allocates a SingleAssignmentDisposable and assigns a disposable object at a random time. Also disposes the container at a random time. + /// Expected behavior is to see the assigned disposable getting disposed no matter what. + /// </summary> + public static void RandomAssignAndDispose() + { + Console.Title = MethodInfo.GetCurrentMethod().Name + " - 0% complete"; + + for (int i = 1; i <= 100; i++) + { + for (int j = 0; j < 10; j++) + { + Impl(); + } + + Console.Title = MethodInfo.GetCurrentMethod().Name + " - " + i + "% complete"; + } + } + + private static void Impl() + { + var rand = new Random(); + + for (int i = 0; i < 1000; i++) + { + var d = new SingleAssignmentDisposable(); + var e = new ManualResetEvent(false); + var cd = new CountdownEvent(2); + + var sleep1 = rand.Next(0, 1) == 0 ? 0 : rand.Next(2, 100); + var sleep2 = rand.Next(0, 1) == 0 ? 0 : rand.Next(2, 100); + + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep1); + + Console.Write("{DB} "); + d.Dispose(); + Console.Write("{DE} "); + + cd.Signal(); + }); + + ThreadPool.QueueUserWorkItem(_ => + { + Helpers.SleepOrSpin(sleep2); + + Console.Write("{AB} "); + d.Disposable = Disposable.Create(() => e.Set()); + Console.Write("{AE} "); + + cd.Signal(); + }); + + e.WaitOne(); + cd.Wait(); + + Console.WriteLine("."); + } + } + } +} +#endif
\ No newline at end of file |