Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'master' of github.com:OmerMor/AsyncBridge

  • Loading branch information...
commit efaf22381fcff4794d91c940ef6a6e46c8399c30 2 parents e9fa1aa + 627d325
Omer Mor authored April 13, 2012
1  src/AsyncBridge/AsyncBridge.csproj
@@ -36,6 +36,7 @@
36 36
   <ItemGroup>
37 37
     <Compile Include="AsyncTaskMethodBuilder.cs" />
38 38
     <Compile Include="AsyncVoidMethodBuilder.cs" />
  39
+    <Compile Include="ConfigurableTaskAwaitable.cs" />
39 40
     <Compile Include="DelayTask.cs" />
40 41
     <Compile Include="TaskUtils.cs" />
41 42
     <Compile Include="VoidTaskResult.cs" />
42  src/AsyncBridge/ConfigurableTaskAwaitable.cs
... ...
@@ -0,0 +1,42 @@
  1
+using System.Threading.Tasks;
  2
+
  3
+namespace AsyncBridge
  4
+{
  5
+    /// <summary>
  6
+    /// An awaitable which wraps a class, maybe preventing it from capturing the SynchronizationContext
  7
+    /// </summary>
  8
+    public class ConfigurableTaskAwaitable<T>
  9
+    {
  10
+        private readonly Task<T> m_task;
  11
+        private readonly bool m_useCapturedContext;
  12
+
  13
+        public ConfigurableTaskAwaitable(Task<T> task, bool useCapturedContext)
  14
+        {
  15
+            m_task = task;
  16
+            m_useCapturedContext = useCapturedContext;
  17
+        }
  18
+
  19
+        public TaskAwaiter<T> GetAwaiter()
  20
+        {
  21
+            return new TaskAwaiter<T>(m_task, m_useCapturedContext);
  22
+        }
  23
+    }
  24
+
  25
+    // ZOMG why isn't void an actual type
  26
+    public class ConfigurableTaskAwaitable
  27
+    {
  28
+        private readonly Task m_task;
  29
+        private readonly bool m_useCapturedContext;
  30
+
  31
+        public ConfigurableTaskAwaitable(Task task, bool useCapturedContext)
  32
+        {
  33
+            m_task = task;
  34
+            m_useCapturedContext = useCapturedContext;
  35
+        }
  36
+
  37
+        public TaskAwaiter GetAwaiter()
  38
+        {
  39
+            return new TaskAwaiter(m_task, m_useCapturedContext);
  40
+        }
  41
+    }
  42
+}
18  src/AsyncBridge/TaskAwaiter.cs
@@ -5,10 +5,12 @@ namespace System.Threading.Tasks
5 5
     public struct TaskAwaiter : INotifyCompletion
6 6
     {
7 7
         private readonly Task m_task;
  8
+        private readonly bool m_useCapturedContext;
8 9
 
9  
-        internal TaskAwaiter(Task task)
  10
+        internal TaskAwaiter(Task task, bool useCapturedContext = true)
10 11
         {
11 12
             m_task = task;
  13
+            m_useCapturedContext = useCapturedContext;
12 14
         }
13 15
 
14 16
         internal static TaskScheduler TaskScheduler
@@ -16,7 +18,7 @@ internal static TaskScheduler TaskScheduler
16 18
             get
17 19
             {
18 20
                 var taskScheduler = SynchronizationContext.Current == null
19  
-                                        ? TaskScheduler.Default
  21
+                                        ? TaskScheduler.Current
20 22
                                         : TaskScheduler.FromCurrentSynchronizationContext();
21 23
                 return taskScheduler;
22 24
             }
@@ -30,7 +32,9 @@ public bool IsCompleted
30 32
         public void OnCompleted(Action continuation)
31 33
         {
32 34
             m_task.ContinueWith(
33  
-                delegate { continuation(); }, TaskScheduler);
  35
+                delegate { continuation(); },
  36
+                // I don't think continuing on the thread pool is what people really wanted when they called ConfigureAwait, but it's what the CTP did
  37
+                m_useCapturedContext ? TaskScheduler : TaskScheduler.Default);
34 38
         }
35 39
 
36 40
         public void GetResult()
