Improve behavior of TcpClient Dispose concurrent with ConnectAsync
authorStephen Toub <stoub@microsoft.com>
Mon, 11 Jul 2016 01:52:53 +0000 (21:52 -0400)
committerStephen Toub <stoub@microsoft.com>
Mon, 11 Jul 2016 11:23:45 +0000 (07:23 -0400)
commit19588745d1706f8717a3d670cf690fa29b0922a2
tree1e0a69abbc004152ea12ecb566941ea06b602bb1
parent2a3f730bfefe59113b4f0f3f7c0067365d236cb9
Improve behavior of TcpClient Dispose concurrent with ConnectAsync

TcpClient.Dispose is not meant to be used concurrently with other operations on the instance, but some code does do so as a way to provide a cancellation mechanism.  There are two easily hit issues with this:
1. On Unix, the ConnectAsync operation doesn't publish the actual Socket on which a connection was made until after the connection is established, as it needs to use temporary sockets to try each potential target address, and publishing it before connecting could end up publishing a Socket that won't end up being the actual one used.  As such, if a Dispose occurs during the ConnectAsync operation, it won't end up disposing the socket being used to make the connection, such that the connection won't be canceled.
2. On all platforms, Dispose nulls out the client socket field.  When the connection then subsequently completes, it hits a NullReferenceException while trying to dereference that field.

This commit addresses both issues:
a. When the client is disposed, on Unix we cancel a CancellationTokenSource, and each Socket we create is registered with that source to dispose the socket.  That way, we dispose of each socket even if it hasn't been published onto the instance yet.
b. We grab the Socket from the field and check for null prior to dereferencing it.

Commit migrated from https://github.com/dotnet/corefx/commit/e3e7ca7630c486f35bf7eac330dedcf729c74e71
src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.Unix.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.Windows.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs