changeset 249:d82909310094 v3

Implab.Test moved to xunit Complete set of PromiseHelpers (Then, Catch, Finally) Removed obsolete types ICancellable, ICancellationToken
author cin
date Wed, 31 Jan 2018 11:28:38 +0300
parents 5cb4826c2c2a
children 9f63dade3a40
files Implab.Test/AsyncTests.cs Implab.Test/CancelationTests.cs Implab.Test/Implab.Test.csproj Implab.Test/Implab.Test.mono.csproj Implab.Test/Mock/MockPollingComponent.cs Implab.Test/Mock/MockRunnableComponent.cs Implab.Test/PollingComponentTests.cs Implab.Test/PromiseHelper.cs Implab.Test/Properties/AssemblyInfo.cs Implab.Test/RunnableComponentTests.cs Implab.Test/UnitTest1.cs Implab.Test/packages.config Implab/CancellationToken.cs Implab/Deferred.cs Implab/Deferred`1.cs Implab/ExceptionHelpers.cs Implab/ICancellable.cs Implab/ICancellationToken.cs Implab/IDeferredT.cs Implab/Promise.cs Implab/PromiseActionReaction.cs Implab/PromiseActionReaction`1.cs Implab/PromiseExtensions.cs Implab/PromiseFuncReaction`1.cs Implab/PromiseFuncReaction`2.cs Implab/PromiseHandler.cs Implab/PromiseReaction.cs Implab/PromiseReaction`1.cs Implab/RejectedPromise.cs Implab/RejectedPromise`1.cs
diffstat 30 files changed, 486 insertions(+), 2195 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Test/AsyncTests.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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<int>();
-            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<int>();
-            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<bool>();
-            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<bool>();
-            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<int>();
-            p.Resolve(100);
-            Assert.AreEqual(p.Join(), 100);
-        }
-
-        [TestMethod]
-        public void JoinFailTest() {
-            var p = new Promise<int>();
-            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<int>();
-
-            var p2 = p.Then(x => x.ToString());
-            p.Resolve(100);
-
-            Assert.AreEqual(p2.Join(), "100");
-        }
-
-        [TestMethod]
-        public void FixErrorTest() {
-            var p = new Promise<int>();
-
-            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<int>();
-
-            var p3 = p1.Chain(x => {
-                var p2 = new Promise<string>();
-                p2.Resolve(x.ToString());
-                return p2;
-            });
-
-            p1.Resolve(100);
-
-            Assert.AreEqual(p3.Join(), "100");
-        }
-
-        [TestMethod]
-        public void ChainFailTest() {
-            var p1 = new Promise<int>();
-
-            var p3 = p1.Chain(x => {
-                var p2 = new Promise<string>();
-                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>();
-            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>();
-            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<int>();
-
-            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<wBatch; i++)
-                        buffer[i] = 1;
-
-                    for(int i =0; i < wCount; i++)
-                        queue.EnqueueRange(buffer,0,wBatch);
-                    Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
-                },
-                () => {
-                    var buffer = new int[wBatch];
-                    for(int i = 0; i<wBatch; i++)
-                        buffer[i] = 2;
-
-                    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[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<int>();
-
-            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<wBatch; i++)
-                        buffer[i] = 1;
-
-                    for(int i =0; i < wCount; i++)
-                        queue.EnqueueRange(buffer,0,wBatch);
-                    Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
-                },
-                () => {
-                    var buffer = new int[wBatch];
-                    for(int i = 0; i<wBatch; i++)
-                        buffer[i] = 2;
-
-                    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<wBatch; i++)
-                        buffer[i] = 3;
-
-                    for(int i =0; i < wCount; i++)
-                        queue.EnqueueRange(buffer,0,wBatch);
-                    Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1);
-                },
-                () => {
-                    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<int>();
-
-            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<wBatch; i++)
-                        buffer[i] = 1;
-
-                    for(int i =0; i < wCount; i++)
-                        queue.EnqueueRange(buffer,0,wBatch);
-                    Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
-                },
-                () => {
-                    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<wBatch; i++)
-                        buffer[i] = 1;
-
-                    for(int i =0; i < wCount; i++)
-                        queue.EnqueueRange(buffer,0,wBatch);
-                    Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1);
-                },
-                /*() => {
-                    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<bool>();
-            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<string>();
-
-            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<int>();
-            promise.Resolve(10);
-            var res = await promise;
-
-            Assert.AreEqual(10, res);
-        }
-
-        #endif
-    }
-}
-
--- a/Implab.Test/CancelationTests.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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) {
-            }
-        }
-    }
-}
-
--- a/Implab.Test/Implab.Test.csproj	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab.Test/Implab.Test.csproj	Wed Jan 31 11:28:38 2018 +0300
@@ -1,88 +1,16 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project Sdk="Microsoft.NET.Sdk">
+
   <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>8.0.30703</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>Implab.Test</RootNamespace>
-    <AssemblyName>Implab.Test</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <FileAlignment>512</FileAlignment>
-    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <TargetFrameworkProfile />
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
+
+    <IsPackable>false</IsPackable>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
-    <Reference Include="System" />
-    <Reference Include="System.Core">
-      <RequiredTargetFramework>3.5</RequiredTargetFramework>
-    </Reference>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="AsyncTests.cs" />
-    <Compile Include="CancelationTests.cs" />
-    <Compile Include="Mock\MockPollingComponent.cs" />
-    <Compile Include="Mock\MockRunnableComponent.cs" />
-    <Compile Include="PollingComponentTests.cs" />
-    <Compile Include="PromiseHelper.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="RunnableComponentTests.cs" />
+    <ProjectReference Include="../Implab/Implab.csproj"/>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
+    <PackageReference Include="xunit" Version="2.3.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
+    <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Implab\Implab.csproj">
-      <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
-      <Name>Implab</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup />
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
-       Other similar extension points exist, see Microsoft.Common.targets.
-  <Target Name="BeforeBuild">
-  </Target>
-  <Target Name="AfterBuild">
-  </Target>
-  -->
-</Project>
\ No newline at end of file
+
+</Project>
--- a/Implab.Test/Implab.Test.mono.csproj	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>8.0.30703</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <RootNamespace>Implab.Test</RootNamespace>
-    <AssemblyName>Implab.Test</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <ReleaseVersion>0.2</ReleaseVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;MONO</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-    <DefineConstants>MONO</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <DefineConstants>NET_4_5;MONO</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
-      <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
-      <Private>True</Private>
-    </Reference>
-    <Reference Include="System" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <Compile Include="AsyncTests.cs" />
-    <Compile Include="PromiseHelper.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="CancelationTests.cs" />
-    <Compile Include="RunnableComponentTests.cs" />
-    <Compile Include="PollingComponentTests.cs" />
-    <Compile Include="Mock\MockRunnableComponent.cs" />
-    <Compile Include="Mock\MockPollingComponent.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Implab\Implab.csproj">
-      <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
-      <Name>Implab</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="packages.config" />
-  </ItemGroup>
-</Project>
\ No newline at end of file
--- a/Implab.Test/Mock/MockPollingComponent.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-using System;
-using Implab.Components;
-
-namespace Implab.Test.Mock {
-    class MockPollingComponent : PollingComponent {
-        public MockPollingComponent(TimeSpan interval, Func<Func<ICancellationToken, IPromise>, IPromise> dispatcher, bool initialized) : base(interval, dispatcher, initialized)  {
-        }
-
-        public Action MockInit {
-            get;
-            set;
-        }
-
-        public Action<Exception> MockOnError {
-            get;
-            set;
-        }
-
-        public Action<Exception> MockOnCancel {
-            get;
-            set;
-        }
-
-        public Func<IPromise> MockStart {
-            get;
-            set;
-        }
-
-        public Func<IPromise> MockStop {
-            get;
-            set;
-        }
-
-        public Func<ICancellationToken, IPromise> 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);
-        }
-    }
-}
-
--- a/Implab.Test/Mock/MockRunnableComponent.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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<IPromise> MockStart {
-            get;
-            set;
-        }
-
-        public Func<IPromise> MockStop {
-            get;
-            set;
-        }
-
-        public Action<bool> 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);
-        }
-    }
-}
-
--- a/Implab.Test/PollingComponentTests.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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);
-        }
-
-    }
-}
-
--- a/Implab.Test/PromiseHelper.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab.Test/PromiseHelper.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -1,21 +1,39 @@
-using Implab.Parallels;
+using Implab;
+using System;
 using System.Threading;
 
 namespace Implab.Test {
     static class PromiseHelper {
-        public static IPromise<T> Sleep<T>(int timeout, T retVal) {
-            return AsyncPool.Invoke((ct) => {
-                ct.CancellationRequested(ct.CancelOperation);
-                Thread.Sleep(timeout);
-                return retVal;
+        public static IPromise<T> Sleep<T>(int timeout, T retVal, CancellationToken ct = default(CancellationToken)) {
+
+            Timer timer = null;
+
+            return Promise.Create<T>((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);
             });
         }
     }
--- a/Implab.Test/Properties/AssemblyInfo.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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.*")]
--- a/Implab.Test/RunnableComponentTests.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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();
-        }
-
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Test/UnitTest1.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -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();
+            }
+        }
+    }
+}
--- a/Implab.Test/packages.config	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="NUnit" version="2.6.4" targetFramework="net45" />
-</packages>
\ No newline at end of file
--- a/Implab/CancellationToken.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-using System;
-using System.Threading;
-using Implab.Parallels;
-
-namespace Implab {
-    /// <summary>
-    /// 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.
-    /// </summary>
-    public class CancellationToken : AbstractEvent<Action<Exception>> {
-        public CancellationToken() {
-            
-        }
-        
-        public void RequestCancellation() {
-
-        }
-
-        public void RequestCancellation(Exception reason) {
-
-        }
-        
-        protected override void SignalHandler(Action<Exception> handler) {
-            throw new NotImplementedException();
-        }
-    }
-}
\ No newline at end of file
--- a/Implab/Deferred.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/Deferred.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -8,51 +8,46 @@
     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
--- a/Implab/Deferred`1.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/Deferred`1.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -4,46 +4,41 @@
 namespace Implab {
     public class Deferred<T> : IResolvable<T> {
         readonly Promise<T> m_promise;
-        readonly IDispatcher m_dispatcher;
 
-        internal Deferred(IDispatcher dispatcher) : this(new Promise<T>(), dispatcher) {
+        internal Deferred() {
+            m_promise = new Promise<T>();
         }
 
-        internal Deferred(Promise<T> promise, IDispatcher dispatcher) {
+        protected Deferred(Promise<T> promise) {
             Debug.Assert(promise != null);
             m_promise = promise;
-            m_dispatcher = dispatcher;
         }
 
         public IPromise<T> 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<T> thenable) {
+        public virtual void Resolve(IPromise<T> 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<T> thenable) {
             try {
                 thenable.Then(this);
             } catch (Exception err) {
--- a/Implab/ExceptionHelpers.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/ExceptionHelpers.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -3,16 +3,19 @@
 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
--- a/Implab/ICancellable.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-using System;
-
-namespace Implab {
-    public interface ICancellable {
-        void Cancel();
-        void Cancel(Exception reason);
-    }
-}
--- a/Implab/ICancellationToken.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-using System;
-
-namespace Implab {
-    public interface ICancellationToken {
-        /// <summary>
-        /// Indicates wherther the cancellation was requested.
-        /// </summary>
-        bool IsCancellationRequested { get ; }
-
-        /// <summary>
-        /// The reason why the operation should be cancelled.
-        /// </summary>
-        Exception CancellationReason { get ; }
-
-        /// <summary>
-        /// Accepts if requested.
-        /// </summary>
-        /// <returns><c>true</c>, if if requested was accepted, <c>false</c> otherwise.</returns>
-        bool CancelOperationIfRequested();
-
-        /// <summary>
-        /// Sets the token to cancelled state.
-        /// </summary>
-        /// <param name="reason">The reason why the operation was cancelled.</param>
-        void CancelOperation(Exception reason);
-
-        /// <summary>
-        /// Adds the listener for the cancellation request, is the cancellation was requested the <paramref name="handler"/>
-        /// is executed immediatelly.
-        /// </summary>
-        /// <param name="handler">The handler which will be executed if the cancel occurs.</param>
-        void CancellationRequested(Action<Exception> handler);
-
-    }
-}
-
--- a/Implab/IDeferredT.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-using System;
-
-namespace Implab {
-    public interface IDeferred<in T> {
-        void Resolve(T value);
-
-        void Reject(Exception error);
-
-        void SetCancelled(Exception error);
-    }
-}
-
--- a/Implab/Promise.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/Promise.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -169,8 +169,7 @@
         public static IPromise<T> Create<T>(PromiseExecutor<T> executor) {
             Safe.ArgumentNotNull(executor, nameof(executor));
 
-            var p = new Promise<T>();
-            var d = new Deferred<T>(p, DefaultDispatcher);
+            var d = new Deferred<T>();
 
             try {
                 executor(d);
@@ -182,7 +181,7 @@
         }
 
         public static IPromise All(IEnumerable<IPromise> 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 @@
             return all.ResultPromise;
         }
 
-        public static IPromise<T[]> All<T>(IEnumerable<IPromise<T>> promises, Func<T, IPromise> cleanup, Action cancel) {
-            var d = new Deferred<T[]>(DefaultDispatcher);
+        public static IPromise<T[]> All<T>(IEnumerable<IPromise<T>> promises, Func<T, IPromise> cleanup = null, Action cancel = null) {
+            var d = new Deferred<T[]>();
             var all = new PromiseAll<T>(d, cleanup, cancel);
             foreach (var promise in promises) {
                 all.AddPromise(promise);
--- a/Implab/PromiseActionReaction.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/PromiseActionReaction.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -2,56 +2,91 @@
 using System.Diagnostics;
 
 namespace Implab {
-    class PromiseActionReaction : PromiseReaction {
+    class PromiseActionReaction : IResolvable {
 
         readonly Deferred m_next;
 
+        readonly IDispatcher m_dispatcher;
+
+        readonly Action<Deferred> m_fulfilled;
+
+        readonly Action<Exception, Deferred> m_rejected;
+
         public IPromise Promise {
             get { return m_next.Promise; }
         }
 
-        public PromiseActionReaction(Action fulfilled, Action<Exception> 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<Deferred> fulfilled, Action<Exception, Deferred> rejected, Deferred next, IDispatcher dispatcher) {
+            m_next = next;
+            m_fulfilled = fulfilled;
+            m_rejected = rejected;
+            m_dispatcher = dispatcher;
         }
 
-        public PromiseActionReaction(Func<IPromise> fulfilled, Func<Exception, IPromise> 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<Exception, IPromise> 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<Exception> rejected, IDispatcher dispatcher) {
+            return new PromiseActionReaction(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred(),
+                dispatcher
+            );
         }
 
-        public PromiseActionReaction(Func<IPromise> fulfilled, Action<Exception> 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<IPromise> fulfilled, Action<Exception> 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<Exception, IPromise> 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<IPromise> fulfilled, Func<Exception, IPromise> 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
--- a/Implab/PromiseActionReaction`1.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/PromiseActionReaction`1.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -2,55 +2,90 @@
 using System.Diagnostics;
 
 namespace Implab {
-    class PromiseActionReaction<T> : PromiseReaction<T> {
+    class PromiseActionReaction<T> : IResolvable<T> {
         readonly Deferred m_next;
 
+        readonly IDispatcher m_dispatcher;
+
+        readonly Action<T, Deferred> m_fulfilled;
+
+        readonly Action<Exception, Deferred> m_rejected;
+
         public IPromise Promise {
             get { return m_next.Promise; }
         }
 
-        public PromiseActionReaction(Action<T> fulfilled, Action<Exception> 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<T, Deferred> fulfilled, Action<Exception, Deferred> rejected, Deferred next, IDispatcher dispatcher) {
+            m_next = next;
+            m_fulfilled = fulfilled;
+            m_rejected = rejected;
+            m_dispatcher = dispatcher;
         }
 
-        public PromiseActionReaction(Func<T, IPromise> fulfilled, Func<Exception, IPromise> 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<T> fulfilled, Func<Exception, IPromise> 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<T> Create(Action<T> fulfilled, Action<Exception> rejected, IDispatcher dispatcher) {
+            return new PromiseActionReaction<T>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred(),
+                dispatcher
+            );
         }
 
-        public PromiseActionReaction(Func<T, IPromise> fulfilled, Action<Exception> 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<T> Create(Func<T,IPromise> fulfilled, Action<Exception> rejected, IDispatcher dispatcher) {
+            return new PromiseActionReaction<T>(
+                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<T> Create(Action<T> fulfilled, Func<Exception, IPromise> rejected, IDispatcher dispatcher) {
+            return new PromiseActionReaction<T>(
+                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<T> Create(Func<T,IPromise> fulfilled, Func<Exception, IPromise> rejected, IDispatcher dispatcher) {
+            return new PromiseActionReaction<T>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred(),
+                dispatcher
+            );
         }
     }
 }
\ No newline at end of file
--- a/Implab/PromiseExtensions.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/PromiseExtensions.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -8,97 +8,145 @@
     public static class PromiseExtensions {
 
         public static IPromise Then(this IPromise that, Action fulfilled, Action<Exception> 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<Exception, IPromise> 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<IPromise> fulfilled, Action<Exception> 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<IPromise> fulfilled) {
+            var reaction = PromiseActionReaction.Create(fulfilled, null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise Then(this IPromise that, Func<IPromise> fulfilled, Func<Exception, IPromise> 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<T>(this IPromise<T> that, Action<T> fulfilled, Action<Exception> rejected) {
-            var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseActionReaction<T>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
+            that.Then(reaction);
+            return reaction.Promise;
+        }
+
+        public static IPromise Then<T>(this IPromise<T> that, Action<T> fulfilled) {
+            var reaction = PromiseActionReaction<T>.Create(fulfilled, null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise Then<T>(this IPromise<T> that, Action<T> fulfilled, Func<Exception, IPromise> rejected) {
-            var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseActionReaction<T>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise Then<T>(this IPromise<T> that, Func<T, IPromise> fulfilled, Action<Exception> rejected) {
-            var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseActionReaction<T>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
+            that.Then(reaction);
+            return reaction.Promise;
+        }
+
+        public static IPromise Then<T>(this IPromise<T> that, Func<T, IPromise> fulfilled) {
+            var reaction = PromiseActionReaction<T>.Create(fulfilled, null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise Then<T>(this IPromise<T> that, Func<T, IPromise> fulfilled, Func<Exception, IPromise> rejected) {
-            var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseActionReaction<T>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tout>(this IPromise that, Func<Tout> fulfilled, Func<Exception, Tout> rejected) {
-            var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
+            that.Then(reaction);
+            return reaction.Promise;
+        }
+
+        public static IPromise<Tout> Then<Tout>(this IPromise that, Func<Tout> fulfilled) {
+            var reaction = PromiseFuncReaction<Tout>.Create(fulfilled, (Func<Exception, Tout>)null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tout>(this IPromise that, Func<Tout> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
-            var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tout>(this IPromise that, Func<IPromise<Tout>> fulfilled, Func<Exception, Tout> rejected) {
-            var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
+            that.Then(reaction);
+            return reaction.Promise;
+        }
+
+        public static IPromise<Tout> Then<Tout>(this IPromise that, Func<IPromise<Tout>> fulfilled) {
+            var reaction = PromiseFuncReaction<Tout>.Create(fulfilled, (Func<Exception, Tout>)null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tout>(this IPromise that, Func<IPromise<Tout>> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
-            var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, Tout> fulfilled, Func<Exception, Tout> rejected) {
-            var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tin, Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
+            that.Then(reaction);
+            return reaction.Promise;
+        }
+
+        public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, Tout> fulfilled) {
+            var reaction = PromiseFuncReaction<Tin, Tout>.Create(fulfilled, (Func<Exception, Tout>)null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, Tout> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
-            var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tin, Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, IPromise<Tout>> fulfilled, Func<Exception, Tout> rejected) {
-            var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tin, Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
+            that.Then(reaction);
+            return reaction.Promise;
+        }
+
+        public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, IPromise<Tout>> fulfilled) {
+            var reaction = PromiseFuncReaction<Tin, Tout>.Create(fulfilled, (Func<Exception, Tout>)null, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
 
         public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, IPromise<Tout>> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
-            var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
+            var reaction = PromiseFuncReaction<Tin, Tout>.Create(fulfilled, rejected, Promise.DefaultDispatcher);
             that.Then(reaction);
             return reaction.Promise;
         }
@@ -126,6 +174,44 @@
         public static IPromise<Tout> Catch<Tin, Tout>(this IPromise<Tin> that, Func<Exception, IPromise<Tout>> rejected) {
             return Then(that, (Func<Tin, Tout>)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<IPromise> final) {
+            return Then(that, final, e => {
+                final();
+                throw e.Rethrow();
+            });
+        }
+
+        public static IPromise<T> Finally<T>(this IPromise<T> that, Action final) {
+            return Then<T, T>(that, x => {
+                final();
+                return x;
+            }, new Func<Exception, T>(e => {
+                final();
+                throw e.Rethrow();
+            }));
+        }
+
+        public static IPromise<T> Finally<T>(this IPromise<T> that, Func<IPromise> final) {
+            return Then<T, T>(that, x => {
+                return final()
+                    .Then(() => x);
+            }, new Func<Exception, IPromise<T>>(e => {
+                return final()
+                    .Then(new Func<T>(() => {
+                        throw e.Rethrow();
+                    }));
+            }));
+        }
+
+
     }
 }
 
--- a/Implab/PromiseFuncReaction`1.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/PromiseFuncReaction`1.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -2,55 +2,90 @@
 using System.Diagnostics;
 
 namespace Implab {
-    class PromiseFuncReaction<TRet> : PromiseReaction {
+    class PromiseFuncReaction<TRet> : IResolvable {
         readonly Deferred<TRet> m_next;
 
+        readonly IDispatcher m_dispatcher;
+
+        readonly Action<Deferred<TRet>> m_fulfilled;
+
+        readonly Action<Exception, Deferred<TRet>> m_rejected;
+
         public IPromise<TRet> Promise {
             get { return m_next.Promise; }
         }
 
-        public PromiseFuncReaction(Func<TRet> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(dispatcher);
-            if (fulfilled != null)
-                FulfilHandler = PromiseHandler.Create(fulfilled, m_next);
-
-            if (rejected != null)
-                RejectHandler = PromiseHandler.Create(rejected, m_next);
+        public PromiseFuncReaction(Action<Deferred<TRet>> fulfilled, Action<Exception, Deferred<TRet>> rejected, Deferred<TRet> next, IDispatcher dispatcher) {
+            m_next = next;
+            m_fulfilled = fulfilled;
+            m_rejected = rejected;
+            m_dispatcher = dispatcher;
         }
 
-        public PromiseFuncReaction(Func<IPromise<TRet>> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(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<TRet> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(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<TRet> Create(Func<TRet> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
 
-        public PromiseFuncReaction(Func<IPromise<TRet>> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(dispatcher);
-            if (fulfilled != null)
-                FulfilHandler = PromiseHandler.Create(fulfilled, m_next);
-
-            if (rejected != null)
-                RejectHandler = PromiseHandler.Create(rejected, m_next);
+        public static PromiseFuncReaction<TRet> Create(Func<IPromise<TRet>> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
 
-        protected override void DefaultReject(Exception reason) {
-            m_next.Reject(reason);
+        public static PromiseFuncReaction<TRet> Create(Func<TRet> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
 
-        protected override void DefaultResolve() {
-            throw new NotImplementedException();
+        public static PromiseFuncReaction<TRet> Create(Func<IPromise<TRet>> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
     }
 }
\ No newline at end of file
--- a/Implab/PromiseFuncReaction`2.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/PromiseFuncReaction`2.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -2,55 +2,96 @@
 using System.Diagnostics;
 
 namespace Implab {
-    class PromiseFuncReaction<TIn, TRet> : PromiseReaction<TIn> {
+    class PromiseFuncReaction<TIn, TRet> : IResolvable<TIn> {
         readonly Deferred<TRet> m_next;
 
-        public IPromise<TRet> Promise { 
+        readonly IDispatcher m_dispatcher;
+
+        readonly Action<TIn, Deferred<TRet>> m_fulfilled;
+
+        readonly Action<Exception, Deferred<TRet>> m_rejected;
+
+        public IPromise<TRet> Promise {
             get { return m_next.Promise; }
         }
 
-        public PromiseFuncReaction(Func<TIn, TRet> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(dispatcher);
-            if (fulfilled != null)
-                FulfilHandler = PromiseHandler.Create(fulfilled, m_next);
-
-            if (rejected != null)
-                RejectHandler = PromiseHandler.Create(rejected, m_next);
+        public PromiseFuncReaction(Action<TIn, Deferred<TRet>> fulfilled, Action<Exception, Deferred<TRet>> rejected, Deferred<TRet> next, IDispatcher dispatcher) {
+            m_next = next;
+            m_fulfilled = fulfilled;
+            m_rejected = rejected;
+            m_dispatcher = dispatcher;
         }
 
-        public PromiseFuncReaction(Func<TIn, IPromise<TRet>> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(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<TIn, TRet> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(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<TIn,TRet> Create(Func<TIn,TRet> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TIn,TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
 
-        public PromiseFuncReaction(Func<TIn, IPromise<TRet>> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) : base(dispatcher) {
-            m_next = new Deferred<TRet>(dispatcher);
-            if (fulfilled != null)
-                FulfilHandler = PromiseHandler.Create(fulfilled, m_next);
-
-            if (rejected != null)
-                RejectHandler = PromiseHandler.Create(rejected, m_next);
+        public static PromiseFuncReaction<TIn,TRet> Create(Func<TIn,IPromise<TRet>> fulfilled, Func<Exception, TRet> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TIn,TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
 
-        protected override void DefaultReject(Exception reason) {
-            m_next.Reject(reason);
+        public static PromiseFuncReaction<TIn,TRet> Create(Func<TIn,TRet> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TIn,TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
 
-        protected override void DefaultResolve(TIn result) {
-            m_next.Resolve((TRet)(object)result);
+        public static PromiseFuncReaction<TIn,TRet> Create(Func<TIn,IPromise<TRet>> fulfilled, Func<Exception, IPromise<TRet>> rejected, IDispatcher dispatcher) {
+            return new PromiseFuncReaction<TIn,TRet>(
+                fulfilled != null ? PromiseHandler.Create(fulfilled) : null,
+                rejected != null ? PromiseHandler.Create(rejected) : null,
+                new Deferred<TRet>(),
+                dispatcher
+            );
         }
     }
 }
\ No newline at end of file
--- a/Implab/PromiseHandler.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/PromiseHandler.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -3,10 +3,10 @@
 
 namespace Implab {
     class PromiseHandler {
-        public static Action<T> Create<T>(Action<T> handler, Deferred next) {
+        public static Action<T, Deferred> Create<T>(Action<T> handler) {
             Debug.Assert(handler != null);
 
-            return (v) => {
+            return (v, next) => {
                 try {
                     handler(v);
                     next.Resolve();
@@ -16,10 +16,10 @@
             };
         }
 
-        public static Action<T> Create<T>(Func<T, IPromise> handler, Deferred next) {
+        public static Action<T, Deferred> Create<T>(Func<T, IPromise> handler) {
             Debug.Assert(handler != null);
 
-            return (v) => {
+            return (v, next) => {
                 try {
                     next.Resolve(handler(v));
                 } catch (Exception err) {
@@ -28,10 +28,10 @@
             };
         }
 
-        public static Action<T> Create<T, T2>(Func<T, T2> handler, Deferred<T2> next) {
+        public static Action<T, Deferred<T2>> Create<T, T2>(Func<T, T2> handler) {
             Debug.Assert(handler != null);
 
-            return (v) => {
+            return (v, next) => {
                 try {
                     next.Resolve(handler(v));
                 } catch (Exception err) {
@@ -40,9 +40,9 @@
             };
         }
 
-        public static Action<T> Create<T, T2>(Func<T, IPromise<T2>> handler, Deferred<T2> next) {
+        public static Action<T, Deferred<T2>> Create<T, T2>(Func<T, IPromise<T2>> handler) {
             Debug.Assert(handler != null);
-            return (v) => {
+            return (v, next) => {
                 try {
                     next.Resolve(handler(v));
                 } catch (Exception err) {
@@ -51,10 +51,10 @@
             };
         }
 
-        public static Action Create(Action handler, Deferred next) {
+        public static Action<Deferred> Create(Action handler) {
             Debug.Assert(handler != null);
 
-            return () => {
+            return (next) => {
                 try {
                     handler();
                     next.Resolve();
@@ -64,10 +64,10 @@
             };
         }
 
-        public static Action Create(Func<IPromise> handler, Deferred next) {
+        public static Action<Deferred> Create(Func<IPromise> handler) {
             Debug.Assert(handler != null);
 
-            return () => {
+            return (next) => {
                 try {
                     next.Resolve(handler());
                 } catch (Exception err) {
@@ -76,10 +76,10 @@
             };
         }
 
-        public static Action Create<T2>(Func<T2> handler, Deferred<T2> next) {
+        public static Action<Deferred<T2>> Create<T2>(Func<T2> handler) {
             Debug.Assert(handler != null);
 
-            return () => {
+            return (next) => {
                 try {
                     next.Resolve(handler());
                 } catch (Exception err) {
@@ -88,9 +88,9 @@
             };
         }
 
-        public static Action Create<T2>(Func<IPromise<T2>> handler, Deferred<T2> next) {
+        public static Action<Deferred<T2>> Create<T2>(Func<IPromise<T2>> handler) {
             Debug.Assert(handler != null);
-            return () => {
+            return (next) => {
                 try {
                     next.Resolve(handler());
                 } catch (Exception err) {
--- a/Implab/PromiseReaction.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-using System;
-
-namespace Implab {
-    /// <summary>
-    /// Базовыйй класс для создания обработчиков результов выполнения обещаний.
-    /// Данный объект связывает обработчик и обешание, при этом для выполнения
-    /// обработчика будет использоваться диспетчер.
-    /// </summary>
-    abstract class PromiseReaction : IResolvable {
-        readonly IDispatcher m_dispatcher;
-
-        protected PromiseReaction(IDispatcher dispatcher) {
-            m_dispatcher = dispatcher;
-        }
-
-        protected Action FulfilHandler { get; set; }
-
-        protected Action<Exception> 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
--- a/Implab/PromiseReaction`1.cs	Tue Jan 30 01:37:17 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-using System;
-
-namespace Implab {
-    /// <summary>
-    /// Базовыйй класс для создания обработчиков результов выполнения обещаний.
-    /// Данный объект связывает обработчик и обешание, при этом для выполнения
-    /// обработчика будет использоваться диспетчер.
-    /// </summary>
-    abstract class PromiseReaction<T> : IResolvable<T> {
-        readonly IDispatcher m_dispatcher;
-
-        protected PromiseReaction(IDispatcher dispatcher) {
-            m_dispatcher = dispatcher;
-        }
-
-        protected Action<T> FulfilHandler { get; set; }
-
-        protected Action<Exception> 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
--- a/Implab/RejectedPromise.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/RejectedPromise.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -24,11 +24,11 @@
         }
 
         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) {
--- a/Implab/RejectedPromise`1.cs	Tue Jan 30 01:37:17 2018 +0300
+++ b/Implab/RejectedPromise`1.cs	Wed Jan 31 11:28:38 2018 +0300
@@ -24,21 +24,19 @@
         }
 
         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) {