@@ -49,10 +53,12 @@ public void GetResult()
49 53
     public struct TaskAwaiter<T> : INotifyCompletion
50 54
     {
51 55
         private readonly Task<T> m_task;
  56
+        private readonly bool m_useCapturedContext;
52 57
 
53  
-        internal TaskAwaiter(Task<T> task)
  58
+        public TaskAwaiter(Task<T> task, bool useCapturedContext = true)
54 59
         {
55 60
             m_task = task;
  61
+            m_useCapturedContext = useCapturedContext;
56 62
         }
57 63
 
58 64
         public bool IsCompleted
@@ -63,7 +69,9 @@ public bool IsCompleted
63 69
         public void OnCompleted(Action continuation)
64 70
         {
65 71
             m_task.ContinueWith(
66  
-                delegate { continuation(); }, TaskAwaiter.TaskScheduler);
  72
+                delegate { continuation(); },
  73
+                // I don't think continuing on the thread pool is what people really wanted when they called ConfigureAwait, but it's what the CTP did
  74
+                m_useCapturedContext ? TaskAwaiter.TaskScheduler : TaskScheduler.Default);
67 75
         }
68 76
 
69 77
         public T GetResult()
10  src/AsyncBridge/TaskUtils.cs
@@ -56,6 +56,16 @@ public static YieldAwaitable Yield()
56 56
             return new YieldAwaitable((object)SynchronizationContext.Current ?? TaskScheduler.Current);
57 57
         }
58 58
 
  59
+        public static ConfigurableTaskAwaitable<T> ConfigureAwait<T>(this Task<T> original, bool continueOnCapturedContext)
  60
+        {
  61
+            return new ConfigurableTaskAwaitable<T>(original, continueOnCapturedContext);
  62
+        }
  63
+
  64
+        public static ConfigurableTaskAwaitable ConfigureAwait(this Task original, bool continueOnCapturedContext)
  65
+        {
  66
+            return new ConfigurableTaskAwaitable(original, continueOnCapturedContext);
  67
+        }
  68
+
59 69
         // Methods which are implemented in terms of TaskFactory
60 70
         public static Task<T[]> WhenAll<T>(params Task<T>[] tasks)
61 71
         {
2  src/AsyncBridge/YieldAwaitable.cs
@@ -21,7 +21,7 @@ public YieldAwaiter GetAwaiter()
21 21
         }
22 22
 
23 23
         [StructLayout(LayoutKind.Sequential, Size = 1)]
24  
-        public struct YieldAwaiter : ICriticalNotifyCompletion
  24
