private static void RefCountWithPost_(IEnumerable<Tuple<int, int>> parameters) { var worker = new Thread(() => { SynchronizationContext.SetSynchronizationContext(new MySyncCtx()); foreach (var p in parameters) { var N = p.Item1; var M = p.Item2; Console.Write("N = {0}, M = {1} - ", N, M); var bar = new Bar(); var foo = Observable.FromEventPattern<FooEventArgs>(h => { /*Console.Write("+");*/ bar.Foo += h; }, h => { bar.Foo -= h; /*Console.Write("-"); */}); var e = new ManualResetEvent(false); var cd = new CountdownEvent(M * 2); for (int i = 0; i < M; i++) { var f = new SingleAssignmentDisposable(); ThreadPool.QueueUserWorkItem(_ => { f.Disposable = foo.Subscribe(__ => { /*Console.Write("!");*/ }); cd.Signal(); }); ThreadPool.QueueUserWorkItem(_ => { f.Dispose(); cd.Signal(); }); } var hasObserved = 0; Console.Write("{SB}"); var d = foo.Subscribe(x => { // // [on BARTDE-M6500 with CPU and RAM pressure] // // Up to 8K concurrent observers, we typically don't see a time gap (expected worst-case behavior). // The code below uses an event to check the desired behavior of eventually tuning in to the event stream. // Console.Write("&" + x.EventArgs.Qux); e.Set(); Interlocked.Exchange(ref hasObserved, 1); }); Console.Write("{SE}"); var t = new Thread(() => { Console.Write("{TB}"); var i = 0; while (Thread.VolatileRead(ref hasObserved) == 0) bar.OnFoo(i++); Console.Write("{TE}"); }); t.Start(); t.Join(); cd.Wait(); e.WaitOne(); d.Dispose(); Console.WriteLine("."); } }); worker.Start(); worker.Join(); }
private static void RefCount_(IEnumerable<Tuple<int, int>> parameters) { foreach (var p in parameters) { var N = p.Item1; var M = p.Item2; Console.Write("N = {0}, M = {1} - ", N, M); var bar = new Bar(); var foo = Observable.FromEventPattern<FooEventArgs>(h => { Console.Write("+"); bar.Foo += h; }, h => { bar.Foo -= h; Console.Write("-"); }); var res = new List<int>(); var n = 0; var e = new ManualResetEvent(false); var cd = new CountdownEvent(M * 2); for (int i = 0; i < M; i++) { var f = new SingleAssignmentDisposable(); ThreadPool.QueueUserWorkItem(_ => { f.Disposable = foo.Subscribe(__ => { Console.Write("!"); }); cd.Signal(); }); ThreadPool.QueueUserWorkItem(_ => { f.Dispose(); cd.Signal(); }); } Console.Write("{SB}"); var d = foo.Subscribe(x => { //Console.Write("&"); if (++n == N) e.Set(); res.Add(x.EventArgs.Qux); }); Console.Write("{SE}"); var t = new Thread(() => { Console.Write("{TB}"); for (int i = 0; i < N; i++) bar.OnFoo(i); Console.Write("{TE}"); }); t.Start(); t.Join(); cd.Wait(); e.WaitOne(); d.Dispose(); if (!res.SequenceEqual(Enumerable.Range(0, N))) { Console.WriteLine("Panic!"); break; } Console.WriteLine("."); } }