The System.Net.* libs in dotnet/runtime inherited this from .NET Framework. To my knowledge it's not once helped flag any issues in dotnet/runtime, it's only built into debug builds, it's become very inconsistent as the code base has evolved, and it's just cluttering stuff up. So, goodbye.
~DebugCriticalHandleMinusOneIsInvalid()
{
- DebugThreadTracking.SetThreadSource(ThreadKinds.Finalization);
if (NetEventSource.IsEnabled) NetEventSource.Info(this, _trace);
}
}
~DebugCriticalHandleZeroOrMinusOneIsInvalid()
{
- DebugThreadTracking.SetThreadSource(ThreadKinds.Finalization);
if (NetEventSource.IsEnabled) NetEventSource.Info(this, _trace);
}
}
~DebugSafeHandleMinusOneIsInvalid()
{
- DebugThreadTracking.SetThreadSource(ThreadKinds.Finalization);
if (NetEventSource.IsEnabled) NetEventSource.Info(this, _trace);
}
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Collections.Generic;
-
-namespace System.Net
-{
- internal static class DebugThreadTracking
- {
- [ThreadStatic]
- private static Stack<ThreadKinds>? t_threadKindStack;
-
- private static Stack<ThreadKinds> ThreadKindStack => t_threadKindStack ?? (t_threadKindStack = new Stack<ThreadKinds>());
-
- internal static ThreadKinds CurrentThreadKind => ThreadKindStack.Count > 0 ? ThreadKindStack.Peek() : ThreadKinds.Other;
-
- internal static IDisposable? SetThreadKind(ThreadKinds kind)
- {
- if ((kind & ThreadKinds.SourceMask) != ThreadKinds.Unknown)
- {
- throw new InternalException(kind);
- }
-
- // Ignore during shutdown.
- if (Environment.HasShutdownStarted)
- {
- return null;
- }
-
- ThreadKinds threadKind = CurrentThreadKind;
- ThreadKinds source = threadKind & ThreadKinds.SourceMask;
-
- // Special warnings when doing dangerous things on a thread.
- if ((threadKind & ThreadKinds.User) != 0 && (kind & ThreadKinds.System) != 0)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, "Thread changed from User to System; user's thread shouldn't be hijacked.");
- }
-
- if ((threadKind & ThreadKinds.Async) != 0 && (kind & ThreadKinds.Sync) != 0)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, "Thread changed from Async to Sync, may block an Async thread.");
- }
- else if ((threadKind & (ThreadKinds.Other | ThreadKinds.CompletionPort)) == 0 && (kind & ThreadKinds.Sync) != 0)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, "Thread from a limited resource changed to Sync, may deadlock or bottleneck.");
- }
-
- ThreadKindStack.Push(
- (((kind & ThreadKinds.OwnerMask) == 0 ? threadKind : kind) & ThreadKinds.OwnerMask) |
- (((kind & ThreadKinds.SyncMask) == 0 ? threadKind : kind) & ThreadKinds.SyncMask) |
- (kind & ~(ThreadKinds.OwnerMask | ThreadKinds.SyncMask)) |
- source);
-
- if (CurrentThreadKind != threadKind)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Thread becomes:({CurrentThreadKind})");
- }
-
- return new ThreadKindFrame();
- }
-
- private class ThreadKindFrame : IDisposable
- {
- private readonly int _frameNumber;
-
- internal ThreadKindFrame()
- {
- _frameNumber = ThreadKindStack.Count;
- }
-
- void IDisposable.Dispose()
- {
- // Ignore during shutdown.
- if (Environment.HasShutdownStarted)
- {
- return;
- }
-
- if (_frameNumber != ThreadKindStack.Count)
- {
- throw new InternalException(_frameNumber);
- }
-
- ThreadKinds previous = ThreadKindStack.Pop();
-
- if (CurrentThreadKind != previous && NetEventSource.IsEnabled)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Thread reverts:({CurrentThreadKind})");
- }
- }
- }
-
- internal static void SetThreadSource(ThreadKinds source)
- {
- if ((source & ThreadKinds.SourceMask) != source || source == ThreadKinds.Unknown)
- {
- throw new ArgumentException("Must specify the thread source.", nameof(source));
- }
-
- if (ThreadKindStack.Count == 0)
- {
- ThreadKindStack.Push(source);
- return;
- }
-
- if (ThreadKindStack.Count > 1)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, "SetThreadSource must be called at the base of the stack, or the stack has been corrupted.");
- while (ThreadKindStack.Count > 1)
- {
- ThreadKindStack.Pop();
- }
- }
-
- if (ThreadKindStack.Peek() != source)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, "The stack has been corrupted.");
- ThreadKinds last = ThreadKindStack.Pop() & ThreadKinds.SourceMask;
- if (last != source && last != ThreadKinds.Other && NetEventSource.IsEnabled)
- {
- NetEventSource.Fail(null, $"Thread source changed.|Was:({last}) Now:({source})");
- }
- ThreadKindStack.Push(source);
- }
- }
- }
-
- [Flags]
- internal enum ThreadKinds
- {
- Unknown = 0x0000,
-
- // Mutually exclusive.
- User = 0x0001, // Thread has entered via an API.
- System = 0x0002, // Thread has entered via a system callback (e.g. completion port) or is our own thread.
-
- // Mutually exclusive.
- Sync = 0x0004, // Thread should block.
- Async = 0x0008, // Thread should not block.
-
- // Mutually exclusive, not always known for a user thread. Never changes.
- Timer = 0x0010, // Thread is the timer thread. (Can't call user code.)
- CompletionPort = 0x0020, // Thread is a ThreadPool completion-port thread.
- Worker = 0x0040, // Thread is a ThreadPool worker thread.
- Finalization = 0x0080, // Thread is the finalization thread.
- Other = 0x0100, // Unknown source.
-
- OwnerMask = User | System,
- SyncMask = Sync | Async,
- SourceMask = Timer | CompletionPort | Worker | Finalization | Other,
-
- // Useful "macros"
- SafeSources = SourceMask & ~(Timer | Finalization), // Methods that "unsafe" sources can call must be explicitly marked.
- ThreadPool = CompletionPort | Worker, // Like Thread.CurrentThread.IsThreadPoolThread
- }
-}
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<AssemblyName>System.Net.Http</AssemblyName>
Link="Common\System\Net\NegotiationInfoClass.cs" />
<Compile Include="$(CommonPath)System\Net\InternalException.cs"
Link="Common\System\Net\InternalException.cs" />
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\DebugSafeHandle.cs"
Link="Common\System\Net\DebugSafeHandle.cs" />
<Compile Include="$(CommonPath)System\Net\DebugCriticalHandleMinusOneIsInvalid.cs"
Link="Common\System\Collections\Generic\BidirectionalDictionary.cs" />
<Compile Include="$(CommonPath)System\NotImplemented.cs"
Link="Common\System\NotImplemented.cs" />
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
Link="Common\System\Net\ContextFlagsPal.cs" />
<Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
Link="Common\System\Collections\Generic\BidirectionalDictionary.cs" />
<Compile Include="$(CommonPath)System\NotImplemented.cs"
Link="Common\System\NotImplemented.cs" />
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
Link="Common\System\Net\ContextFlagsPal.cs" />
<Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
<ItemGroup Condition=" '$(TargetsWindows)' == 'true'">
<Reference Include="System.Memory" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
Link="Common\System\Collections\Generic\BidirectionalDictionary.cs" />
<Compile Include="$(CommonPath)System\NotImplemented.cs"
Link="Common\System\NotImplemented.cs" />
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
Link="Common\System\Net\ContextFlagsPal.cs" />
<Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
<Compile Include="$(CommonPath)Interop\Windows\SspiCli\SSPIWrapper.cs"
Link="Common\Interop\Windows\SspiCli\SSPIWrapper.cs" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
<Compile Include="System\Net\IPHostEntry.cs" />
<Compile Include="System\Net\NetEventSource.NameResolution.cs" />
<!-- Logging -->
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Net\InternalException.cs"
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.Net
-{
- public static class DebugThreadTracking
- {
- internal static void SetThreadSource(ThreadKinds source)
- {
- }
- }
-
- [Flags]
- internal enum ThreadKinds
- {
- Unknown = 0x0000,
-
- // Mutually exclusive.
- User = 0x0001, // Thread has entered via an API.
- System = 0x0002, // Thread has entered via a system callback (e.g. completion port) or is our own thread.
-
- // Mutually exclusive.
- Sync = 0x0004, // Thread should block.
- Async = 0x0008, // Thread should not block.
-
- // Mutually exclusive, not always known for a user thread. Never changes.
- Timer = 0x0010, // Thread is the timer thread. (Can't call user code.)
- CompletionPort = 0x0020, // Thread is a ThreadPool completion-port thread.
- Worker = 0x0040, // Thread is a ThreadPool worker thread.
- Finalization = 0x0080, // Thread is the finalization thread.
- Other = 0x0100, // Unknown source.
-
- OwnerMask = User | System,
- SyncMask = Sync | Async,
- SourceMask = Timer | CompletionPort | Worker | Finalization | Other,
-
- // Useful "macros"
- SafeSources = SourceMask & ~(Timer | Finalization), // Methods that "unsafe" sources can call must be explicitly marked.
- ThreadPool = CompletionPort | Worker, // Like Thread.CurrentThread.IsThreadPoolThread
- }
-}
Link="ProductionCode\System\Net\IPHostEntry.cs" />
<Compile Include="Fakes\FakeContextAwareResult.cs" />
<Compile Include="NameResolutionPalTests.cs" />
- <Compile Include="Fakes\DebugThreadTracking.cs" />
<Compile Include="Fakes\DnsFake.cs" />
<Compile Include="Fakes\IPAddressFakeExtensions.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Link="Common\System\Net\InternalException.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\HttpDateParser.cs"
Link="Common\System\Net\HttpDateParser.cs" />
<Compile Include="$(CommonPath)System\Net\HttpKnownHeaderNames.cs"
}
set
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ bool fChunked;
+ //
+ // on blank string, remove current header
+ //
+ if (string.IsNullOrWhiteSpace(value))
{
-#endif
- bool fChunked;
//
- // on blank string, remove current header
+ // if the value is blank, then remove the header
//
- if (string.IsNullOrWhiteSpace(value))
- {
- //
- // if the value is blank, then remove the header
- //
- _webHeaderCollection.Remove(HttpKnownHeaderNames.TransferEncoding);
- return;
- }
+ _webHeaderCollection.Remove(HttpKnownHeaderNames.TransferEncoding);
+ return;
+ }
- //
- // if not check if the user is trying to set chunked:
- //
- fChunked = (value.IndexOf(ChunkedHeader, StringComparison.OrdinalIgnoreCase) != -1);
+ //
+ // if not check if the user is trying to set chunked:
+ //
+ fChunked = (value.IndexOf(ChunkedHeader, StringComparison.OrdinalIgnoreCase) != -1);
- //
- // prevent them from adding chunked, or from adding an Encoding without
- // turning on chunked, the reason is due to the HTTP Spec which prevents
- // additional encoding types from being used without chunked
- //
- if (fChunked)
- {
- throw new ArgumentException(SR.net_nochunked, nameof(value));
- }
- else if (!SendChunked)
- {
- throw new InvalidOperationException(SR.net_needchunked);
- }
- else
- {
- string checkedValue = HttpValidationHelpers.CheckBadHeaderValueChars(value);
- _webHeaderCollection[HttpKnownHeaderNames.TransferEncoding] = checkedValue;
- }
-#if DEBUG
+ //
+ // prevent them from adding chunked, or from adding an Encoding without
+ // turning on chunked, the reason is due to the HTTP Spec which prevents
+ // additional encoding types from being used without chunked
+ //
+ if (fChunked)
+ {
+ throw new ArgumentException(SR.net_nochunked, nameof(value));
+ }
+ else if (!SendChunked)
+ {
+ throw new InvalidOperationException(SR.net_needchunked);
+ }
+ else
+ {
+ string checkedValue = HttpValidationHelpers.CheckBadHeaderValueChars(value);
+ _webHeaderCollection[HttpKnownHeaderNames.TransferEncoding] = checkedValue;
}
-#endif
}
}
}
set
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- bool fKeepAlive;
- bool fClose;
+ bool fKeepAlive;
+ bool fClose;
- //
- // on blank string, remove current header
- //
- if (string.IsNullOrWhiteSpace(value))
- {
- _webHeaderCollection.Remove(HttpKnownHeaderNames.Connection);
- return;
- }
+ //
+ // on blank string, remove current header
+ //
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ _webHeaderCollection.Remove(HttpKnownHeaderNames.Connection);
+ return;
+ }
- fKeepAlive = (value.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) != -1);
- fClose = (value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1);
+ fKeepAlive = (value.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) != -1);
+ fClose = (value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1);
- //
- // Prevent keep-alive and close from being added
- //
+ //
+ // Prevent keep-alive and close from being added
+ //
- if (fKeepAlive ||
- fClose)
- {
- throw new ArgumentException(SR.net_connarg, nameof(value));
- }
- else
- {
- string checkedValue = HttpValidationHelpers.CheckBadHeaderValueChars(value);
- _webHeaderCollection[HttpKnownHeaderNames.Connection] = checkedValue;
- }
-#if DEBUG
+ if (fKeepAlive ||
+ fClose)
+ {
+ throw new ArgumentException(SR.net_connarg, nameof(value));
+ }
+ else
+ {
+ string checkedValue = HttpValidationHelpers.CheckBadHeaderValueChars(value);
+ _webHeaderCollection[HttpKnownHeaderNames.Connection] = checkedValue;
}
-#endif
}
}
}
set
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- // only remove everything other than 100-cont
- bool fContinue100;
+ // only remove everything other than 100-cont
+ bool fContinue100;
- //
- // on blank string, remove current header
- //
+ //
+ // on blank string, remove current header
+ //
- if (string.IsNullOrWhiteSpace(value))
- {
- _webHeaderCollection.Remove(HttpKnownHeaderNames.Expect);
- return;
- }
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ _webHeaderCollection.Remove(HttpKnownHeaderNames.Expect);
+ return;
+ }
- //
- // Prevent 100-continues from being added
- //
+ //
+ // Prevent 100-continues from being added
+ //
- fContinue100 = (value.IndexOf(ContinueHeader, StringComparison.OrdinalIgnoreCase) != -1);
+ fContinue100 = (value.IndexOf(ContinueHeader, StringComparison.OrdinalIgnoreCase) != -1);
- if (fContinue100)
- {
- throw new ArgumentException(SR.net_no100, nameof(value));
- }
- else
- {
- string checkedValue = HttpValidationHelpers.CheckBadHeaderValueChars(value);
- _webHeaderCollection[HttpKnownHeaderNames.Expect] = checkedValue;
- }
-#if DEBUG
+ if (fContinue100)
+ {
+ throw new ArgumentException(SR.net_no100, nameof(value));
+ }
+ else
+ {
+ string checkedValue = HttpValidationHelpers.CheckBadHeaderValueChars(value);
+ _webHeaderCollection[HttpKnownHeaderNames.Expect] = checkedValue;
}
-#endif
}
}
private DateTime GetDateHeaderHelper(string headerName)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- string? headerValue = _webHeaderCollection[headerName];
+ string? headerValue = _webHeaderCollection[headerName];
- if (headerValue == null)
- {
- return DateTime.MinValue; // MinValue means header is not present
- }
- if (HttpDateParser.TryParse(headerValue, out DateTimeOffset dateTimeOffset))
- {
- return dateTimeOffset.LocalDateTime;
- }
- else
- {
- throw new ProtocolViolationException(SR.net_baddate);
- }
-#if DEBUG
+ if (headerValue == null)
+ {
+ return DateTime.MinValue; // MinValue means header is not present
+ }
+ if (HttpDateParser.TryParse(headerValue, out DateTimeOffset dateTimeOffset))
+ {
+ return dateTimeOffset.LocalDateTime;
+ }
+ else
+ {
+ throw new ProtocolViolationException(SR.net_baddate);
}
-#endif
}
private void SetDateHeaderHelper(string headerName, DateTime dateTime)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- if (dateTime == DateTime.MinValue)
- SetSpecialHeaders(headerName, null); // remove header
- else
- SetSpecialHeaders(headerName, HttpDateParser.DateToString(dateTime.ToUniversalTime()));
-#if DEBUG
- }
-#endif
+ if (dateTime == DateTime.MinValue)
+ SetSpecialHeaders(headerName, null); // remove header
+ else
+ SetSpecialHeaders(headerName, HttpDateParser.DateToString(dateTime.ToUniversalTime()));
}
private bool TryGetHostUri(string hostName, [NotNullWhen(true)] out Uri? hostUri)
private static void ThreadProc()
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(null);
-#if DEBUG
- DebugThreadTracking.SetThreadSource(ThreadKinds.Timer);
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.System | ThreadKinds.Async))
+
+ // Set this thread as a background thread. On AppDomain/Process shutdown, the thread will just be killed.
+ Thread.CurrentThread.IsBackground = true;
+
+ // Keep a permanent lock on s_Queues. This lets for example Shutdown() know when this thread isn't running.
+ lock (s_queues)
{
-#endif
- // Set this thread as a background thread. On AppDomain/Process shutdown, the thread will just be killed.
- Thread.CurrentThread.IsBackground = true;
+ // If shutdown was recently called, abort here.
+ if (Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Running, (int)TimerThreadState.Running) !=
+ (int)TimerThreadState.Running)
+ {
+ return;
+ }
- // Keep a permanent lock on s_Queues. This lets for example Shutdown() know when this thread isn't running.
- lock (s_queues)
+ bool running = true;
+ while (running)
{
- // If shutdown was recently called, abort here.
- if (Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Running, (int)TimerThreadState.Running) !=
- (int)TimerThreadState.Running)
+ try
{
- return;
- }
+ s_threadReadyEvent.Reset();
- bool running = true;
- while (running)
- {
- try
+ while (true)
{
- s_threadReadyEvent.Reset();
-
- while (true)
+ // Copy all the new queues to the real queues. Since only this thread modifies the real queues, it doesn't have to lock it.
+ if (s_newQueues.Count > 0)
{
- // Copy all the new queues to the real queues. Since only this thread modifies the real queues, it doesn't have to lock it.
- if (s_newQueues.Count > 0)
+ lock (s_newQueues)
{
- lock (s_newQueues)
+ for (LinkedListNode<WeakReference>? node = s_newQueues.First; node != null; node = s_newQueues.First)
{
- for (LinkedListNode<WeakReference>? node = s_newQueues.First; node != null; node = s_newQueues.First)
- {
- s_newQueues.Remove(node);
- s_queues.AddLast(node);
- }
+ s_newQueues.Remove(node);
+ s_queues.AddLast(node);
}
}
+ }
- int now = Environment.TickCount;
- int nextTick = 0;
- bool haveNextTick = false;
- for (LinkedListNode<WeakReference>? node = s_queues.First; node != null; /* node = node.Next must be done in the body */)
+ int now = Environment.TickCount;
+ int nextTick = 0;
+ bool haveNextTick = false;
+ for (LinkedListNode<WeakReference>? node = s_queues.First; node != null; /* node = node.Next must be done in the body */)
+ {
+ TimerQueue? queue = (TimerQueue?)node.Value.Target;
+ if (queue == null)
{
- TimerQueue? queue = (TimerQueue?)node.Value.Target;
- if (queue == null)
- {
- LinkedListNode<WeakReference>? next = node.Next;
- s_queues.Remove(node);
- node = next;
- continue;
- }
-
- // Fire() will always return values that should be interpreted as later than 'now' (that is, even if 'now' is
- // returned, it is 0x100000000 milliseconds in the future). There's also a chance that Fire() will return a value
- // intended as > 0x100000000 milliseconds from 'now'. Either case will just cause an extra scan through the timers.
- int nextTickInstance;
- if (queue.Fire(out nextTickInstance) && (!haveNextTick || IsTickBetween(now, nextTick, nextTickInstance)))
- {
- nextTick = nextTickInstance;
- haveNextTick = true;
- }
+ LinkedListNode<WeakReference>? next = node.Next;
+ s_queues.Remove(node);
+ node = next;
+ continue;
+ }
- node = node.Next;
+ // Fire() will always return values that should be interpreted as later than 'now' (that is, even if 'now' is
+ // returned, it is 0x100000000 milliseconds in the future). There's also a chance that Fire() will return a value
+ // intended as > 0x100000000 milliseconds from 'now'. Either case will just cause an extra scan through the timers.
+ int nextTickInstance;
+ if (queue.Fire(out nextTickInstance) && (!haveNextTick || IsTickBetween(now, nextTick, nextTickInstance)))
+ {
+ nextTick = nextTickInstance;
+ haveNextTick = true;
}
- // Figure out how long to wait, taking into account how long the loop took.
- // Add 15 ms to compensate for poor TickCount resolution (want to guarantee a firing).
- int newNow = Environment.TickCount;
- int waitDuration = haveNextTick ?
- (int)(IsTickBetween(now, nextTick, newNow) ?
- Math.Min(unchecked((uint)(nextTick - newNow)), (uint)(int.MaxValue - TickCountResolution)) + TickCountResolution :
- 0) :
- ThreadIdleTimeoutMilliseconds;
+ node = node.Next;
+ }
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Waiting for {waitDuration}ms");
+ // Figure out how long to wait, taking into account how long the loop took.
+ // Add 15 ms to compensate for poor TickCount resolution (want to guarantee a firing).
+ int newNow = Environment.TickCount;
+ int waitDuration = haveNextTick ?
+ (int)(IsTickBetween(now, nextTick, newNow) ?
+ Math.Min(unchecked((uint)(nextTick - newNow)), (uint)(int.MaxValue - TickCountResolution)) + TickCountResolution :
+ 0) :
+ ThreadIdleTimeoutMilliseconds;
- int waitResult = WaitHandle.WaitAny(s_threadEvents, waitDuration, false);
+ if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Waiting for {waitDuration}ms");
- // 0 is s_ThreadShutdownEvent - die.
- if (waitResult == 0)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Awoke, cause: Shutdown");
- running = false;
- break;
- }
+ int waitResult = WaitHandle.WaitAny(s_threadEvents, waitDuration, false);
+
+ // 0 is s_ThreadShutdownEvent - die.
+ if (waitResult == 0)
+ {
+ if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Awoke, cause: Shutdown");
+ running = false;
+ break;
+ }
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Awoke, cause {(waitResult == WaitHandle.WaitTimeout ? "Timeout" : "Prod")}");
+ if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Awoke, cause {(waitResult == WaitHandle.WaitTimeout ? "Timeout" : "Prod")}");
- // If we timed out with nothing to do, shut down.
- if (waitResult == WaitHandle.WaitTimeout && !haveNextTick)
+ // If we timed out with nothing to do, shut down.
+ if (waitResult == WaitHandle.WaitTimeout && !haveNextTick)
+ {
+ Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Idle, (int)TimerThreadState.Running);
+ // There could have been one more prod between the wait and the exchange. Check, and abort if necessary.
+ if (s_threadReadyEvent.WaitOne(0, false))
{
- Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Idle, (int)TimerThreadState.Running);
- // There could have been one more prod between the wait and the exchange. Check, and abort if necessary.
- if (s_threadReadyEvent.WaitOne(0, false))
+ if (Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Running, (int)TimerThreadState.Idle) ==
+ (int)TimerThreadState.Idle)
{
- if (Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Running, (int)TimerThreadState.Idle) ==
- (int)TimerThreadState.Idle)
- {
- continue;
- }
+ continue;
}
-
- running = false;
- break;
}
+
+ running = false;
+ break;
}
}
- catch (Exception exception)
- {
- if (ExceptionCheck.IsFatal(exception))
- throw;
+ }
+ catch (Exception exception)
+ {
+ if (ExceptionCheck.IsFatal(exception))
+ throw;
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, exception);
+ if (NetEventSource.IsEnabled) NetEventSource.Error(null, exception);
- // The only options are to continue processing and likely enter an error-loop,
- // shut down timers for this AppDomain, or shut down the AppDomain. Go with shutting
- // down the AppDomain in debug, and going into a loop in retail, but try to make the
- // loop somewhat slow. Note that in retail, this can only be triggered by OutOfMemory or StackOverflow,
- // or an exception thrown within TimerThread - the rest are caught in Fire().
+ // The only options are to continue processing and likely enter an error-loop,
+ // shut down timers for this AppDomain, or shut down the AppDomain. Go with shutting
+ // down the AppDomain in debug, and going into a loop in retail, but try to make the
+ // loop somewhat slow. Note that in retail, this can only be triggered by OutOfMemory or StackOverflow,
+ // or an exception thrown within TimerThread - the rest are caught in Fire().
#if !DEBUG
- Thread.Sleep(1000);
+ Thread.Sleep(1000);
#else
- throw;
+ throw;
#endif
- }
}
}
-
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Stop");
-#if DEBUG
}
-#endif
+
+ if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Stop");
}
/// <summary>
<Compile Include="System\Security\Authentication\ExtendedProtection\ProtectionScenario.cs" />
<Compile Include="System\Security\Authentication\ExtendedProtection\ServiceNameCollection.cs" />
<!-- Logging -->
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Net\InternalException.cs"
protected override void Dispose(bool disposing)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
+ try
{
-#endif
- try
+ if (disposing)
{
- if (disposing)
+ if (_leaveStreamOpen)
{
- if (_leaveStreamOpen)
- {
- _innerStream.Flush();
- }
- else
- {
- _innerStream.Dispose();
- }
+ _innerStream.Flush();
+ }
+ else
+ {
+ _innerStream.Dispose();
}
}
- finally
- {
- base.Dispose(disposing);
- }
-#if DEBUG
}
-#endif
+ finally
+ {
+ base.Dispose(disposing);
+ }
}
public override ValueTask DisposeAsync()
public NegotiateStream(Stream innerStream, bool leaveInnerStreamOpen) : base(innerStream, leaveInnerStreamOpen)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- _negoState = new NegoState(innerStream);
- _package = NegoState.DefaultPackage;
- InitializeStreamPart();
-#if DEBUG
- }
-#endif
+ _negoState = new NegoState(innerStream);
+ _package = NegoState.DefaultPackage;
+ InitializeStreamPart();
}
public virtual IAsyncResult BeginAuthenticateAsClient(AsyncCallback? asyncCallback, object? asyncState)
AsyncCallback? asyncCallback,
object? asyncState)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- _negoState.ValidateCreateContext(_package, false, credential, targetName, binding, requiredProtectionLevel, allowedImpersonationLevel);
+ _negoState.ValidateCreateContext(_package, false, credential, targetName, binding, requiredProtectionLevel, allowedImpersonationLevel);
- LazyAsyncResult result = new LazyAsyncResult(_negoState, asyncState, asyncCallback);
- _negoState.ProcessAuthentication(result);
+ LazyAsyncResult result = new LazyAsyncResult(_negoState, asyncState, asyncCallback);
+ _negoState.ProcessAuthentication(result);
- return result;
-#if DEBUG
- }
-#endif
+ return result;
}
public virtual void EndAuthenticateAsClient(IAsyncResult asyncResult)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- _negoState.EndProcessAuthentication(asyncResult);
-#if DEBUG
- }
-#endif
+ _negoState.EndProcessAuthentication(asyncResult);
}
public virtual void AuthenticateAsServer()
public virtual void AuthenticateAsServer(NetworkCredential credential, ExtendedProtectionPolicy? policy, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel requiredImpersonationLevel)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
- {
-#endif
- _negoState.ValidateCreateContext(_package, credential, string.Empty, policy, requiredProtectionLevel, requiredImpersonationLevel);
- _negoState.ProcessAuthentication(null);
-#if DEBUG
- }
-#endif
+ _negoState.ValidateCreateContext(_package, credential, string.Empty, policy, requiredProtectionLevel, requiredImpersonationLevel);
+ _negoState.ProcessAuthentication(null);
}
public virtual IAsyncResult BeginAuthenticateAsServer(AsyncCallback? asyncCallback, object? asyncState)
AsyncCallback? asyncCallback,
object? asyncState)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- _negoState.ValidateCreateContext(_package, credential, string.Empty, policy, requiredProtectionLevel, requiredImpersonationLevel);
+ _negoState.ValidateCreateContext(_package, credential, string.Empty, policy, requiredProtectionLevel, requiredImpersonationLevel);
- LazyAsyncResult result = new LazyAsyncResult(_negoState, asyncState, asyncCallback);
- _negoState.ProcessAuthentication(result);
+ LazyAsyncResult result = new LazyAsyncResult(_negoState, asyncState, asyncCallback);
+ _negoState.ProcessAuthentication(result);
- return result;
-#if DEBUG
- }
-#endif
+ return result;
}
//
public virtual void EndAuthenticateAsServer(IAsyncResult asyncResult)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- _negoState.EndProcessAuthentication(asyncResult);
-#if DEBUG
- }
-#endif
+ _negoState.EndProcessAuthentication(asyncResult);
}
public virtual void AuthenticateAsClient()
public virtual void AuthenticateAsClient(
NetworkCredential credential, ChannelBinding? binding, string targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
- {
-#endif
- _negoState.ValidateCreateContext(_package, false, credential, targetName, binding, requiredProtectionLevel, allowedImpersonationLevel);
- _negoState.ProcessAuthentication(null);
-#if DEBUG
- }
-#endif
+ _negoState.ValidateCreateContext(_package, false, credential, targetName, binding, requiredProtectionLevel, allowedImpersonationLevel);
+ _negoState.ProcessAuthentication(null);
}
public virtual Task AuthenticateAsClientAsync()
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- return _negoState.IsAuthenticated;
-#if DEBUG
- }
-#endif
+ return _negoState.IsAuthenticated;
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- return _negoState.IsMutuallyAuthenticated;
-#if DEBUG
- }
-#endif
+ return _negoState.IsMutuallyAuthenticated;
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- return _negoState.IsEncrypted;
-#if DEBUG
- }
-#endif
+ return _negoState.IsEncrypted;
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- return _negoState.IsSigned;
-#if DEBUG
- }
-#endif
+ return _negoState.IsSigned;
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- return _negoState.IsServer;
-#if DEBUG
- }
-#endif
+ return _negoState.IsServer;
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- return _negoState.AllowedImpersonation;
-#if DEBUG
- }
-#endif
+ return _negoState.AllowedImpersonation;
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ if (_remoteIdentity == null)
{
-#endif
-
- if (_remoteIdentity == null)
- {
- _remoteIdentity = _negoState.GetIdentity();
- }
-
- return _remoteIdentity;
-#if DEBUG
+ _remoteIdentity = _negoState.GetIdentity();
}
-#endif
+
+ return _remoteIdentity;
}
}
public override void Flush()
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
- {
-#endif
- InnerStream.Flush();
-#if DEBUG
- }
-#endif
+ InnerStream.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
protected override void Dispose(bool disposing)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
+ try
{
-#endif
- try
- {
- _negoState.Close();
- }
- finally
- {
- base.Dispose(disposing);
- }
-#if DEBUG
+ _negoState.Close();
+ }
+ finally
+ {
+ base.Dispose(disposing);
}
-#endif
}
public override async ValueTask DisposeAsync()
public override int Read(byte[] buffer, int offset, int count)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
- {
-#endif
- _negoState.CheckThrow(true);
+ _negoState.CheckThrow(true);
- if (!_negoState.CanGetSecureStream)
- {
- return InnerStream.Read(buffer, offset, count);
- }
-
- return ProcessRead(buffer, offset, count, null);
-#if DEBUG
+ if (!_negoState.CanGetSecureStream)
+ {
+ return InnerStream.Read(buffer, offset, count);
}
-#endif
+
+ return ProcessRead(buffer, offset, count, null);
}
public override void Write(byte[] buffer, int offset, int count)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
- {
-#endif
- _negoState.CheckThrow(true);
+ _negoState.CheckThrow(true);
- if (!_negoState.CanGetSecureStream)
- {
- InnerStream.Write(buffer, offset, count);
- return;
- }
-
- ProcessWrite(buffer, offset, count, null);
-#if DEBUG
+ if (!_negoState.CanGetSecureStream)
+ {
+ InnerStream.Write(buffer, offset, count);
+ return;
}
-#endif
+
+ ProcessWrite(buffer, offset, count, null);
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, object? asyncState)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- _negoState.CheckThrow(true);
-
- if (!_negoState.CanGetSecureStream)
- {
- return TaskToApm.Begin(InnerStream.ReadAsync(buffer, offset, count), asyncCallback, asyncState);
- }
+ _negoState.CheckThrow(true);
- BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback);
- AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult);
- ProcessRead(buffer, offset, count, asyncRequest);
- return bufferResult;
-#if DEBUG
+ if (!_negoState.CanGetSecureStream)
+ {
+ return TaskToApm.Begin(InnerStream.ReadAsync(buffer, offset, count), asyncCallback, asyncState);
}
-#endif
+
+ BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback);
+ AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult);
+ ProcessRead(buffer, offset, count, asyncRequest);
+ return bufferResult;
}
public override int EndRead(IAsyncResult asyncResult)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- _negoState.CheckThrow(true);
-
- if (!_negoState.CanGetSecureStream)
- {
- return TaskToApm.End<int>(asyncResult);
- }
+ _negoState.CheckThrow(true);
+ if (!_negoState.CanGetSecureStream)
+ {
+ return TaskToApm.End<int>(asyncResult);
+ }
- if (asyncResult == null)
- {
- throw new ArgumentNullException(nameof(asyncResult));
- }
+ if (asyncResult == null)
+ {
+ throw new ArgumentNullException(nameof(asyncResult));
+ }
- BufferAsyncResult? bufferResult = asyncResult as BufferAsyncResult;
- if (bufferResult == null)
- {
- throw new ArgumentException(SR.Format(SR.net_io_async_result, asyncResult.GetType().FullName), nameof(asyncResult));
- }
+ BufferAsyncResult? bufferResult = asyncResult as BufferAsyncResult;
+ if (bufferResult == null)
+ {
+ throw new ArgumentException(SR.Format(SR.net_io_async_result, asyncResult.GetType().FullName), nameof(asyncResult));
+ }
- if (Interlocked.Exchange(ref _NestedRead, 0) == 0)
- {
- throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndRead"));
- }
+ if (Interlocked.Exchange(ref _NestedRead, 0) == 0)
+ {
+ throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndRead"));
+ }
- // No "artificial" timeouts implemented so far, InnerStream controls timeout.
- bufferResult.InternalWaitForCompletion();
+ // No "artificial" timeouts implemented so far, InnerStream controls timeout.
+ bufferResult.InternalWaitForCompletion();
- if (bufferResult.Result is Exception e)
+ if (bufferResult.Result is Exception e)
+ {
+ if (e is IOException)
{
- if (e is IOException)
- {
- ExceptionDispatchInfo.Throw(e);
- }
-
- throw new IOException(SR.net_io_read, e);
+ ExceptionDispatchInfo.Throw(e);
}
- return bufferResult.Int32Result;
-#if DEBUG
+ throw new IOException(SR.net_io_read, e);
}
-#endif
+
+ return bufferResult.Int32Result;
}
- //
- //
+
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, object? asyncState)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- _negoState.CheckThrow(true);
+ _negoState.CheckThrow(true);
- if (!_negoState.CanGetSecureStream)
- {
- return TaskToApm.Begin(InnerStream.WriteAsync(buffer, offset, count), asyncCallback, asyncState);
- }
+ if (!_negoState.CanGetSecureStream)
+ {
+ return TaskToApm.Begin(InnerStream.WriteAsync(buffer, offset, count), asyncCallback, asyncState);
+ }
- BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback);
- AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult);
+ BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback);
+ AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult);
- ProcessWrite(buffer, offset, count, asyncRequest);
- return bufferResult;
-#if DEBUG
- }
-#endif
+ ProcessWrite(buffer, offset, count, asyncRequest);
+ return bufferResult;
}
public override void EndWrite(IAsyncResult asyncResult)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- _negoState.CheckThrow(true);
+ _negoState.CheckThrow(true);
- if (!_negoState.CanGetSecureStream)
- {
- TaskToApm.End(asyncResult);
- return;
- }
+ if (!_negoState.CanGetSecureStream)
+ {
+ TaskToApm.End(asyncResult);
+ return;
+ }
- if (asyncResult == null)
- {
- throw new ArgumentNullException(nameof(asyncResult));
- }
+ if (asyncResult == null)
+ {
+ throw new ArgumentNullException(nameof(asyncResult));
+ }
- BufferAsyncResult? bufferResult = asyncResult as BufferAsyncResult;
- if (bufferResult == null)
- {
- throw new ArgumentException(SR.Format(SR.net_io_async_result, asyncResult.GetType().FullName), nameof(asyncResult));
- }
+ BufferAsyncResult? bufferResult = asyncResult as BufferAsyncResult;
+ if (bufferResult == null)
+ {
+ throw new ArgumentException(SR.Format(SR.net_io_async_result, asyncResult.GetType().FullName), nameof(asyncResult));
+ }
- if (Interlocked.Exchange(ref _NestedWrite, 0) == 0)
- {
- throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndWrite"));
- }
+ if (Interlocked.Exchange(ref _NestedWrite, 0) == 0)
+ {
+ throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndWrite"));
+ }
- // No "artificial" timeouts implemented so far, InnerStream controls timeout.
- bufferResult.InternalWaitForCompletion();
+ // No "artificial" timeouts implemented so far, InnerStream controls timeout.
+ bufferResult.InternalWaitForCompletion();
- if (bufferResult.Result is Exception e)
+ if (bufferResult.Result is Exception e)
+ {
+ if (e is IOException)
{
- if (e is IOException)
- {
- ExceptionDispatchInfo.Throw(e);
- }
-
- throw new IOException(SR.net_io_write, e);
+ ExceptionDispatchInfo.Throw(e);
}
-#if DEBUG
+
+ throw new IOException(SR.net_io_write, e);
}
-#endif
}
}
}
<Compile Include="$(CommonPath)System\IO\StreamHelpers.CopyValidation.cs"
Link="Common\System\IO\StreamHelpers.CopyValidation.cs" />
<!-- Logging -->
- <Compile Include="$(CommonPath)System\Net\Logging\DebugThreadTracking.cs"
- Link="Common\System\Net\Logging\DebugThreadTracking.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Net\InternalException.cs"
private static unsafe void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
{
-#if DEBUG
- DebugThreadTracking.SetThreadSource(ThreadKinds.CompletionPort);
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.System))
+ BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped)!;
+
+ if (asyncResult.InternalPeekCompleted)
{
-#endif
- BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped)!;
+ NetEventSource.Fail(null, $"asyncResult.IsCompleted: {asyncResult}");
+ }
+ if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"errorCode:{errorCode} numBytes:{numBytes} nativeOverlapped:{(IntPtr)nativeOverlapped}");
+
+ // Complete the IO and invoke the user's callback.
+ SocketError socketError = (SocketError)errorCode;
- if (asyncResult.InternalPeekCompleted)
+ if (socketError != SocketError.Success && socketError != SocketError.OperationAborted)
+ {
+ // There are cases where passed errorCode does not reflect the details of the underlined socket error.
+ // "So as of today, the key is the difference between WSAECONNRESET and ConnectionAborted,
+ // .e.g remote party or network causing the connection reset or something on the local host (e.g. closesocket
+ // or receiving data after shutdown (SD_RECV)). With Winsock/TCP stack rewrite in longhorn, there may
+ // be other differences as well."
+
+ Socket? socket = asyncResult.AsyncObject as Socket;
+ if (socket == null)
{
- NetEventSource.Fail(null, $"asyncResult.IsCompleted: {asyncResult}");
+ socketError = SocketError.NotSocket;
}
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"errorCode:{errorCode} numBytes:{numBytes} nativeOverlapped:{(IntPtr)nativeOverlapped}");
-
- // Complete the IO and invoke the user's callback.
- SocketError socketError = (SocketError)errorCode;
-
- if (socketError != SocketError.Success && socketError != SocketError.OperationAborted)
+ else if (socket.Disposed)
{
- // There are cases where passed errorCode does not reflect the details of the underlined socket error.
- // "So as of today, the key is the difference between WSAECONNRESET and ConnectionAborted,
- // .e.g remote party or network causing the connection reset or something on the local host (e.g. closesocket
- // or receiving data after shutdown (SD_RECV)). With Winsock/TCP stack rewrite in longhorn, there may
- // be other differences as well."
-
- Socket? socket = asyncResult.AsyncObject as Socket;
- if (socket == null)
- {
- socketError = SocketError.NotSocket;
- }
- else if (socket.Disposed)
- {
- socketError = SocketError.OperationAborted;
- }
- else
+ socketError = SocketError.OperationAborted;
+ }
+ else
+ {
+ try
{
- try
+ // The async IO completed with a failure.
+ // Here we need to call WSAGetOverlappedResult() just so GetLastSocketError() will return the correct error.
+ SocketFlags ignore;
+ bool success = Interop.Winsock.WSAGetOverlappedResult(
+ socket.SafeHandle,
+ nativeOverlapped,
+ out numBytes,
+ false,
+ out ignore);
+ if (!success)
{
- // The async IO completed with a failure.
- // Here we need to call WSAGetOverlappedResult() just so GetLastSocketError() will return the correct error.
- SocketFlags ignore;
- bool success = Interop.Winsock.WSAGetOverlappedResult(
- socket.SafeHandle,
- nativeOverlapped,
- out numBytes,
- false,
- out ignore);
- if (!success)
- {
- socketError = SocketPal.GetLastSocketError();
- }
- if (success)
- {
- NetEventSource.Fail(asyncResult, $"Unexpectedly succeeded. errorCode:{errorCode} numBytes:{numBytes}");
- }
+ socketError = SocketPal.GetLastSocketError();
}
- catch (ObjectDisposedException)
+ if (success)
{
- // Disposed check above does not always work since this code is subject to race conditions
- socketError = SocketError.OperationAborted;
+ NetEventSource.Fail(asyncResult, $"Unexpectedly succeeded. errorCode:{errorCode} numBytes:{numBytes}");
}
}
+ catch (ObjectDisposedException)
+ {
+ // Disposed check above does not always work since this code is subject to race conditions
+ socketError = SocketError.OperationAborted;
+ }
}
-
- // Set results and invoke callback
- asyncResult.CompletionCallback((int)numBytes, socketError);
-#if DEBUG
}
-#endif
+
+ // Set results and invoke callback
+ asyncResult.CompletionCallback((int)numBytes, socketError);
}
// Called either synchronously from SocketPal async routines or asynchronously via CompletionPortCallback above.
public NetworkStream(Socket socket, FileAccess access, bool ownsSocket)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
+ if (socket == null)
{
-#endif
- if (socket == null)
- {
- throw new ArgumentNullException(nameof(socket));
- }
- if (!socket.Blocking)
- {
- // Stream.Read*/Write* are incompatible with the semantics of non-blocking sockets, and
- // allowing non-blocking sockets could result in non-deterministic failures from those
- // operations. A developer that requires using NetworkStream with a non-blocking socket can
- // temporarily flip Socket.Blocking as a workaround.
- throw new IOException(SR.net_sockets_blocking);
- }
- if (!socket.Connected)
- {
- throw new IOException(SR.net_notconnected);
- }
- if (socket.SocketType != SocketType.Stream)
- {
- throw new IOException(SR.net_notstream);
- }
+ throw new ArgumentNullException(nameof(socket));
+ }
+ if (!socket.Blocking)
+ {
+ // Stream.Read*/Write* are incompatible with the semantics of non-blocking sockets, and
+ // allowing non-blocking sockets could result in non-deterministic failures from those
+ // operations. A developer that requires using NetworkStream with a non-blocking socket can
+ // temporarily flip Socket.Blocking as a workaround.
+ throw new IOException(SR.net_sockets_blocking);
+ }
+ if (!socket.Connected)
+ {
+ throw new IOException(SR.net_notconnected);
+ }
+ if (socket.SocketType != SocketType.Stream)
+ {
+ throw new IOException(SR.net_notstream);
+ }
- _streamSocket = socket;
- _ownsSocket = ownsSocket;
+ _streamSocket = socket;
+ _ownsSocket = ownsSocket;
- switch (access)
- {
- case FileAccess.Read:
- _readable = true;
- break;
- case FileAccess.Write:
- _writeable = true;
- break;
- case FileAccess.ReadWrite:
- default: // assume FileAccess.ReadWrite
- _readable = true;
- _writeable = true;
- break;
- }
-#if DEBUG
+ switch (access)
+ {
+ case FileAccess.Read:
+ _readable = true;
+ break;
+ case FileAccess.Write:
+ _writeable = true;
+ break;
+ case FileAccess.ReadWrite:
+ default: // assume FileAccess.ReadWrite
+ _readable = true;
+ _writeable = true;
+ break;
}
-#endif
}
public Socket Socket => _streamSocket;
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ int timeout = (int)_streamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout)!;
+ if (timeout == 0)
{
-#endif
- int timeout = (int)_streamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout)!;
- if (timeout == 0)
- {
- return -1;
- }
- return timeout;
-#if DEBUG
+ return -1;
}
-#endif
+ return timeout;
}
set
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ if (value <= 0 && value != System.Threading.Timeout.Infinite)
{
-#endif
- if (value <= 0 && value != System.Threading.Timeout.Infinite)
- {
- throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_gt_zero);
- }
- SetSocketTimeoutOption(SocketShutdown.Receive, value, false);
-#if DEBUG
+ throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_gt_zero);
}
-#endif
+ SetSocketTimeoutOption(SocketShutdown.Receive, value, false);
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ int timeout = (int)_streamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout)!;
+ if (timeout == 0)
{
-#endif
- int timeout = (int)_streamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout)!;
- if (timeout == 0)
- {
- return -1;
- }
- return timeout;
-#if DEBUG
+ return -1;
}
-#endif
+ return timeout;
}
set
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ if (value <= 0 && value != System.Threading.Timeout.Infinite)
{
-#endif
- if (value <= 0 && value != System.Threading.Timeout.Infinite)
- {
- throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_gt_zero);
- }
- SetSocketTimeoutOption(SocketShutdown.Send, value, false);
-#if DEBUG
+ throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_gt_zero);
}
-#endif
+ SetSocketTimeoutOption(SocketShutdown.Send, value, false);
}
}
{
get
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
- {
-#endif
- ThrowIfDisposed();
+ ThrowIfDisposed();
- // Ask the socket how many bytes are available. If it's
- // not zero, return true.
- return _streamSocket.Available != 0;
-#if DEBUG
- }
-#endif
+ // Ask the socket how many bytes are available. If it's
+ // not zero, return true.
+ return _streamSocket.Available != 0;
}
}
// Number of bytes we read, or 0 if the socket is closed.
public override int Read(byte[] buffer, int offset, int size)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
+ bool canRead = CanRead; // Prevent race with Dispose.
+ ThrowIfDisposed();
+ if (!canRead)
{
-#endif
- bool canRead = CanRead; // Prevent race with Dispose.
- ThrowIfDisposed();
- if (!canRead)
- {
- throw new InvalidOperationException(SR.net_writeonlystream);
- }
+ throw new InvalidOperationException(SR.net_writeonlystream);
+ }
- // Validate input parameters.
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
- if ((uint)offset > buffer.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- if ((uint)size > buffer.Length - offset)
- {
- throw new ArgumentOutOfRangeException(nameof(size));
- }
+ // Validate input parameters.
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+ if ((uint)offset > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+ if ((uint)size > buffer.Length - offset)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
- try
- {
- return _streamSocket.Receive(buffer, offset, size, 0);
- }
- catch (Exception exception) when (!(exception is OutOfMemoryException))
- {
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
- }
-#if DEBUG
+ try
+ {
+ return _streamSocket.Receive(buffer, offset, size, 0);
+ }
+ catch (Exception exception) when (!(exception is OutOfMemoryException))
+ {
+ // Some sort of error occurred on the socket call,
+ // set the SocketException as InnerException and throw.
+ throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
-#endif
}
public override int Read(Span<byte> buffer)
// way to indicate an error.
public override void Write(byte[] buffer, int offset, int size)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
+ bool canWrite = CanWrite; // Prevent race with Dispose.
+ ThrowIfDisposed();
+ if (!canWrite)
{
-#endif
- bool canWrite = CanWrite; // Prevent race with Dispose.
- ThrowIfDisposed();
- if (!canWrite)
- {
- throw new InvalidOperationException(SR.net_readonlystream);
- }
+ throw new InvalidOperationException(SR.net_readonlystream);
+ }
- // Validate input parameters.
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
- if ((uint)offset > buffer.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- if ((uint)size > buffer.Length - offset)
- {
- throw new ArgumentOutOfRangeException(nameof(size));
- }
+ // Validate input parameters.
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+ if ((uint)offset > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+ if ((uint)size > buffer.Length - offset)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
- try
- {
- // Since the socket is in blocking mode this will always complete
- // after ALL the requested number of bytes was transferred.
- _streamSocket.Send(buffer, offset, size, SocketFlags.None);
- }
- catch (Exception exception) when (!(exception is OutOfMemoryException))
- {
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
- }
-#if DEBUG
+ try
+ {
+ // Since the socket is in blocking mode this will always complete
+ // after ALL the requested number of bytes was transferred.
+ _streamSocket.Send(buffer, offset, size, SocketFlags.None);
+ }
+ catch (Exception exception) when (!(exception is OutOfMemoryException))
+ {
+ // Some sort of error occurred on the socket call,
+ // set the SocketException as InnerException and throw.
+ throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
-#endif
}
public override void Write(ReadOnlySpan<byte> buffer)
public void Close(int timeout)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync))
+ if (timeout < -1)
{
-#endif
- if (timeout < -1)
- {
- throw new ArgumentOutOfRangeException(nameof(timeout));
- }
- _closeTimeout = timeout;
- Dispose();
-#if DEBUG
+ throw new ArgumentOutOfRangeException(nameof(timeout));
}
-#endif
+ _closeTimeout = timeout;
+ Dispose();
}
private volatile bool _disposed = false;
protected override void Dispose(bool disposing)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
+ // Mark this as disposed before changing anything else.
+ bool disposed = _disposed;
+ _disposed = true;
+ if (!disposed && disposing)
{
-#endif
- // Mark this as disposed before changing anything else.
- bool disposed = _disposed;
- _disposed = true;
- if (!disposed && disposing)
+ // The only resource we need to free is the network stream, since this
+ // is based on the client socket, closing the stream will cause us
+ // to flush the data to the network, close the stream and (in the
+ // NetoworkStream code) close the socket as well.
+ _readable = false;
+ _writeable = false;
+ if (_ownsSocket)
{
- // The only resource we need to free is the network stream, since this
- // is based on the client socket, closing the stream will cause us
- // to flush the data to the network, close the stream and (in the
- // NetoworkStream code) close the socket as well.
- _readable = false;
- _writeable = false;
- if (_ownsSocket)
- {
- // If we own the Socket (false by default), close it
- // ignoring possible exceptions (eg: the user told us
- // that we own the Socket but it closed at some point of time,
- // here we would get an ObjectDisposedException)
- _streamSocket.InternalShutdown(SocketShutdown.Both);
- _streamSocket.Close(_closeTimeout);
- }
+ // If we own the Socket (false by default), close it
+ // ignoring possible exceptions (eg: the user told us
+ // that we own the Socket but it closed at some point of time,
+ // here we would get an ObjectDisposedException)
+ _streamSocket.InternalShutdown(SocketShutdown.Both);
+ _streamSocket.Close(_closeTimeout);
}
-#if DEBUG
}
-#endif
+
base.Dispose(disposing);
}
- ~NetworkStream()
- {
-#if DEBUG
- DebugThreadTracking.SetThreadSource(ThreadKinds.Finalization);
-#endif
- Dispose(false);
- }
+ ~NetworkStream() => Dispose(false);
// BeginRead - provide async read functionality.
//
// An IASyncResult, representing the read.
public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback? callback, object? state)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ bool canRead = CanRead; // Prevent race with Dispose.
+ ThrowIfDisposed();
+ if (!canRead)
{
-#endif
- bool canRead = CanRead; // Prevent race with Dispose.
- ThrowIfDisposed();
- if (!canRead)
- {
- throw new InvalidOperationException(SR.net_writeonlystream);
- }
+ throw new InvalidOperationException(SR.net_writeonlystream);
+ }
- // Validate input parameters.
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
- if ((uint)offset > buffer.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- if ((uint)size > buffer.Length - offset)
- {
- throw new ArgumentOutOfRangeException(nameof(size));
- }
+ // Validate input parameters.
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+ if ((uint)offset > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+ if ((uint)size > buffer.Length - offset)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
- try
- {
- return _streamSocket.BeginReceive(
- buffer,
- offset,
- size,
- SocketFlags.None,
- callback,
- state);
- }
- catch (Exception exception) when (!(exception is OutOfMemoryException))
- {
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
- }
-#if DEBUG
+ try
+ {
+ return _streamSocket.BeginReceive(
+ buffer,
+ offset,
+ size,
+ SocketFlags.None,
+ callback,
+ state);
+ }
+ catch (Exception exception) when (!(exception is OutOfMemoryException))
+ {
+ // Some sort of error occurred on the socket call,
+ // set the SocketException as InnerException and throw.
+ throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
-#endif
}
// EndRead - handle the end of an async read.
// The number of bytes read. May throw an exception.
public override int EndRead(IAsyncResult asyncResult)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- ThrowIfDisposed();
+ ThrowIfDisposed();
- // Validate input parameters.
- if (asyncResult == null)
- {
- throw new ArgumentNullException(nameof(asyncResult));
- }
+ // Validate input parameters.
+ if (asyncResult == null)
+ {
+ throw new ArgumentNullException(nameof(asyncResult));
+ }
- try
- {
- return _streamSocket.EndReceive(asyncResult);
- }
- catch (Exception exception) when (!(exception is OutOfMemoryException))
- {
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
- }
-#if DEBUG
+ try
+ {
+ return _streamSocket.EndReceive(asyncResult);
+ }
+ catch (Exception exception) when (!(exception is OutOfMemoryException))
+ {
+ // Some sort of error occurred on the socket call,
+ // set the SocketException as InnerException and throw.
+ throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
-#endif
}
// BeginWrite - provide async write functionality.
// An IASyncResult, representing the write.
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback? callback, object? state)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
+ bool canWrite = CanWrite; // Prevent race with Dispose.
+ ThrowIfDisposed();
+ if (!canWrite)
{
-#endif
- bool canWrite = CanWrite; // Prevent race with Dispose.
- ThrowIfDisposed();
- if (!canWrite)
- {
- throw new InvalidOperationException(SR.net_readonlystream);
- }
+ throw new InvalidOperationException(SR.net_readonlystream);
+ }
- // Validate input parameters.
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
- if ((uint)offset > buffer.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- if ((uint)size > buffer.Length - offset)
- {
- throw new ArgumentOutOfRangeException(nameof(size));
- }
+ // Validate input parameters.
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+ if ((uint)offset > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+ if ((uint)size > buffer.Length - offset)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
- try
- {
- // Call BeginSend on the Socket.
- return _streamSocket.BeginSend(
- buffer,
- offset,
- size,
- SocketFlags.None,
- callback,
- state);
- }
- catch (Exception exception) when (!(exception is OutOfMemoryException))
- {
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
- }
-#if DEBUG
+ try
+ {
+ // Call BeginSend on the Socket.
+ return _streamSocket.BeginSend(
+ buffer,
+ offset,
+ size,
+ SocketFlags.None,
+ callback,
+ state);
+ }
+ catch (Exception exception) when (!(exception is OutOfMemoryException))
+ {
+ // Some sort of error occurred on the socket call,
+ // set the SocketException as InnerException and throw.
+ throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
-#endif
}
// Handle the end of an asynchronous write.
// Returns: The number of bytes read. May throw an exception.
public override void EndWrite(IAsyncResult asyncResult)
{
-#if DEBUG
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.User))
- {
-#endif
- ThrowIfDisposed();
+ ThrowIfDisposed();
- // Validate input parameters.
- if (asyncResult == null)
- {
- throw new ArgumentNullException(nameof(asyncResult));
- }
+ // Validate input parameters.
+ if (asyncResult == null)
+ {
+ throw new ArgumentNullException(nameof(asyncResult));
+ }
- try
- {
- _streamSocket.EndSend(asyncResult);
- }
- catch (Exception exception) when (!(exception is OutOfMemoryException))
- {
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
- }
-#if DEBUG
+ try
+ {
+ _streamSocket.EndSend(asyncResult);
+ }
+ catch (Exception exception) when (!(exception is OutOfMemoryException))
+ {
+ // Some sort of error occurred on the socket call,
+ // set the SocketException as InnerException and throw.
+ throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
-#endif
}
// ReadAsync - provide async read functionality.
public void Dispose() => Dispose(true);
- ~TcpClient()
- {
-#if DEBUG
- DebugThreadTracking.SetThreadSource(ThreadKinds.Finalization);
- using (DebugThreadTracking.SetThreadKind(ThreadKinds.System | ThreadKinds.Async))
- {
-#endif
- Dispose(false);
-#if DEBUG
- }
-#endif
- }
+ ~TcpClient() => Dispose(false);
// Gets or sets the size of the receive buffer in bytes.
public int ReceiveBufferSize