Skip to content

Commit 6445062

Browse files
authored
Fixes sync context capture and multiple async continuation issues (#5044)
* Fixes sync context capture and multiple async continuation issues * Fix callback contract capturing Sync Context breaking test due to XUnit sync context * Fix some more test issues now we're capturing sync context * Fix test mistake where it was making 100 calls instead of 1 * Add client certificate requirement to new tests using client certificates
1 parent 6da4817 commit 6445062

File tree

10 files changed

+391
-18
lines changed

10 files changed

+391
-18
lines changed

global.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"runtimes": {
55
"dotnet": [
66
"3.1.5",
7-
"6.0.12"
7+
"6.0.15",
8+
"7.0.4"
89
]
910
}
1011
},

src/System.Private.ServiceModel/tests/Common/Scenarios/ScenarioTestTypes.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,20 @@ public SingleThreadSynchronizationContext(bool trackOperations)
185185
public override void Post(SendOrPostCallback d, object state)
186186
{
187187
if (d == null) throw new ArgumentNullException("d");
188-
_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
188+
if (ExecutionContext.IsFlowSuppressed())
189+
{
190+
_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
191+
}
192+
else
193+
{
194+
var ec = ExecutionContext.Capture();
195+
_queue.Add(new KeyValuePair<SendOrPostCallback, object>(RunOnCapturedState, (d, state, ec)));
196+
void RunOnCapturedState(object obj)
197+
{
198+
var s = ((SendOrPostCallback d, object state, ExecutionContext ec))obj;
199+
ExecutionContext.Run(s.ec, new ContextCallback(d), s.state);
200+
}
201+
}
189202
}
190203

191204
public override void Send(SendOrPostCallback d, object state)
@@ -218,6 +231,15 @@ public void RunOnCurrentThread()
218231
}
219232
}
220233

234+
public Task RunOnThreadPoolThread()
235+
{
236+
return Task.Run(() =>
237+
{
238+
SetSynchronizationContext(this);
239+
RunOnCurrentThread();
240+
});
241+
}
242+
221243
public void Complete()
222244
{
223245
_queue.CompleteAdding();
@@ -674,6 +696,7 @@ public string StringValue
674696
}
675697
}
676698

699+
[CallbackBehavior(UseSynchronizationContext = false)]
677700
public class WcfDuplexService_CallbackDebugBehavior_Callback : IWcfDuplexService_CallbackDebugBehavior_Callback
678701
{
679702
public void ReplyThrow(string input)
@@ -840,6 +863,7 @@ public MyDuplexClientBase(InstanceContext callbackInstance, Binding binding, End
840863
}
841864
}
842865

866+
[CallbackBehavior(UseSynchronizationContext = false)]
843867
public class WcfDuplexServiceCallback : IWcfDuplexServiceCallback, IWcfDuplexService_DataContract_Callback, IWcfDuplexService_Xml_Callback
844868
{
845869
private TaskCompletionSource<Guid> _tcs;

src/System.Private.ServiceModel/tests/Scenarios/Binding/Tcp/NetTcpBindingTests.4.0.0.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.ServiceModel;
6+
using System.Threading.Tasks;
67
using Infrastructure.Common;
78
using Xunit;
89

@@ -40,4 +41,42 @@ public static void SecurityModeNone_Echo_RoundTrips_String()
4041
ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
4142
}
4243
}
44+
45+
// Test for https://github.com/dotnet/wcf/issues/4946
46+
[WcfFact]
47+
[OuterLoop]
48+
public static async Task SecurityModeNone_Echo_RoundTrips_String_SyncAfterAsync()
49+
{
50+
string testString = "Hello";
51+
ChannelFactory<IWcfServiceGenerated> factory = null;
52+
IWcfServiceGenerated serviceProxy = null;
53+
54+
try
55+
{
56+
// *** SETUP *** \\
57+
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
58+
factory = new ChannelFactory<IWcfServiceGenerated>(binding, new EndpointAddress(Endpoints.Tcp_NoSecurity_Address));
59+
serviceProxy = factory.CreateChannel();
60+
61+
// *** EXECUTE *** \\
62+
string result = serviceProxy.Echo(testString);
63+
Assert.Equal(testString, result);
64+
// Without the ConfigureAwait, the xunit sync context will hop threads from the
65+
// completing thread to their sync context. ConfigureAwait means it continues
66+
// executing on the completing thread and triggers the bug (without the fix).
67+
result = await serviceProxy.EchoAsync(testString).ConfigureAwait(false);
68+
Assert.Equal(testString, result);
69+
result = serviceProxy.Echo(testString);
70+
Assert.Equal(testString, result);
71+
72+
// *** CLEANUP *** \\
73+
((ICommunicationObject)serviceProxy).Close();
74+
factory.Close();
75+
}
76+
finally
77+
{
78+
// *** ENSURE CLEANUP *** \\
79+
ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
80+
}
81+
}
4382
}

0 commit comments

Comments
 (0)