+        public struct YieldAwaiter : ICriticalNotifyCompletion, INotifyCompletion
25 25
         {
26 26
             private static readonly WaitCallback s_waitCallbackRunAction = runAction;
27 27
             private static readonly SendOrPostCallback s_sendOrPostCallbackRunAction =
8  tests/AsyncBridge-Net35.Tests/AsyncBridge-Net35.Tests.csproj
@@ -43,7 +43,13 @@
43 43
     <Reference Include="System.Xml" />
44 44
   </ItemGroup>
45 45
   <ItemGroup>
46  
-    <Compile Include="..\AsyncBridge.Tests\*.cs" />
  46
+    <Compile Include="..\AsyncBridge.Tests\DelayTest.cs" />
  47
+    <Compile Include="..\AsyncBridge.Tests\SyncContextTests.cs" />
  48
+    <Compile Include="..\AsyncBridge.Tests\Test.cs" />
  49
+    <Compile Include="..\AsyncBridge.Tests\WhenAllTests.cs" />
  50
+    <Compile Include="..\AsyncBridge.Tests\WhenAnyTests.cs" />
  51
+      <Link>SyncContextTests.cs</Link>
  52
+    </Compile>
47 53
     <Compile Include="Properties\AssemblyInfo.cs" />
48 54
   </ItemGroup>
49 55
   <ItemGroup>
1  tests/AsyncBridge.Tests/AsyncBridge.Tests.csproj
@@ -43,6 +43,7 @@
43 43
   </ItemGroup>
44 44
   <ItemGroup>
45 45
     <Compile Include="DelayTest.cs" />
  46
+    <Compile Include="SyncContextTests.cs" />
46 47
     <Compile Include="Test.cs" />
47 48
     <Compile Include="WhenAllTests.cs" />
48 49
     <Compile Include="WhenAnyTests.cs" />
100  tests/AsyncBridge.Tests/SyncContextTests.cs
... ...
@@ -0,0 +1,100 @@
  1
+using System.Threading;
  2
+using System.Threading.Tasks;
  3
+using Microsoft.VisualStudio.TestTools.UnitTesting;
  4
+
  5
+namespace AsyncBridge.Tests
  6
+{
  7
+    [TestClass]
  8
+    public class SyncContextTests
  9
+    {
  10
+        class MagicSynchronizationContext : SynchronizationContext
  11
+        {
  12
+            public static readonly MagicSynchronizationContext Instance = new MagicSynchronizationContext();
  13
+
  14
+            public override void Post(SendOrPostCallback d, object state)
  15
+            {
  16
+                base.Post(o =>
  17
+                {
  18
+                    SetSynchronizationContext(this);
  19
+                    d(o);
  20
+                }, state);
  21
+            }
  22
+        }
  23
+
  24
+        [TestMethod]
  25
+        public async Task YieldSyncContext()
  26
+        {
  27
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  28
+            await TaskUtils.Yield();
  29
+            Assert.IsTrue(SynchronizationContext.Current is MagicSynchronizationContext);
  30
+        }
  31
+
  32
+        [TestMethod]
  33
+        public async Task FromResultSyncContext()
  34
+        {
  35
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  36
+            int r = await TaskUtils.FromResult(4);
  37
+            Assert.IsTrue(SynchronizationContext.Current is MagicSynchronizationContext);
  38
+            Assert.AreEqual(4, r);
  39
+        }
  40
+
  41
+        [TestMethod]
  42
+        public async Task DelaySyncContext()
  43
+        {
  44
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  45
+            await TaskUtils.Delay(1);
  46
+            Assert.IsTrue(SynchronizationContext.Current is MagicSynchronizationContext);
  47
+        }
  48
+
  49
+        [TestMethod]
  50
+        public async Task SimpleTaskSyncContext()
  51
+        {
  52
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  53
+            await WaitABit();
  54
+            Assert.IsTrue(SynchronizationContext.Current is MagicSynchronizationContext);
  55
+        }
  56
+
  57
+        [TestMethod]
  58
+        public async Task ReturningTaskSyncContext()
  59
+        {
  60
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  61
+            int r = await WaitAThing();
  62
+            Assert.IsTrue(SynchronizationContext.Current is MagicSynchronizationContext);
  63
+            Assert.AreEqual(6, r);
  64
+        }
  65
+
  66
+        [TestMethod]
  67
+        public async Task ConfiguredSimpleTaskSyncContext()
  68
+        {
  69
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  70
+            await WaitABit().ConfigureAwait(false);
  71
+            Assert.IsFalse(SynchronizationContext.Current is MagicSynchronizationContext);
  72
+        }
  73
+
  74
+        [TestMethod]
  75
+        public async Task ConfiguredReturningTaskSyncContext()
  76
+        {
  77
+            SynchronizationContext.SetSynchronizationContext(MagicSynchronizationContext.Instance);
  78
+            int r = await WaitAThing().ConfigureAwait(false);
  79
+            Assert.IsFalse(SynchronizationContext.Current is MagicSynchronizationContext);
  80
+            Assert.AreEqual(6, r);
  81
+        }
  82
+
  83
+        /// <summary>
  84
+        /// Exercise our AsyncTaskMethodBuilder
  85
+        /// </summary>
  86
+        private async Task WaitABit()
  87
+        {
  88
+            await TaskUtils.Delay(1);
  89
+        }
  90
+
  91
+        /// <summary>
  92
+        /// Exercise our AsyncTaskMethodBuilder'1
  93
+        /// </summary>
  94
+        private async Task<int> WaitAThing()
  95
+        {
  96
+            await TaskUtils.Delay(1);
  97
+            return await TaskUtils.FromResult(6);
  98
+        }
  99
+    }
  100
+}

0 notes on commit efaf223

Please sign in to comment.
Something went wrong with that request. Please try again.