// 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
{
///
/// 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.
///
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";
}
}
///
/// 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.
///
public static void DisposeBeforeAddRemove()
{
Impl(0);
}
///
/// 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.
///
public static void DisposeDuringAddRemove()
{
Impl(1);
}
///
/// 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.
///
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