# HG changeset patch # User cin # Date 2018-01-31 08:28:38 # Node ID d82909310094b010df5b84f0e85f3229286754ae # Parent 5cb4826c2c2a97b8ce89dd5ba957c9a0ead10477 Implab.Test moved to xunit Complete set of PromiseHelpers (Then, Catch, Finally) Removed obsolete types ICancellable, ICancellationToken diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs deleted file mode 100644 --- a/Implab.Test/AsyncTests.cs +++ /dev/null @@ -1,878 +0,0 @@ -using System; -using System.Reflection; -using System.Threading; -using Implab.Parallels; - -#if MONO - -using NUnit.Framework; -using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; -using TestMethodAttribute = NUnit.Framework.TestAttribute; - -#else - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -#endif - -namespace Implab.Test { - [TestClass] - public class AsyncTests { - [TestMethod] - public void ResolveTest() { - int res = -1; - var p = new Promise(); - p.Then(x => res = x); - p.Resolve(100); - - Assert.AreEqual(100, res); - } - - [TestMethod] - public void RejectTest() { - int res = -1; - Exception err = null; - - var p = new Promise(); - p.Then( - x => res = x, - e => { - err = e; - return -2; - } - ); - p.Reject(new ApplicationException("error")); - - Assert.AreEqual(res, -1); - Assert.AreEqual(err.Message, "error"); - - } - - [TestMethod] - public void CancelExceptionTest() { - var p = new Promise(); - p.CancelOperation(null); - - var p2 = p.Then(x => x, null, reason => { - throw new ApplicationException("CANCELLED"); - }); - - try { - p2.Join(); - Assert.Fail(); - } catch (ApplicationException err) { - Assert.AreEqual("CANCELLED", err.InnerException.Message); - } - - } - - [TestMethod] - public void ContinueOnCancelTest() { - var p = new Promise(); - p.CancelOperation(null); - - var p2 = p - .Then(x => x, null, reason => { - throw new ApplicationException("CANCELLED"); - }) - .Then(x => x, e => true); - - Assert.AreEqual(true, p2.Join()); - } - - [TestMethod] - public void JoinSuccessTest() { - var p = new Promise(); - p.Resolve(100); - Assert.AreEqual(p.Join(), 100); - } - - [TestMethod] - public void JoinFailTest() { - var p = new Promise(); - p.Reject(new ApplicationException("failed")); - - try { - p.Join(); - throw new ApplicationException("WRONG!"); - } catch (TargetInvocationException err) { - Assert.AreEqual(err.InnerException.Message, "failed"); - } catch { - Assert.Fail("Got wrong excaption"); - } - } - - [TestMethod] - public void MapTest() { - var p = new Promise(); - - var p2 = p.Then(x => x.ToString()); - p.Resolve(100); - - Assert.AreEqual(p2.Join(), "100"); - } - - [TestMethod] - public void FixErrorTest() { - var p = new Promise(); - - var p2 = p.Then(x => x, e => 101); - - p.Reject(new Exception()); - - Assert.AreEqual(p2.Join(), 101); - } - - [TestMethod] - public void ChainTest() { - var p1 = new Promise(); - - var p3 = p1.Chain(x => { - var p2 = new Promise(); - p2.Resolve(x.ToString()); - return p2; - }); - - p1.Resolve(100); - - Assert.AreEqual(p3.Join(), "100"); - } - - [TestMethod] - public void ChainFailTest() { - var p1 = new Promise(); - - var p3 = p1.Chain(x => { - var p2 = new Promise(); - p2.Reject(new Exception("DIE!!!")); - return p2; - }); - - p1.Resolve(100); - - Assert.IsTrue(p3.IsResolved); - } - - [TestMethod] - public void PoolTest() { - var pid = Thread.CurrentThread.ManagedThreadId; - var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId); - - Assert.AreNotEqual(pid, p.Join()); - } - - [TestMethod] - public void WorkerPoolSizeTest() { - var pool = new WorkerPool(5, 10, 1); - - Assert.AreEqual(5, pool.PoolSize); - - pool.Invoke(() => { Thread.Sleep(100000000); return 10; }); - pool.Invoke(() => { Thread.Sleep(100000000); return 10; }); - pool.Invoke(() => { Thread.Sleep(100000000); return 10; }); - - Assert.AreEqual(5, pool.PoolSize); - - for (int i = 0; i < 100; i++) - pool.Invoke(() => { Thread.Sleep(100000000); return 10; }); - Thread.Sleep(200); - Assert.AreEqual(10, pool.PoolSize); - - pool.Dispose(); - } - - [TestMethod] - public void WorkerPoolCorrectTest() { - var pool = new WorkerPool(0,1000,100); - - const int iterations = 1000; - int pending = iterations; - var stop = new ManualResetEvent(false); - - var count = 0; - for (int i = 0; i < iterations; i++) { - pool - .Invoke(() => 1) - .Then(x => Interlocked.Add(ref count, x)) - .Then(x => Math.Log10(x)) - .On(() => { - Interlocked.Decrement(ref pending); - if (pending == 0) - stop.Set(); - }, PromiseEventType.All); - } - - stop.WaitOne(); - - Assert.AreEqual(iterations, count); - Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads); - pool.Dispose(); - - } - - [TestMethod] - public void WorkerPoolDisposeTest() { - var pool = new WorkerPool(5, 20); - Assert.AreEqual(5, pool.PoolSize); - pool.Dispose(); - Thread.Sleep(500); - Assert.AreEqual(0, pool.PoolSize); - pool.Dispose(); - } - - [TestMethod] - public void MTQueueTest() { - var queue = new SimpleAsyncQueue(); - int res; - - queue.Enqueue(10); - Assert.IsTrue(queue.TryDequeue(out res)); - Assert.AreEqual(10, res); - Assert.IsFalse(queue.TryDequeue(out res)); - - for (int i = 0; i < 1000; i++) - queue.Enqueue(i); - - for (int i = 0; i < 1000; i++) { - queue.TryDequeue(out res); - Assert.AreEqual(i, res); - } - - int writers = 0; - int readers = 0; - var stop = new ManualResetEvent(false); - int total = 0; - var ticks = Environment.TickCount; - - const int itemsPerWriter = 1000000; - const int writersCount = 10; - - for (int i = 0; i < writersCount; i++) { - Interlocked.Increment(ref writers); - AsyncPool - .RunThread(() => { - for (int ii = 0; ii < itemsPerWriter; ii++) { - queue.Enqueue(1); - } - return 1; - }) - .On(() => Interlocked.Decrement(ref writers), PromiseEventType.All); - } - - for (int i = 0; i < 10; i++) { - Interlocked.Increment(ref readers); - AsyncPool - .RunThread(() => { - int t; - do { - while (queue.TryDequeue(out t)) - Interlocked.Add(ref total, t); - } while (writers > 0); - return 1; - }) - .On(() => { - Interlocked.Decrement(ref readers); - if (readers == 0) - stop.Set(); - }, PromiseEventType.All); - } - - stop.WaitOne(); - - Console.WriteLine("{0} in {1}ms", total, Environment.TickCount - ticks); - - Assert.AreEqual(itemsPerWriter * writersCount, total); - } - - [TestMethod] - public void AsyncQueueTest() { - var queue = new AsyncQueue(); - int res; - - queue.Enqueue(10); - Assert.IsTrue(queue.TryDequeue(out res)); - Assert.AreEqual(10, res); - Assert.IsFalse(queue.TryDequeue(out res)); - - for (int i = 0; i < 1000; i++) - queue.Enqueue(i); - - for (int i = 0; i < 1000; i++) { - queue.TryDequeue(out res); - Assert.AreEqual(i, res); - } - - const int count = 10000000; - - int res1 = 0, res2 = 0; - var t1 = Environment.TickCount; - - AsyncPool.RunThread( - () => { - for (var i = 0; i < count; i++) - queue.Enqueue(1); - Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1); - }, - () => { - for (var i = 0; i < count; i++) - queue.Enqueue(2); - Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1); - }, - () => { - int temp; - int i = 0; - while (i < count) - if (queue.TryDequeue(out temp)) { - i++; - res1 += temp; - } - Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1); - }, - () => { - int temp; - int i = 0; - while (i < count) - if (queue.TryDequeue(out temp)) { - i++; - res2 += temp; - } - Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1); - } - ) - .PromiseAll() - .Join(); - - Assert.AreEqual(count * 3, res1 + res2); - - Console.WriteLine( - "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}", - Environment.TickCount - t1, - res1, - res2, - res1 + res2, - count - ); - } - - [TestMethod] - public void AsyncQueueBatchTest() { - var queue = new AsyncQueue(); - - const int wBatch = 29; - const int wCount = 400000; - const int total = wBatch * wCount * 2; - const int summ = wBatch * wCount * 3; - - int r1 = 0, r2 = 0; - const int rBatch = 111; - int read = 0; - - var t1 = Environment.TickCount; - - AsyncPool.RunThread( - () => { - var buffer = new int[wBatch]; - for(int i = 0; i { - var buffer = new int[wBatch]; - for(int i = 0; i { - var buffer = new int[rBatch]; - - while(read < total) { - int actual; - if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) { - for(int i=0; i< actual; i++) - r1 += buffer[i]; - Interlocked.Add(ref read, actual); - } - } - - Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1); - }, - () => { - var buffer = new int[rBatch]; - - while(read < total) { - int actual; - if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) { - for(int i=0; i< actual; i++) - r2 += buffer[i]; - Interlocked.Add(ref read, actual); - } - } - - Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1); - } - ) - .PromiseAll() - .Join(); - - Assert.AreEqual(summ , r1 + r2); - - Console.WriteLine( - "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}", - Environment.TickCount - t1, - r1, - r2, - r1 + r2, - total - ); - } - - [TestMethod] - public void AsyncQueueChunkDequeueTest() { - var queue = new AsyncQueue(); - - const int wBatch = 31; - const int wCount = 200000; - const int total = wBatch * wCount * 3; - const int summ = wBatch * wCount * 6; - - int r1 = 0, r2 = 0; - const int rBatch = 1024; - int read = 0; - - var t1 = Environment.TickCount; - - AsyncPool.RunThread( - () => { - var buffer = new int[wBatch]; - for(int i = 0; i { - var buffer = new int[wBatch]; - for(int i = 0; i { - var buffer = new int[wBatch]; - for(int i = 0; i { - var buffer = new int[rBatch]; - int count = 1; - double avgchunk = 0; - while(read < total) { - int actual; - if (queue.TryDequeueChunk(buffer,0,rBatch,out actual)) { - for(int i=0; i< actual; i++) - r2 += buffer[i]; - Interlocked.Add(ref read, actual); - avgchunk = avgchunk*(count-1)/count + actual/(double)count; - count ++; - } - } - - Console.WriteLine("done reader #2: {0} ms, avg chunk size: {1}", Environment.TickCount - t1, avgchunk); - } - ) - .PromiseAll() - .Join(); - - Assert.AreEqual(summ , r1 + r2); - - Console.WriteLine( - "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}", - Environment.TickCount - t1, - r1, - r2, - r1 + r2, - total - ); - } - - [TestMethod] - public void AsyncQueueDrainTest() { - var queue = new AsyncQueue(); - - const int wBatch = 32; - const int wCount = 200000; - const int total = wBatch * wCount * 3; - const int summ = wBatch * wCount * 3; - - int r1 = 0, r2 = 0; - int read = 0; - - var t1 = Environment.TickCount; - - AsyncPool.RunThread( - () => { - var buffer = new int[wBatch]; - for(int i = 0; i { - var buffer = new int[wBatch]; - for (int i = 0; i < wBatch; i++) - buffer[i] = 1; - - for (int i = 0; i < wCount; i++) - queue.EnqueueRange(buffer, 0, wBatch); - Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1); - }, - () => { - var buffer = new int[wBatch]; - for(int i = 0; i { - int temp; - int count = 0; - while (read < total) - if (queue.TryDequeue(out temp)) { - count++; - r1 += temp; - Interlocked.Increment(ref read); - } - Console.WriteLine("done reader #1: {0} ms, {1} count", Environment.TickCount - t1, count); - },*/ - /*() => { - var buffer = new int[rBatch]; - var count = 0; - while(read < total) { - int actual; - if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) { - for(int i=0; i< actual; i++) - r1 += buffer[i]; - Interlocked.Add(ref read, actual); - count += actual; - } - } - - Console.WriteLine("done reader #1: {0} ms, {1} items", Environment.TickCount - t1, count); - },*/ - () => { - var count = 0; - int emptyDrains = 0; - - while (read < total) { - var buffer = queue.Drain(); - if (buffer.Count == 0) - emptyDrains++; - for(int i=0; i< buffer.Count; i++) - r1 += buffer[i]; - Interlocked.Add(ref read, buffer.Count); - count += buffer.Count; - } - Console.WriteLine("done reader #1: {0} ms, {1} items, empty: {2}", Environment.TickCount - t1, count, emptyDrains); - }, - () => { - var count = 0; - int emptyDrains = 0; - - while (read < total) { - var buffer = queue.Drain(); - if (buffer.Count == 0) - emptyDrains++; - - for (int i=0; i< buffer.Count; i++) - r2 += buffer[i]; - Interlocked.Add(ref read, buffer.Count); - count += buffer.Count; - } - Console.WriteLine("done reader #2: {0} ms, {1} items, empty: {2}", Environment.TickCount - t1, count, emptyDrains); - } - ) - .PromiseAll() - .Join(); - - Assert.AreEqual(summ , r1 + r2); - - Console.WriteLine( - "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}", - Environment.TickCount - t1, - r1, - r2, - r1 + r2, - total - ); - } - - [TestMethod] - public void ParallelMapTest() { - - const int count = 100000; - - var args = new double[count]; - var rand = new Random(); - - for (int i = 0; i < count; i++) - args[i] = rand.NextDouble(); - - var t = Environment.TickCount; - var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join(); - - Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t); - - t = Environment.TickCount; - for (int i = 0; i < count; i++) - Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]); - Console.WriteLine("Verified in {0} ms", Environment.TickCount - t); - } - - [TestMethod] - public void ChainedMapTest() { - - using (var pool = new WorkerPool()) { - const int count = 10000; - - var args = new double[count]; - var rand = new Random(); - - for (int i = 0; i < count; i++) - args[i] = rand.NextDouble(); - - var t = Environment.TickCount; - var res = args - .ChainedMap( - // Analysis disable once AccessToDisposedClosure - x => pool.Invoke( - () => Math.Sin(x * x) - ), - 4 - ) - .Join(); - - Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t); - - t = Environment.TickCount; - for (int i = 0; i < count; i++) - Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]); - Console.WriteLine("Verified in {0} ms", Environment.TickCount - t); - Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads); - } - } - - [TestMethod] - public void ParallelForEachTest() { - - const int count = 100000; - - var args = new int[count]; - var rand = new Random(); - - for (int i = 0; i < count; i++) - args[i] = (int)(rand.NextDouble() * 100); - - int result = 0; - - var t = Environment.TickCount; - args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join(); - - Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result); - - int result2 = 0; - - t = Environment.TickCount; - for (int i = 0; i < count; i++) - result2 += args[i]; - Assert.AreEqual(result2, result); - Console.WriteLine("Verified in {0} ms", Environment.TickCount - t); - } - - [TestMethod] - public void ComplexCase1Test() { - var flags = new bool[3]; - - // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map) - - var step1 = PromiseHelper - .Sleep(200, "Alan") - .On(() => flags[0] = true, PromiseEventType.Cancelled); - var p = step1 - .Chain(x => - PromiseHelper - .Sleep(200, "Hi, " + x) - .Then(y => y) - .On(() => flags[1] = true, PromiseEventType.Cancelled) - ) - .On(() => flags[2] = true, PromiseEventType.Cancelled); - step1.Join(); - p.Cancel(); - try { - Assert.AreEqual(p.Join(), "Hi, Alan"); - Assert.Fail("Shouldn't get here"); - } catch (OperationCanceledException) { - } - - Assert.IsFalse(flags[0]); - Assert.IsTrue(flags[1]); - Assert.IsTrue(flags[2]); - } - - [TestMethod] - public void ChainedCancel1Test() { - // при отмене сцепленной асинхронной операции все обещание должно - // завершаться ошибкой OperationCanceledException - var p = PromiseHelper - .Sleep(1, "Hi, HAL!") - .Then(x => { - // запускаем две асинхронные операции - var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!"); - // вторая операция отменяет первую до завершения - PromiseHelper - .Sleep(100, "HAL, STOP!") - .Then(result.Cancel); - return result; - }); - try { - p.Join(); - } catch (TargetInvocationException err) { - Assert.IsTrue(err.InnerException is OperationCanceledException); - } - } - - [TestMethod] - public void ChainedCancel2Test() { - // при отмене цепочки обещаний, вложенные операции также должны отменяться - var pSurvive = new Promise(); - var hemStarted = new Signal(); - var p = PromiseHelper - .Sleep(1, "Hi, HAL!") - .Chain(() => { - hemStarted.Set(); - // запускаем две асинхронные операции - var result = PromiseHelper - .Sleep(2000, "HEM ENABLED!!!") - .Then(() => pSurvive.Resolve(false)); - - result - .On(() => pSurvive.Resolve(true), PromiseEventType.Cancelled); - - return result; - }); - - hemStarted.Wait(); - p.Cancel(); - - try { - p.Join(); - Assert.Fail(); - } catch (OperationCanceledException) { - } - Assert.IsTrue(pSurvive.Join()); - } - - [TestMethod] - public void SharedLockTest() { - var l = new SharedLock(); - int shared = 0; - int exclusive = 0; - var s1 = new Signal(); - var log = new AsyncQueue(); - - try { - AsyncPool.RunThread( - () => { - log.Enqueue("Reader #1 started"); - try { - l.LockShared(); - log.Enqueue("Reader #1 lock got"); - if (Interlocked.Increment(ref shared) == 2) - s1.Set(); - s1.Wait(); - log.Enqueue("Reader #1 finished"); - Interlocked.Decrement(ref shared); - } finally { - l.Release(); - log.Enqueue("Reader #1 lock released"); - } - }, - () => { - log.Enqueue("Reader #2 started"); - - try { - l.LockShared(); - log.Enqueue("Reader #2 lock got"); - - if (Interlocked.Increment(ref shared) == 2) - s1.Set(); - s1.Wait(); - log.Enqueue("Reader #2 upgrading to writer"); - Interlocked.Decrement(ref shared); - l.Upgrade(); - log.Enqueue("Reader #2 upgraded"); - - Assert.AreEqual(1, Interlocked.Increment(ref exclusive)); - Assert.AreEqual(0, shared); - log.Enqueue("Reader #2 finished"); - Interlocked.Decrement(ref exclusive); - } finally { - l.Release(); - log.Enqueue("Reader #2 lock released"); - } - }, - () => { - log.Enqueue("Writer #1 started"); - try { - l.LockExclusive(); - log.Enqueue("Writer #1 got the lock"); - Assert.AreEqual(1, Interlocked.Increment(ref exclusive)); - Interlocked.Decrement(ref exclusive); - log.Enqueue("Writer #1 is finished"); - } finally { - l.Release(); - log.Enqueue("Writer #1 lock released"); - } - } - ).PromiseAll().Join(1000); - log.Enqueue("Done"); - } catch(Exception error) { - log.Enqueue(error.Message); - throw; - } finally { - foreach (var m in log) - Console.WriteLine(m); - } - } - - #if NET_4_5 - - [TestMethod] - public async void TaskInteropTest() { - var promise = new Promise(); - promise.Resolve(10); - var res = await promise; - - Assert.AreEqual(10, res); - } - - #endif - } -} - diff --git a/Implab.Test/CancelationTests.cs b/Implab.Test/CancelationTests.cs deleted file mode 100644 --- a/Implab.Test/CancelationTests.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using Implab.Parallels; - -#if MONO - -using NUnit.Framework; -using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; -using TestMethodAttribute = NUnit.Framework.TestAttribute; - -#else - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -#endif - -namespace Implab.Test { - [TestClass] - public class CancelationTests { - - [TestMethod] - public void PromiseCancelTest() { - var p = new Promise(); - bool requested = false; - var reason = new Exception("Test"); - - // request cancelation - p.Cancel(reason); - - Assert.IsTrue(p.IsCancellationRequested); - Assert.AreSame(reason, p.CancellationReason); - Assert.IsFalse(p.IsCancelled); - - p.CancellationRequested(r => { - Assert.AreSame(reason, r); - requested = true; - }); - - Assert.IsTrue(requested); - - // cancel the promise - Assert.IsTrue(p.CancelOperationIfRequested()); - Assert.IsTrue(p.IsCancelled); - Assert.AreSame(reason, p.RejectReason); - } - - [TestMethod] - public void CancelActionBeforeStartTask() { - bool run = false; - var task = new ActionTask(() => { - run = true; - }, null, null, true); - - // request cancelation - task.Cancel(); - Assert.IsTrue(task.IsCancelled); - task.Resolve(); - Assert.IsFalse(run); - } - - [TestMethod] - public void CancelActionAfterTaskStarted() { - var finish = new Signal(); - var started = new Signal(); - - var task = new ActionTask(() => { - started.Set(); - finish.Wait(); - }, null, null, true); - - AsyncPool.RunThread(() => { - task.Resolve(); - }); - - started.Wait(1000); - - task.Cancel(); - Assert.IsTrue(task.IsCancellationRequested); - Assert.IsFalse(task.IsCancelled); - Assert.IsFalse(task.IsFulfilled); - - finish.Set(); - task.Join(1000); - - } - - [TestMethod] - public void CancelTaskChainFromBottom() { - var started = new Signal(); - var check1 = new Signal(); - var requested = false; - var p1 = AsyncPool.RunThread(token => { - token.CancellationRequested(reason => requested = true); - started.Set(); - check1.Wait(); - token.CancelOperationIfRequested(); - }); - - started.Wait(); - - var p2 = p1.Then(() => { - }); - - Assert.IsFalse(p1.IsResolved); - Assert.IsFalse(p2.IsResolved); - - p2.Cancel(); - - Assert.IsFalse(p2.IsCancelled); - Assert.IsFalse(p1.IsCancelled); - Assert.IsTrue(requested); - - check1.Set(); - - try { - p2.Join(1000); - Assert.Fail("The chain isn't cancelled"); - } catch(OperationCanceledException){ - } - - Assert.IsTrue(p1.IsCancelled); - Assert.IsTrue(p2.IsCancelled); - } - - - - [TestMethod] - public void CancellableAsyncTask() { - var finish = new Signal(); - var started = new Signal(); - - var p = AsyncPool.RunThread(token => { - token.CancellationRequested(r => finish.Set()); - started.Set(); - finish.Wait(); - Assert.IsTrue(token.CancelOperationIfRequested()); - }); - - started.Wait(1000); - Assert.IsFalse(p.IsResolved); - p.Cancel(); - try { - p.Join(1000); - } catch (OperationCanceledException) { - } - } - } -} - diff --git a/Implab.Test/Implab.Test.csproj b/Implab.Test/Implab.Test.csproj --- a/Implab.Test/Implab.Test.csproj +++ b/Implab.Test/Implab.Test.csproj @@ -1,88 +1,16 @@ - - + + - Debug - AnyCPU - 8.0.30703 - 2.0 - {63F92C0C-61BF-48C0-A377-8D67C3C661D0} - Library - Properties - Implab.Test - Implab.Test - v4.5 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false + netcoreapp2.0 + + false - - - - 3.5 - - - - - - - - - - - + + + + + - - - {99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9} - Implab - - - - - - \ No newline at end of file + + diff --git a/Implab.Test/Implab.Test.mono.csproj b/Implab.Test/Implab.Test.mono.csproj deleted file mode 100644 --- a/Implab.Test/Implab.Test.mono.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {2BD05F84-E067-4B87-9477-FDC2676A21C6} - Library - Implab.Test - Implab.Test - v4.5 - 0.2 - - - true - full - false - bin\Debug - DEBUG;MONO - prompt - 4 - false - - - true - bin\Release - prompt - 4 - false - MONO - - - true - full - false - bin\Debug - DEBUG;TRACE;NET_4_5;MONO - prompt - 4 - false - - - true - bin\Release - NET_4_5;MONO - prompt - 4 - false - - - - ..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - - - - - - - - - - - - - {F550F1F8-8746-4AD0-9614-855F4C4B7F05} - Implab - - - - - - - - - \ No newline at end of file diff --git a/Implab.Test/Mock/MockPollingComponent.cs b/Implab.Test/Mock/MockPollingComponent.cs deleted file mode 100644 --- a/Implab.Test/Mock/MockPollingComponent.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using Implab.Components; - -namespace Implab.Test.Mock { - class MockPollingComponent : PollingComponent { - public MockPollingComponent(TimeSpan interval, Func, IPromise> dispatcher, bool initialized) : base(interval, dispatcher, initialized) { - } - - public Action MockInit { - get; - set; - } - - public Action MockOnError { - get; - set; - } - - public Action MockOnCancel { - get; - set; - } - - public Func MockStart { - get; - set; - } - - public Func MockStop { - get; - set; - } - - public Func MockTick { - get; - set; - } - - protected override IPromise OnStart() { - return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart); - } - - protected override IPromise OnStop() { - return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop); - } - - protected override void OnInitialize() { - if (MockInit != null) - MockInit(); - } - - protected override IPromise OnTick(ICancellationToken cancellationToken) { - return MockTick != null ? Safe.Run(() => MockTick(cancellationToken)) : Promise.Success; - } - - protected override void OnTickCancel(Exception error) { - if (MockOnCancel != null) - MockOnCancel(error); - } - - protected override void OnTickError(Exception error) { - if (MockOnError != null) - MockOnError(error); - } - - public void CallComponentFail(Exception error) { - Fail(error); - } - } -} - diff --git a/Implab.Test/Mock/MockRunnableComponent.cs b/Implab.Test/Mock/MockRunnableComponent.cs deleted file mode 100644 --- a/Implab.Test/Mock/MockRunnableComponent.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Implab.Components; - -namespace Implab.Test.Mock { - class MockRunnableComponent : RunnableComponent { - public MockRunnableComponent(bool initialized) : base(initialized) { - } - - public MockRunnableComponent(bool initialized, bool reusable) : base(initialized, reusable) { - } - - public Action MockInit { - get; - set; - } - - public Func MockStart { - get; - set; - } - - public Func MockStop { - get; - set; - } - - public Action MockDispose { - get; - set; - } - - protected override IPromise OnStart() { - return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart); - } - - protected override IPromise OnStop() { - return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop); - } - - protected override void OnInitialize() { - if (MockInit != null) - MockInit(); - } - - protected override void Dispose(bool disposing) { - if (MockDispose != null) - MockDispose(disposing); - base.Dispose(disposing); - } - } -} - diff --git a/Implab.Test/PollingComponentTests.cs b/Implab.Test/PollingComponentTests.cs deleted file mode 100644 --- a/Implab.Test/PollingComponentTests.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Reflection; -using System.Threading; -using Implab.Parallels; -using Implab.Components; -using Implab.Test.Mock; - -#if MONO - -using NUnit.Framework; -using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; -using TestMethodAttribute = NUnit.Framework.TestAttribute; -using AssertFailedException = NUnit.Framework.AssertionException; -#else - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -#endif - -namespace Implab.Test { - [TestClass] - public class PollingComponentTests { - static void ShouldThrow(Action action) { - try { - action(); - Assert.Fail(); - } catch (AssertFailedException) { - throw; - } catch { - } - } - - [TestMethod] - public void NormalFlowTest() { - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, false); - - Assert.AreEqual(ExecutionState.Created, comp.State); - - comp.Initialize(); - - Assert.AreEqual(ExecutionState.Ready, comp.State); - - comp.Start().Join(1000); - - Assert.AreEqual(ExecutionState.Running, comp.State); - - comp.Stop().Join(1000); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - - } - - [TestMethod] - public void ShouldStartTicks() { - var signal = new Signal(); - - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true); - comp.MockTick = ct => { - signal.Set(); - return Promise.Success; - }; - - comp.Start().Join(1000); - signal.Wait(1000); - comp.Stop().Join(1000); - } - - [TestMethod] - public void StopShouldWaitForTickToComplete() { - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true); - var signal = new Signal(); - var promise = new Promise(); - - // timer should tick once - comp.MockTick = ct => { - signal.Set(); - return promise; - }; - - // start timer - comp.Start().Join(1000); - - signal.Wait(); // wait for tick - - // try to stop component - var stopping = comp.Stop(); - - Assert.AreEqual(ExecutionState.Stopping, comp.State); - ShouldThrow(() => stopping.Join(100)); - Assert.AreEqual(ExecutionState.Stopping, comp.State); - - // complete operation - promise.Resolve(); - - // the component should stop normally - stopping.Join(1000); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void ShouldRecoverAfterTickError() { - var ticks = 0; - - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true); - var signal = new Signal(); // will signal when timer fires 10 times - - comp.MockTick = ct => { - ticks++; - if (ticks == 10) - signal.Set(); - // each time handler dies - throw new Exception("tainted handler"); - }; - - comp.Start(); - - signal.Wait(1000); - - comp.Stop().Join(1000); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void StopCancelHandlerOnStop() { - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true); - var started = new Signal(); - bool cancelled = false; - - // timer should tick once - comp.MockTick = ct => { - started.Set(); - - while(!ct.IsCancellationRequested) { - Thread.Sleep(1); - } - - cancelled = true; - - throw new OperationCanceledException(); - }; - - // start timer - comp.Start().Join(1000); - - started.Wait(); // wait for tick - - // try to stop component - comp.Stop().Join(1000); - - Assert.AreEqual(true, cancelled); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void FailTickOnStopShouldBeIgnored() { - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true); - var started = new Signal(); - var finish = new Signal(); - - // timer should tick once - comp.MockTick = ct => { - started.Set(); - finish.Wait(); - // component is in stopping state here - throw new Exception("Die, die, die!!!"); - }; - - - comp.MockOnError = comp.CallComponentFail; - - // start timer - comp.Start().Join(1000); - - started.Wait(); // wait for tick - - // try to stop component - var stopping = comp.Stop(); - - // the component is in stopping state but it is waiting for the tick handler to complete - finish.Set(); // signal the tick handler to finish - - // tick handler should stop rather soon - stopping.Join(1000); - - // validate the component is disposed - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void FailTickShouldFailComponent() { - var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true); - var started = new Signal(); - var finish = new Signal(); - - // timer should tick once - comp.MockTick = ct => { - started.Set(); - throw new Exception("Die, die, die!!!"); - }; - - - comp.MockOnError = err => { - comp.CallComponentFail(err); - finish.Set(); - }; - - // start timer - comp.Start().Join(1000); - - started.Wait(); // wait for tick - - finish.Wait(); - - // try to stop component - ShouldThrow(() => comp.Stop()); - - Assert.AreEqual(ExecutionState.Failed, comp.State); - Assert.IsNotNull(comp.LastError); - Assert.AreEqual("Die, die, die!!!", comp.LastError.Message); - - comp.Dispose(); - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - } -} - diff --git a/Implab.Test/PromiseHelper.cs b/Implab.Test/PromiseHelper.cs --- a/Implab.Test/PromiseHelper.cs +++ b/Implab.Test/PromiseHelper.cs @@ -1,21 +1,39 @@ -using Implab.Parallels; +using Implab; +using System; using System.Threading; namespace Implab.Test { static class PromiseHelper { - public static IPromise Sleep(int timeout, T retVal) { - return AsyncPool.Invoke((ct) => { - ct.CancellationRequested(ct.CancelOperation); - Thread.Sleep(timeout); - return retVal; + public static IPromise Sleep(int timeout, T retVal, CancellationToken ct = default(CancellationToken)) { + + Timer timer = null; + + return Promise.Create((d) => { + timer = new Timer(x => { + d.Resolve(retVal); + }, null, timeout, Timeout.Infinite); + + if(ct.CanBeCanceled) + ct.Register(d.Cancel); + + }).Finally(() => { + Safe.Dispose(timer); }); } - public static IPromise Sleep(int timeout) { - return AsyncPool.Invoke((ct) => { - ct.CancellationRequested(ct.CancelOperation); - Thread.Sleep(timeout); - return 0; + public static IPromise Sleep(int timeout, CancellationToken ct = default(CancellationToken)) { + Timer timer = null; + + return Promise.Create((d) => { + timer = new Timer(x => { + d.Resolve(); + }, null, timeout, Timeout.Infinite); + + if(ct.CanBeCanceled) + ct.Register(d.Cancel); + + }).Finally(() => { + Safe.Dispose(timer); }); } } diff --git a/Implab.Test/Properties/AssemblyInfo.cs b/Implab.Test/Properties/AssemblyInfo.cs deleted file mode 100644 --- a/Implab.Test/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Implab.Test")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Implab.Test")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("bfcae720-21eb-4411-b70a-6eeab99071de")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("0.0.*")] diff --git a/Implab.Test/RunnableComponentTests.cs b/Implab.Test/RunnableComponentTests.cs deleted file mode 100644 --- a/Implab.Test/RunnableComponentTests.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Reflection; -using System.Threading; -using Implab.Parallels; -using Implab.Components; -using Implab.Test.Mock; - -#if MONO - -using NUnit.Framework; -using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; -using TestMethodAttribute = NUnit.Framework.TestAttribute; -using AssertFailedException = NUnit.Framework.AssertionException; -#else - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -#endif - -namespace Implab.Test { - [TestClass] - public class RunnableComponentTests { - - static void ShouldThrow(Action action) { - try { - action(); - Assert.Fail(); - } catch (AssertFailedException) { - throw; - } catch { - } - } - - - - [TestMethod] - public void NormalFlowTest() { - var comp = new MockRunnableComponent(false); - - Assert.AreEqual(ExecutionState.Created, comp.State); - - comp.Initialize(); - - Assert.AreEqual(ExecutionState.Ready, comp.State); - - comp.Start().Join(1000); - - Assert.AreEqual(ExecutionState.Running, comp.State); - - comp.Stop().Join(1000); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - - } - - [TestMethod] - public void InitFailTest() { - var comp = new MockRunnableComponent(false) { - MockInit = () => { - throw new Exception("BAD"); - } - }; - - ShouldThrow(() => comp.Start()); - ShouldThrow(() => comp.Stop()); - Assert.AreEqual(ExecutionState.Created, comp.State); - - ShouldThrow(comp.Initialize); - - Assert.AreEqual(ExecutionState.Failed, comp.State); - - ShouldThrow(() => comp.Start()); - ShouldThrow(() => comp.Stop()); - Assert.AreEqual(ExecutionState.Failed, comp.State); - - comp.Dispose(); - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void DisposedTest() { - - var comp = new MockRunnableComponent(false); - comp.Dispose(); - - ShouldThrow(() => comp.Start()); - ShouldThrow(() => comp.Stop()); - ShouldThrow(comp.Initialize); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void ShouldCallDisposeOnStop() { - var comp = new MockRunnableComponent(true); - - bool disposed = false; - comp.MockDispose = (disposing) => { - disposed = true; - }; - - comp.Start().Join(1000); - comp.Stop().Join(1000); - - ShouldThrow(() => comp.Start()); - ShouldThrow(() => comp.Stop()); - ShouldThrow(comp.Initialize); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - Assert.IsTrue(disposed); - } - - [TestMethod] - public void ShouldNotCallDisposeOnStop() { - var comp = new MockRunnableComponent(true, true); - - bool disposed = false; - comp.MockDispose = (disposing) => { - disposed = true; - }; - - comp.Start().Join(1000); - comp.Stop().Join(1000); - - Assert.AreEqual(ExecutionState.Ready, comp.State); - Assert.IsFalse(disposed); - } - - [TestMethod] - public void SelfDisposeOnStop() { - var comp = new MockRunnableComponent(true, true); - - bool disposed = false; - comp.MockDispose = (disposing) => { - disposed = true; - }; - - comp.Start().Join(1000); - comp.Stop().Join(1000); - - Assert.AreEqual(ExecutionState.Ready, comp.State); - Assert.IsFalse(disposed); - - comp.MockStop = () => { - comp.Dispose(); - return Promise.Success; - }; - - comp.Start().Join(1000); - comp.Stop().Join(1000); - - Assert.AreEqual(ExecutionState.Disposed, comp.State); - Assert.IsTrue(disposed); - } - - [TestMethod] - public void StartCancelTest() { - var comp = new MockRunnableComponent(true) { - MockStart = () => PromiseHelper.Sleep(100000, 0) - }; - - var p = comp.Start(); - Assert.AreEqual(ExecutionState.Starting, comp.State); - p.Cancel(); - ShouldThrow(() => p.Join(1000)); - Assert.AreEqual(ExecutionState.Failed, comp.State); - - Assert.IsTrue(comp.LastError is OperationCanceledException); - - comp.Dispose(); - } - - [TestMethod] - public void StartStopTest() { - var stop = new Signal(); - var comp = new MockRunnableComponent(true) { - MockStart = () => PromiseHelper.Sleep(100000, 0), - MockStop = () => AsyncPool.RunThread(stop.Wait) - }; - - var p1 = comp.Start(); - var p2 = comp.Stop(); - // should enter stopping state - - ShouldThrow(p1.Join); - Assert.IsTrue(p1.IsCancelled); - Assert.AreEqual(ExecutionState.Stopping, comp.State); - - stop.Set(); - p2.Join(1000); - Assert.AreEqual(ExecutionState.Disposed, comp.State); - } - - [TestMethod] - public void StartStopFailTest() { - var comp = new MockRunnableComponent(true) { - MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); }) - }; - - comp.Start(); - var p = comp.Stop(); - // if Start fails to cancel, should fail to stop - ShouldThrow(() => p.Join(1000)); - Assert.AreEqual(ExecutionState.Failed, comp.State); - Assert.IsNotNull(comp.LastError); - Assert.AreEqual("I'm dead", comp.LastError.Message); - } - - [TestMethod] - public void StopCancelTest() { - var comp = new MockRunnableComponent(true) { - MockStop = () => PromiseHelper.Sleep(100000, 0) - }; - - comp.Start(); - var p = comp.Stop(); - Assert.AreEqual(ExecutionState.Stopping, comp.State); - p.Cancel(); - ShouldThrow(() => p.Join(1000)); - Assert.AreEqual(ExecutionState.Failed, comp.State); - Assert.IsTrue(comp.LastError is OperationCanceledException); - - comp.Dispose(); - } - - } -} - diff --git a/Implab.Test/UnitTest1.cs b/Implab.Test/UnitTest1.cs new file mode 100644 --- /dev/null +++ b/Implab.Test/UnitTest1.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading; +using Xunit; + +namespace Implab.Test +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + using(var cts = new CancellationTokenSource(1000)) { + PromiseHelper.Sleep(10000, cts.Token).Join(); + } + } + } +} diff --git a/Implab.Test/packages.config b/Implab.Test/packages.config deleted file mode 100644 --- a/Implab.Test/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Implab/CancellationToken.cs b/Implab/CancellationToken.cs deleted file mode 100644 --- a/Implab/CancellationToken.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Threading; -using Implab.Parallels; - -namespace Implab { - /// - /// The cancellation token signals to the worker that cancellation has been - /// requested, after the signal is received the worker decides wheather to - /// cancel its work or to continue. - /// - public class CancellationToken : AbstractEvent> { - public CancellationToken() { - - } - - public void RequestCancellation() { - - } - - public void RequestCancellation(Exception reason) { - - } - - protected override void SignalHandler(Action handler) { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/Implab/Deferred.cs b/Implab/Deferred.cs --- a/Implab/Deferred.cs +++ b/Implab/Deferred.cs @@ -8,51 +8,46 @@ namespace Implab { public class Deferred : IResolvable { readonly Promise m_promise; - readonly IDispatcher m_dispatcher; - - internal Deferred(IDispatcher dispatcher) : this(new Promise(), dispatcher) { + internal Deferred() { + m_promise = new Promise(); } internal Deferred(Promise promise, IDispatcher dispatcher) { Debug.Assert(promise != null); m_promise = promise; - m_dispatcher = dispatcher; } public IPromise Promise { get { return m_promise; } } - public void Reject(Exception error) { + public void Cancel() { + Reject(new OperationCanceledException()); + } + + public virtual void Reject(Exception error) { if (error is PromiseTransientException) error = ((PromiseTransientException)error).InnerException; m_promise.RejectPromise(error); } - public void Resolve() { + public virtual void Resolve() { m_promise.ResolvePromise(); } - public void Resolve(IPromise thenable) { + public virtual void Resolve(IPromise thenable) { if (thenable == null) Reject(new Exception("The promise or task are expected")); if (thenable == m_promise) Reject(new Exception("The promise cannot be resolved with oneself")); - else if (m_dispatcher != null) - // dispatch (see ecma-262/6.0: 25.4.1.3.2 Promise Resolve Functions) - m_dispatcher.Enqueue(Chain, thenable); - else - Chain(thenable); - } - - void Chain(IPromise thenable) { try { thenable.Then(this); } catch (Exception err) { Reject(err); } } + } } \ No newline at end of file diff --git a/Implab/Deferred`1.cs b/Implab/Deferred`1.cs --- a/Implab/Deferred`1.cs +++ b/Implab/Deferred`1.cs @@ -4,46 +4,41 @@ using System.Diagnostics; namespace Implab { public class Deferred : IResolvable { readonly Promise m_promise; - readonly IDispatcher m_dispatcher; - internal Deferred(IDispatcher dispatcher) : this(new Promise(), dispatcher) { + internal Deferred() { + m_promise = new Promise(); } - internal Deferred(Promise promise, IDispatcher dispatcher) { + protected Deferred(Promise promise) { Debug.Assert(promise != null); m_promise = promise; - m_dispatcher = dispatcher; } public IPromise Promise { get { return m_promise; } } - public void Reject(Exception error) { + public void Cancel() { + Reject(new OperationCanceledException()); + } + + public virtual void Reject(Exception error) { if (error is PromiseTransientException) error = ((PromiseTransientException)error).InnerException; m_promise.RejectPromise(error); } - public void Resolve(T value) { + public virtual void Resolve(T value) { m_promise.ResolvePromise(value); } - public void Resolve(IPromise thenable) { + public virtual void Resolve(IPromise thenable) { if (thenable == null) Reject(new Exception("The promise or task are expected")); if (thenable == m_promise) Reject(new Exception("The promise cannot be resolved with oneself")); - else if (m_dispatcher != null) - // dispatch (see ecma-262/6.0: 25.4.1.3.2 Promise Resolve Functions) - m_dispatcher.Enqueue(Chain, thenable); - else - Chain(thenable); - } - - void Chain(IPromise thenable) { try { thenable.Then(this); } catch (Exception err) { diff --git a/Implab/ExceptionHelpers.cs b/Implab/ExceptionHelpers.cs --- a/Implab/ExceptionHelpers.cs +++ b/Implab/ExceptionHelpers.cs @@ -3,16 +3,19 @@ using System.Reflection; using System.Runtime.ExceptionServices; namespace Implab { - public static class ExceptionHelpers { - public static void Rethrow(this Exception that) { + static class ExceptionHelpers { + public static Exception Rethrow(this Exception that) { ExceptionDispatchInfo.Capture(that).Throw(); + return new TargetInvocationException(that); } - public static void ThrowInvocationException(this Exception that) { - if (that is OperationCanceledException) - throw new OperationCanceledException("Operation cancelled", that); + public static Exception Wrap(this Exception that) { + if (that == null) + return new Exception(); + else if (that is OperationCanceledException) + return new OperationCanceledException("The operation has been cancelled", that); else - throw new TargetInvocationException(that); + return new TargetInvocationException(that); } } } \ No newline at end of file diff --git a/Implab/ICancellable.cs b/Implab/ICancellable.cs deleted file mode 100644 --- a/Implab/ICancellable.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Implab { - public interface ICancellable { - void Cancel(); - void Cancel(Exception reason); - } -} diff --git a/Implab/ICancellationToken.cs b/Implab/ICancellationToken.cs deleted file mode 100644 --- a/Implab/ICancellationToken.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace Implab { - public interface ICancellationToken { - /// - /// Indicates wherther the cancellation was requested. - /// - bool IsCancellationRequested { get ; } - - /// - /// The reason why the operation should be cancelled. - /// - Exception CancellationReason { get ; } - - /// - /// Accepts if requested. - /// - /// true, if if requested was accepted, false otherwise. - bool CancelOperationIfRequested(); - - /// - /// Sets the token to cancelled state. - /// - /// The reason why the operation was cancelled. - void CancelOperation(Exception reason); - - /// - /// Adds the listener for the cancellation request, is the cancellation was requested the - /// is executed immediatelly. - /// - /// The handler which will be executed if the cancel occurs. - void CancellationRequested(Action handler); - - } -} - diff --git a/Implab/IDeferredT.cs b/Implab/IDeferredT.cs deleted file mode 100644 --- a/Implab/IDeferredT.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Implab { - public interface IDeferred { - void Resolve(T value); - - void Reject(Exception error); - - void SetCancelled(Exception error); - } -} - diff --git a/Implab/Promise.cs b/Implab/Promise.cs --- a/Implab/Promise.cs +++ b/Implab/Promise.cs @@ -169,8 +169,7 @@ namespace Implab { public static IPromise Create(PromiseExecutor executor) { Safe.ArgumentNotNull(executor, nameof(executor)); - var p = new Promise(); - var d = new Deferred(p, DefaultDispatcher); + var d = new Deferred(); try { executor(d); @@ -182,7 +181,7 @@ namespace Implab { } public static IPromise All(IEnumerable promises) { - var d = new Deferred(DefaultDispatcher); + var d = new Deferred(); var all = new PromiseAll(d); foreach (var promise in promises) { all.AddPromise(promise); @@ -193,8 +192,8 @@ namespace Implab { return all.ResultPromise; } - public static IPromise All(IEnumerable> promises, Func cleanup, Action cancel) { - var d = new Deferred(DefaultDispatcher); + public static IPromise All(IEnumerable> promises, Func cleanup = null, Action cancel = null) { + var d = new Deferred(); var all = new PromiseAll(d, cleanup, cancel); foreach (var promise in promises) { all.AddPromise(promise); diff --git a/Implab/PromiseActionReaction.cs b/Implab/PromiseActionReaction.cs --- a/Implab/PromiseActionReaction.cs +++ b/Implab/PromiseActionReaction.cs @@ -2,56 +2,91 @@ using System; using System.Diagnostics; namespace Implab { - class PromiseActionReaction : PromiseReaction { + class PromiseActionReaction : IResolvable { readonly Deferred m_next; + readonly IDispatcher m_dispatcher; + + readonly Action m_fulfilled; + + readonly Action m_rejected; + public IPromise Promise { get { return m_next.Promise; } } - public PromiseActionReaction(Action fulfilled, Action rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public PromiseActionReaction(Action fulfilled, Action rejected, Deferred next, IDispatcher dispatcher) { + m_next = next; + m_fulfilled = fulfilled; + m_rejected = rejected; + m_dispatcher = dispatcher; } - public PromiseActionReaction(Func fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + public void Resolve() { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(ResolveImpl); + else + ResolveImpl(); + } else { + m_next.Resolve(); + } + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + void ResolveImpl() { + m_fulfilled(m_next); + } + + public void Reject(Exception error) { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(RejectImpl, error); + else + RejectImpl(error); + } else { + m_next.Reject(error); + } } - public PromiseActionReaction(Action fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + void RejectImpl(Exception error) { + m_rejected(error, m_next); + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseActionReaction Create(Action fulfilled, Action rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - public PromiseActionReaction(Func fulfilled, Action rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseActionReaction Create(Func fulfilled, Action rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultReject(Exception reason) { - m_next.Reject(reason); + public static PromiseActionReaction Create(Action fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultResolve() { - m_next.Resolve(); + public static PromiseActionReaction Create(Func fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } } } \ No newline at end of file diff --git a/Implab/PromiseActionReaction`1.cs b/Implab/PromiseActionReaction`1.cs --- a/Implab/PromiseActionReaction`1.cs +++ b/Implab/PromiseActionReaction`1.cs @@ -2,55 +2,90 @@ using System; using System.Diagnostics; namespace Implab { - class PromiseActionReaction : PromiseReaction { + class PromiseActionReaction : IResolvable { readonly Deferred m_next; + readonly IDispatcher m_dispatcher; + + readonly Action m_fulfilled; + + readonly Action m_rejected; + public IPromise Promise { get { return m_next.Promise; } } - public PromiseActionReaction(Action fulfilled, Action rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public PromiseActionReaction(Action fulfilled, Action rejected, Deferred next, IDispatcher dispatcher) { + m_next = next; + m_fulfilled = fulfilled; + m_rejected = rejected; + m_dispatcher = dispatcher; } - public PromiseActionReaction(Func fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + public void Resolve(T result) { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(ResolveImpl, result); + else + ResolveImpl(result); + } else { + m_next.Resolve(); + } + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + void ResolveImpl (T result) { + m_fulfilled(result, m_next); + } + + public void Reject(Exception error) { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(RejectImpl, error); + else + RejectImpl(error); + } else { + m_next.Reject(error); + } } - public PromiseActionReaction(Action fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + void RejectImpl(Exception error) { + m_rejected(error, m_next); + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseActionReaction Create(Action fulfilled, Action rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - public PromiseActionReaction(Func fulfilled, Action rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseActionReaction Create(Func fulfilled, Action rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultReject(Exception reason) { - m_next.Reject(reason); + public static PromiseActionReaction Create(Action fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultResolve(T result) { - m_next.Resolve(); + public static PromiseActionReaction Create(Func fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseActionReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } } } \ No newline at end of file diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs --- a/Implab/PromiseExtensions.cs +++ b/Implab/PromiseExtensions.cs @@ -8,97 +8,145 @@ namespace Implab { public static class PromiseExtensions { public static IPromise Then(this IPromise that, Action fulfilled, Action rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Action fulfilled) { + var reaction = PromiseActionReaction.Create(fulfilled, null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Action fulfilled, Func rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Action rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Func fulfilled) { + var reaction = PromiseActionReaction.Create(fulfilled, null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Func rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Action fulfilled, Action rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Action fulfilled) { + var reaction = PromiseActionReaction.Create(fulfilled, null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Action fulfilled, Func rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Action rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Func fulfilled) { + var reaction = PromiseActionReaction.Create(fulfilled, null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Func rejected) { - var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseActionReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Func rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Func fulfilled) { + var reaction = PromiseFuncReaction.Create(fulfilled, (Func)null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Func> rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func> fulfilled, Func rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Func> fulfilled) { + var reaction = PromiseFuncReaction.Create(fulfilled, (Func)null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func> fulfilled, Func> rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Func rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Func fulfilled) { + var reaction = PromiseFuncReaction.Create(fulfilled, (Func)null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func fulfilled, Func> rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func> fulfilled, Func rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); + that.Then(reaction); + return reaction.Promise; + } + + public static IPromise Then(this IPromise that, Func> fulfilled) { + var reaction = PromiseFuncReaction.Create(fulfilled, (Func)null, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } public static IPromise Then(this IPromise that, Func> fulfilled, Func> rejected) { - var reaction = new PromiseFuncReaction(fulfilled, rejected, Promise.DefaultDispatcher); + var reaction = PromiseFuncReaction.Create(fulfilled, rejected, Promise.DefaultDispatcher); that.Then(reaction); return reaction.Promise; } @@ -126,6 +174,44 @@ namespace Implab { public static IPromise Catch(this IPromise that, Func> rejected) { return Then(that, (Func)null, rejected); } + + public static IPromise Finally(this IPromise that, Action final) { + return Then(that, final, e => { + final(); + throw e.Rethrow(); + }); + } + + public static IPromise Finally(this IPromise that, Func final) { + return Then(that, final, e => { + final(); + throw e.Rethrow(); + }); + } + + public static IPromise Finally(this IPromise that, Action final) { + return Then(that, x => { + final(); + return x; + }, new Func(e => { + final(); + throw e.Rethrow(); + })); + } + + public static IPromise Finally(this IPromise that, Func final) { + return Then(that, x => { + return final() + .Then(() => x); + }, new Func>(e => { + return final() + .Then(new Func(() => { + throw e.Rethrow(); + })); + })); + } + + } } diff --git a/Implab/PromiseFuncReaction`1.cs b/Implab/PromiseFuncReaction`1.cs --- a/Implab/PromiseFuncReaction`1.cs +++ b/Implab/PromiseFuncReaction`1.cs @@ -2,55 +2,90 @@ using System; using System.Diagnostics; namespace Implab { - class PromiseFuncReaction : PromiseReaction { + class PromiseFuncReaction : IResolvable { readonly Deferred m_next; + readonly IDispatcher m_dispatcher; + + readonly Action> m_fulfilled; + + readonly Action> m_rejected; + public IPromise Promise { get { return m_next.Promise; } } - public PromiseFuncReaction(Func fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public PromiseFuncReaction(Action> fulfilled, Action> rejected, Deferred next, IDispatcher dispatcher) { + m_next = next; + m_fulfilled = fulfilled; + m_rejected = rejected; + m_dispatcher = dispatcher; } - public PromiseFuncReaction(Func> fulfilled, Func> rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + public void Resolve() { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(ResolveImpl); + else + ResolveImpl(); + } else { + m_next.Resolve(default(TRet)); + } + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + void ResolveImpl () { + m_fulfilled(m_next); + } + + public void Reject(Exception error) { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(RejectImpl, error); + else + RejectImpl(error); + } else { + m_next.Reject(error); + } } - public PromiseFuncReaction(Func fulfilled, Func> rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + void RejectImpl(Exception error) { + m_rejected(error, m_next); + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseFuncReaction Create(Func fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - public PromiseFuncReaction(Func> fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseFuncReaction Create(Func> fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultReject(Exception reason) { - m_next.Reject(reason); + public static PromiseFuncReaction Create(Func fulfilled, Func> rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultResolve() { - throw new NotImplementedException(); + public static PromiseFuncReaction Create(Func> fulfilled, Func> rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } } } \ No newline at end of file diff --git a/Implab/PromiseFuncReaction`2.cs b/Implab/PromiseFuncReaction`2.cs --- a/Implab/PromiseFuncReaction`2.cs +++ b/Implab/PromiseFuncReaction`2.cs @@ -2,55 +2,96 @@ using System; using System.Diagnostics; namespace Implab { - class PromiseFuncReaction : PromiseReaction { + class PromiseFuncReaction : IResolvable { readonly Deferred m_next; - public IPromise Promise { + readonly IDispatcher m_dispatcher; + + readonly Action> m_fulfilled; + + readonly Action> m_rejected; + + public IPromise Promise { get { return m_next.Promise; } } - public PromiseFuncReaction(Func fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public PromiseFuncReaction(Action> fulfilled, Action> rejected, Deferred next, IDispatcher dispatcher) { + m_next = next; + m_fulfilled = fulfilled; + m_rejected = rejected; + m_dispatcher = dispatcher; } - public PromiseFuncReaction(Func> fulfilled, Func> rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + public void Resolve(TIn result) { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(ResolveImpl, result); + else + ResolveImpl(result); + } else { + try { + m_next.Resolve((TRet)(object)result); + } catch(Exception error) { + // handle cast exceptions + m_next.Reject(error); + } + } + } - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + void ResolveImpl (TIn result) { + m_fulfilled(result, m_next); + } + + public void Reject(Exception error) { + if (m_fulfilled != null) { + if (m_dispatcher != null) + m_dispatcher.Enqueue(RejectImpl, error); + else + RejectImpl(error); + } else { + m_next.Reject(error); + } } - public PromiseFuncReaction(Func fulfilled, Func> rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); + void RejectImpl(Exception error) { + m_rejected(error, m_next); + } + - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseFuncReaction Create(Func fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - public PromiseFuncReaction(Func> fulfilled, Func rejected, IDispatcher dispatcher) : base(dispatcher) { - m_next = new Deferred(dispatcher); - if (fulfilled != null) - FulfilHandler = PromiseHandler.Create(fulfilled, m_next); - - if (rejected != null) - RejectHandler = PromiseHandler.Create(rejected, m_next); + public static PromiseFuncReaction Create(Func> fulfilled, Func rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultReject(Exception reason) { - m_next.Reject(reason); + public static PromiseFuncReaction Create(Func fulfilled, Func> rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } - protected override void DefaultResolve(TIn result) { - m_next.Resolve((TRet)(object)result); + public static PromiseFuncReaction Create(Func> fulfilled, Func> rejected, IDispatcher dispatcher) { + return new PromiseFuncReaction( + fulfilled != null ? PromiseHandler.Create(fulfilled) : null, + rejected != null ? PromiseHandler.Create(rejected) : null, + new Deferred(), + dispatcher + ); } } } \ No newline at end of file diff --git a/Implab/PromiseHandler.cs b/Implab/PromiseHandler.cs --- a/Implab/PromiseHandler.cs +++ b/Implab/PromiseHandler.cs @@ -3,10 +3,10 @@ using System.Diagnostics; namespace Implab { class PromiseHandler { - public static Action Create(Action handler, Deferred next) { + public static Action Create(Action handler) { Debug.Assert(handler != null); - return (v) => { + return (v, next) => { try { handler(v); next.Resolve(); @@ -16,10 +16,10 @@ namespace Implab { }; } - public static Action Create(Func handler, Deferred next) { + public static Action Create(Func handler) { Debug.Assert(handler != null); - return (v) => { + return (v, next) => { try { next.Resolve(handler(v)); } catch (Exception err) { @@ -28,10 +28,10 @@ namespace Implab { }; } - public static Action Create(Func handler, Deferred next) { + public static Action> Create(Func handler) { Debug.Assert(handler != null); - return (v) => { + return (v, next) => { try { next.Resolve(handler(v)); } catch (Exception err) { @@ -40,9 +40,9 @@ namespace Implab { }; } - public static Action Create(Func> handler, Deferred next) { + public static Action> Create(Func> handler) { Debug.Assert(handler != null); - return (v) => { + return (v, next) => { try { next.Resolve(handler(v)); } catch (Exception err) { @@ -51,10 +51,10 @@ namespace Implab { }; } - public static Action Create(Action handler, Deferred next) { + public static Action Create(Action handler) { Debug.Assert(handler != null); - return () => { + return (next) => { try { handler(); next.Resolve(); @@ -64,10 +64,10 @@ namespace Implab { }; } - public static Action Create(Func handler, Deferred next) { + public static Action Create(Func handler) { Debug.Assert(handler != null); - return () => { + return (next) => { try { next.Resolve(handler()); } catch (Exception err) { @@ -76,10 +76,10 @@ namespace Implab { }; } - public static Action Create(Func handler, Deferred next) { + public static Action> Create(Func handler) { Debug.Assert(handler != null); - return () => { + return (next) => { try { next.Resolve(handler()); } catch (Exception err) { @@ -88,9 +88,9 @@ namespace Implab { }; } - public static Action Create(Func> handler, Deferred next) { + public static Action> Create(Func> handler) { Debug.Assert(handler != null); - return () => { + return (next) => { try { next.Resolve(handler()); } catch (Exception err) { diff --git a/Implab/PromiseReaction.cs b/Implab/PromiseReaction.cs deleted file mode 100644 --- a/Implab/PromiseReaction.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -namespace Implab { - /// - /// Базовыйй класс для создания обработчиков результов выполнения обещаний. - /// Данный объект связывает обработчик и обешание, при этом для выполнения - /// обработчика будет использоваться диспетчер. - /// - abstract class PromiseReaction : IResolvable { - readonly IDispatcher m_dispatcher; - - protected PromiseReaction(IDispatcher dispatcher) { - m_dispatcher = dispatcher; - } - - protected Action FulfilHandler { get; set; } - - protected Action RejectHandler { get; set; } - - public void Reject(Exception error) { - if (RejectHandler == null) - DefaultReject(error); - else if (m_dispatcher != null) - m_dispatcher.Enqueue(RejectHandler, error); - else - RejectHandler(error); - } - - public void Resolve() { - if (FulfilHandler == null) - DefaultResolve(); - else if (m_dispatcher != null) - m_dispatcher.Enqueue(FulfilHandler); - else - FulfilHandler(); - } - - protected abstract void DefaultResolve(); - - protected abstract void DefaultReject(Exception reason); - } -} \ No newline at end of file diff --git a/Implab/PromiseReaction`1.cs b/Implab/PromiseReaction`1.cs deleted file mode 100644 --- a/Implab/PromiseReaction`1.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -namespace Implab { - /// - /// Базовыйй класс для создания обработчиков результов выполнения обещаний. - /// Данный объект связывает обработчик и обешание, при этом для выполнения - /// обработчика будет использоваться диспетчер. - /// - abstract class PromiseReaction : IResolvable { - readonly IDispatcher m_dispatcher; - - protected PromiseReaction(IDispatcher dispatcher) { - m_dispatcher = dispatcher; - } - - protected Action FulfilHandler { get; set; } - - protected Action RejectHandler { get; set; } - - public void Reject(Exception error) { - if (RejectHandler == null) - DefaultReject(error); - else if (m_dispatcher != null) - m_dispatcher.Enqueue(RejectHandler, error); - else - RejectHandler(error); - } - - public void Resolve(T result) { - if (FulfilHandler == null) - DefaultResolve(result); - else if (m_dispatcher != null) - m_dispatcher.Enqueue(FulfilHandler, result); - else - FulfilHandler(result); - } - - protected abstract void DefaultResolve(T result); - - protected abstract void DefaultReject(Exception reason); - } -} \ No newline at end of file diff --git a/Implab/RejectedPromise.cs b/Implab/RejectedPromise.cs --- a/Implab/RejectedPromise.cs +++ b/Implab/RejectedPromise.cs @@ -24,11 +24,11 @@ namespace Implab } public void Join() { - m_reason.ThrowInvocationException(); + throw m_reason.Wrap(); } public void Join(int timeout) { - m_reason.ThrowInvocationException(); + throw m_reason.Wrap(); } public void Then(IResolvable next) { diff --git a/Implab/RejectedPromise`1.cs b/Implab/RejectedPromise`1.cs --- a/Implab/RejectedPromise`1.cs +++ b/Implab/RejectedPromise`1.cs @@ -24,21 +24,19 @@ namespace Implab } void IPromise.Join() { - m_reason.ThrowInvocationException(); + throw m_reason.Wrap(); } void IPromise.Join(int timeout) { - m_reason.ThrowInvocationException(); + throw m_reason.Wrap(); } public T Join() { - m_reason.ThrowInvocationException(); - throw new Exception(); // unreachable code + throw m_reason.Wrap(); } public T Join(int timeout) { - m_reason.ThrowInvocationException(); - throw new Exception(); // unreachable code + throw m_reason.Wrap(); } public void Then(IResolvable next) {