diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs
index 93df3bdfed9..4a62578672c 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs
@@ -102,21 +102,6 @@ namespace System.Threading
[return: NotNullIfNotNull(nameof(location1))]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? ExchangeObject([NotNullIfNotNull(nameof(value))] ref object? location1, object? value);
-
- // The below whole method reduces to a single call to Exchange(ref object, object) but
- // the JIT thinks that it will generate more native code than it actually does.
-
- /// Sets a variable of the specified type to a specified value and returns the original value, as an atomic operation.
- /// The variable to set to the specified value.
- /// The value to which the parameter is set.
- /// The original value of .
- /// The address of location1 is a null pointer.
- /// The type to be used for and . This type must be a reference type.
- [Intrinsic]
- [return: NotNullIfNotNull(nameof(location1))]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Exchange([NotNullIfNotNull(nameof(value))] ref T location1, T value) where T : class? =>
- Unsafe.As(Exchange(ref Unsafe.As(ref location1), value));
#endregion
#region CompareExchange
@@ -183,29 +168,6 @@ namespace System.Threading
[MethodImpl(MethodImplOptions.InternalCall)]
[return: NotNullIfNotNull(nameof(location1))]
private static extern object? CompareExchangeObject(ref object? location1, object? value, object? comparand);
-
- // Note that getILIntrinsicImplementationForInterlocked() in vm\jitinterface.cpp replaces
- // the body of the following method with the following IL:
- // ldarg.0
- // ldarg.1
- // ldarg.2
- // call System.Threading.Interlocked::CompareExchange(ref Object, Object, Object)
- // ret
- // The workaround is no longer strictly necessary now that we have Unsafe.As but it does
- // have the advantage of being less sensitive to JIT's inliner decisions.
-
- /// Compares two instances of the specified reference type for reference equality and, if they are equal, replaces the first one.
- /// The destination, whose value is compared by reference with and possibly replaced.
- /// The value that replaces the destination value if the comparison by reference results in equality.
- /// The object that is compared by reference to the value at .
- /// The original value in .
- /// The address of is a null pointer.
- /// The type to be used for , , and . This type must be a reference type.
- [Intrinsic]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [return: NotNullIfNotNull(nameof(location1))]
- public static T CompareExchange(ref T location1, T value, T comparand) where T : class? =>
- Unsafe.As(CompareExchange(ref Unsafe.As(ref location1), value, comparand));
#endregion
#region Add
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 0d2045d553c..322ce25ca96 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -4130,11 +4130,7 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
case NI_System_Threading_Interlocked_Exchange:
case NI_System_Threading_Interlocked_ExchangeAdd:
{
- assert(callType != TYP_STRUCT);
- assert(sig->numArgs == 2);
-
var_types retType = JITtype2varType(sig->retType);
- assert((genTypeSize(retType) >= 4) || (ni == NI_System_Threading_Interlocked_Exchange));
if (genTypeSize(retType) > TARGET_POINTER_SIZE)
{
@@ -4159,6 +4155,10 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
break;
}
+ assert(callType != TYP_STRUCT);
+ assert(sig->numArgs == 2);
+ assert((genTypeSize(retType) >= 4) || (ni == NI_System_Threading_Interlocked_Exchange));
+
GenTree* op2 = impPopStack().val;
GenTree* op1 = impPopStack().val;
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs
index 09b3609b456..0a7fc2e65f2 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs
@@ -36,14 +36,6 @@ namespace System.Threading
#endif
}
- [Intrinsic]
- [return: NotNullIfNotNull(nameof(location1))]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T CompareExchange(ref T location1, T value, T comparand) where T : class?
- {
- return Unsafe.As(CompareExchange(ref Unsafe.As(ref location1), value, comparand));
- }
-
[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(location1))]
@@ -92,16 +84,6 @@ namespace System.Threading
#endif
}
- [Intrinsic]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [return: NotNullIfNotNull(nameof(location1))]
- public static T Exchange([NotNullIfNotNull(nameof(value))] ref T location1, T value) where T : class?
- {
- if (Unsafe.IsNullRef(ref location1))
- ThrowHelper.ThrowNullReferenceException();
- return Unsafe.As(RuntimeImports.InterlockedExchange(ref Unsafe.As(ref location1), value));
- }
-
[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(location1))]
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs
index 0e9ffcc0157..f5296b93ec8 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs
@@ -46,12 +46,6 @@ namespace Internal.IL
switch (owningType.Name)
{
- case "Interlocked":
- {
- if (owningType.Namespace == "System.Threading")
- return InterlockedIntrinsics.EmitIL(method);
- }
- break;
case "Unsafe":
{
if (owningType.Namespace == "System.Runtime.CompilerServices")
@@ -108,6 +102,12 @@ namespace Internal.IL
switch (owningType.Name)
{
+ case "Interlocked":
+ {
+ if (owningType.Namespace == "System.Threading")
+ return InterlockedIntrinsics.EmitIL(method);
+ }
+ break;
case "Activator":
{
TypeSystemContext context = owningType.Context;
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs
index 7f0a8d2f191..c5aa13bd3dd 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs
@@ -19,6 +19,7 @@ namespace Internal.IL.Stubs
MethodDesc method)
{
Debug.Assert(((MetadataType)method.OwningType).Name == "Interlocked");
+ Debug.Assert(!method.IsGenericMethodDefinition);
if (method.HasInstantiation && method.Name == "CompareExchange")
{
@@ -30,22 +31,45 @@ namespace Internal.IL.Stubs
if (compilationModuleGroup.ContainsType(method.OwningType))
#endif // READYTORUN
{
- TypeDesc objectType = method.Context.GetWellKnownType(WellKnownType.Object);
- MethodDesc compareExchangeObject = method.OwningType.GetKnownMethod("CompareExchange",
- new MethodSignature(
- MethodSignatureFlags.Static,
- genericParameterCount: 0,
- returnType: objectType,
- parameters: new TypeDesc[] { objectType.MakeByRefType(), objectType, objectType }));
+ // Rewrite the generic Interlocked.CompareExchange to be a call to one of the non-generic overloads.
+ TypeDesc ceArgType = null;
- ILEmitter emit = new ILEmitter();
- ILCodeStream codeStream = emit.NewCodeStream();
- codeStream.EmitLdArg(0);
- codeStream.EmitLdArg(1);
- codeStream.EmitLdArg(2);
- codeStream.Emit(ILOpcode.call, emit.NewToken(compareExchangeObject));
- codeStream.Emit(ILOpcode.ret);
- return emit.Link(method);
+ TypeDesc tType = method.Instantiation[0];
+ if (!tType.IsValueType)
+ {
+ ceArgType = method.Context.GetWellKnownType(WellKnownType.Object);
+ }
+ else if ((tType.IsPrimitive || tType.IsEnum) && (tType.UnderlyingType.Category is not (TypeFlags.Single or TypeFlags.Double)))
+ {
+ int size = tType.GetElementSize().AsInt;
+ Debug.Assert(size is 1 or 2 or 4 or 8);
+ ceArgType = size switch
+ {
+ 1 => method.Context.GetWellKnownType(WellKnownType.Byte),
+ 2 => method.Context.GetWellKnownType(WellKnownType.UInt16),
+ 4 => method.Context.GetWellKnownType(WellKnownType.Int32),
+ _ => method.Context.GetWellKnownType(WellKnownType.Int64),
+ };
+ }
+
+ if (ceArgType is not null)
+ {
+ MethodDesc compareExchangeNonGeneric = method.OwningType.GetKnownMethod("CompareExchange",
+ new MethodSignature(
+ MethodSignatureFlags.Static,
+ genericParameterCount: 0,
+ returnType: ceArgType,
+ parameters: [ceArgType.MakeByRefType(), ceArgType, ceArgType]));
+
+ ILEmitter emit = new ILEmitter();
+ ILCodeStream codeStream = emit.NewCodeStream();
+ codeStream.EmitLdArg(0);
+ codeStream.EmitLdArg(1);
+ codeStream.EmitLdArg(2);
+ codeStream.Emit(ILOpcode.call, emit.NewToken(compareExchangeNonGeneric));
+ codeStream.Emit(ILOpcode.ret);
+ return emit.Link(method);
+ }
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs
index c5e67286dc2..7fed0872c7d 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs
@@ -85,11 +85,6 @@ namespace Internal.IL
return UnsafeIntrinsics.EmitIL(method);
}
- if (mdType.Name == "Interlocked" && mdType.Namespace == "System.Threading")
- {
- return InterlockedIntrinsics.EmitIL(_compilationModuleGroup, method);
- }
-
return null;
}
@@ -114,6 +109,11 @@ namespace Internal.IL
return TryGetIntrinsicMethodILForActivator(method);
}
+ if (mdType.Name == "Interlocked" && mdType.Namespace == "System.Threading")
+ {
+ return InterlockedIntrinsics.EmitIL(_compilationModuleGroup, method);
+ }
+
return null;
}
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index cca199fbe75..76e40d4477f 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -694,6 +694,10 @@ DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE_MDARRAY, GetArrayDa
DEFINE_CLASS(INTERLOCKED, Threading, Interlocked)
DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_T, CompareExchange, GM_RefT_T_T_RetT)
DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_OBJECT,CompareExchange, SM_RefObject_Object_Object_RetObject)
+DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_BYTE, CompareExchange, SM_RefByte_Byte_Byte_RetByte)
+DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_USHRT, CompareExchange, SM_RefUShrt_UShrt_UShrt_RetUShrt)
+DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_INT, CompareExchange, SM_RefInt_Int_Int_RetInt)
+DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_LONG, CompareExchange, SM_RefLong_Long_Long_RetLong)
DEFINE_CLASS(RAW_DATA, CompilerServices, RawData)
DEFINE_FIELD(RAW_DATA, DATA, Data)
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 0aa34005bdb..3b8c32e20fc 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -7154,28 +7154,79 @@ bool getILIntrinsicImplementationForInterlocked(MethodDesc * ftn,
if (ftn->GetMemberDef() != CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_T)->GetMemberDef())
return false;
- // Get MethodDesc for non-generic System.Threading.Interlocked.CompareExchange()
- MethodDesc* cmpxchgObject = CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_OBJECT);
+ // Determine the type of the generic T method parameter
+ _ASSERTE(ftn->HasMethodInstantiation());
+ _ASSERTE(ftn->GetNumGenericMethodArgs() == 1);
+ TypeHandle typeHandle = ftn->GetMethodInstantiation()[0];
- // Setup up the body of the method
- static BYTE il[] = {
- CEE_LDARG_0,
- CEE_LDARG_1,
- CEE_LDARG_2,
- CEE_CALL,0,0,0,0,
- CEE_RET
- };
+ // Setup up the body of the CompareExchange methods; the method token will be patched on first use.
+ static BYTE il[5][9] =
+ {
+ { CEE_LDARG_0, CEE_LDARG_1, CEE_LDARG_2, CEE_CALL, 0, 0, 0, 0, CEE_RET }, // object
+ { CEE_LDARG_0, CEE_LDARG_1, CEE_LDARG_2, CEE_CALL, 0, 0, 0, 0, CEE_RET }, // byte
+ { CEE_LDARG_0, CEE_LDARG_1, CEE_LDARG_2, CEE_CALL, 0, 0, 0, 0, CEE_RET }, // ushort
+ { CEE_LDARG_0, CEE_LDARG_1, CEE_LDARG_2, CEE_CALL, 0, 0, 0, 0, CEE_RET }, // int
+ { CEE_LDARG_0, CEE_LDARG_1, CEE_LDARG_2, CEE_CALL, 0, 0, 0, 0, CEE_RET }, // long
+ };
- // Get the token for non-generic System.Threading.Interlocked.CompareExchange(), and patch [target]
- mdMethodDef cmpxchgObjectToken = cmpxchgObject->GetMemberDef();
- il[4] = (BYTE)((int)cmpxchgObjectToken >> 0);
- il[5] = (BYTE)((int)cmpxchgObjectToken >> 8);
- il[6] = (BYTE)((int)cmpxchgObjectToken >> 16);
- il[7] = (BYTE)((int)cmpxchgObjectToken >> 24);
+ // Based on the generic method parameter, determine which overload of CompareExchange
+ // to delegate to, or if we can't handle the type at all.
+ int ilIndex;
+ MethodDesc* cmpxchgMethod;
+ if (!typeHandle.IsValueType())
+ {
+ ilIndex = 0;
+ cmpxchgMethod = CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_OBJECT);
+ }
+ else
+ {
+ CorElementType elementType = typeHandle.GetVerifierCorElementType();
+ if (!CorTypeInfo::IsPrimitiveType(elementType) ||
+ elementType == ELEMENT_TYPE_R4 ||
+ elementType == ELEMENT_TYPE_R8)
+ {
+ return false;
+ }
+ else
+ {
+ switch (typeHandle.GetSize())
+ {
+ case 1:
+ ilIndex = 1;
+ cmpxchgMethod = CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_BYTE);
+ break;
+
+ case 2:
+ ilIndex = 2;
+ cmpxchgMethod = CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_USHRT);
+ break;
+
+ case 4:
+ ilIndex = 3;
+ cmpxchgMethod = CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_INT);
+ break;
+
+ case 8:
+ ilIndex = 4;
+ cmpxchgMethod = CoreLibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_LONG);
+ break;
+
+ default:
+ _ASSERT(!"Unexpected primitive type size");
+ return false;
+ }
+ }
+ }
+
+ mdMethodDef cmpxchgToken = cmpxchgMethod->GetMemberDef();
+ il[ilIndex][4] = (BYTE)((int)cmpxchgToken >> 0);
+ il[ilIndex][5] = (BYTE)((int)cmpxchgToken >> 8);
+ il[ilIndex][6] = (BYTE)((int)cmpxchgToken >> 16);
+ il[ilIndex][7] = (BYTE)((int)cmpxchgToken >> 24);
// Initialize methInfo
- methInfo->ILCode = const_cast(il);
- methInfo->ILCodeSize = sizeof(il);
+ methInfo->ILCode = const_cast(il[ilIndex]);
+ methInfo->ILCodeSize = sizeof(il[ilIndex]);
methInfo->maxStack = 3;
methInfo->EHcount = 0;
methInfo->options = (CorInfoOptions)0;
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index 69eae039076..8a926c6b790 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -586,6 +586,8 @@ DEFINE_METASIG_T(SM(RefDec_RetVoid, r(g(DECIMAL)), v))
DEFINE_METASIG(GM(RefT_T_T_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)) M(0) M(0), M(0)))
DEFINE_METASIG(SM(RefObject_Object_Object_RetObject, r(j) j j, j))
+DEFINE_METASIG(SM(RefByte_Byte_Byte_RetByte, r(b) b b, b))
+DEFINE_METASIG(SM(RefUShrt_UShrt_UShrt_RetUShrt, r(H) H H, H))
DEFINE_METASIG_T(SM(RefCleanupWorkListElement_RetVoid, r(C(CLEANUP_WORK_LIST_ELEMENT)), v))
DEFINE_METASIG_T(SM(RefCleanupWorkListElement_SafeHandle_RetIntPtr, r(C(CLEANUP_WORK_LIST_ELEMENT)) C(SAFE_HANDLE), I))
diff --git a/src/libraries/Common/src/System/Net/StreamBuffer.cs b/src/libraries/Common/src/System/Net/StreamBuffer.cs
index 32bc0f3f4e4..4af8e4bd751 100644
--- a/src/libraries/Common/src/System/Net/StreamBuffer.cs
+++ b/src/libraries/Common/src/System/Net/StreamBuffer.cs
@@ -292,7 +292,7 @@ namespace System.IO
private ManualResetValueTaskSourceCore _waitSource; // mutable struct, do not make this readonly
private CancellationTokenRegistration _waitSourceCancellation;
- private int _hasWaiter;
+ private bool _hasWaiter;
ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _waitSource.GetStatus(token);
@@ -300,7 +300,7 @@ namespace System.IO
void IValueTaskSource.GetResult(short token)
{
- Debug.Assert(_hasWaiter == 0);
+ Debug.Assert(!_hasWaiter);
// Clean up the registration. This will wait for any in-flight cancellation to complete.
_waitSourceCancellation.Dispose();
@@ -312,7 +312,7 @@ namespace System.IO
public void SignalWaiter()
{
- if (Interlocked.Exchange(ref _hasWaiter, 0) == 1)
+ if (Interlocked.Exchange(ref _hasWaiter, false))
{
_waitSource.SetResult(true);
}
@@ -322,7 +322,7 @@ namespace System.IO
{
Debug.Assert(cancellationToken.IsCancellationRequested);
- if (Interlocked.Exchange(ref _hasWaiter, 0) == 1)
+ if (Interlocked.Exchange(ref _hasWaiter, false))
{
_waitSource.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException(cancellationToken)));
}
@@ -330,13 +330,13 @@ namespace System.IO
public void Reset()
{
- if (_hasWaiter != 0)
+ if (_hasWaiter)
{
throw new InvalidOperationException("Concurrent use is not supported");
}
_waitSource.Reset();
- Volatile.Write(ref _hasWaiter, 1);
+ Volatile.Write(ref _hasWaiter, true);
}
public void Wait()
diff --git a/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs b/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs
index f025a7977f2..5666195ac86 100644
--- a/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs
+++ b/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs
@@ -21,8 +21,8 @@ namespace System.Net.WebSockets
// Used by the class to indicate that the stream is writable.
private bool _writeable;
- // Whether Dispose has been called. 0 == false, 1 == true
- private int _disposed;
+ // Whether Dispose has been called.
+ private bool _disposed;
public WebSocketStream(WebSocket socket)
: this(socket, FileAccess.ReadWrite, ownsSocket: false)
@@ -140,7 +140,7 @@ namespace System.Net.WebSockets
protected override void Dispose(bool disposing)
{
- if (Interlocked.Exchange(ref _disposed, 1) != 0)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
@@ -269,7 +269,7 @@ namespace System.Net.WebSockets
private void ThrowIfDisposed()
{
- ObjectDisposedException.ThrowIf(_disposed != 0, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
}
private static IOException WrapException(string resourceFormatString, Exception innerException)
diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs
index 544aa251d1b..acc0f1f4d84 100644
--- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs
+++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs
@@ -628,11 +628,11 @@ namespace System.Collections.Concurrent
// Finally, wait for all unsynchronized operations on each queue to be done.
for (WorkStealingQueue? queue = head; queue != null; queue = queue._nextQueue)
{
- if (queue._currentOp != (int)Operation.None)
+ if (queue._currentOp != Operation.None)
{
SpinWait spinner = default;
do { spinner.SpinOnce(); }
- while (queue._currentOp != (int)Operation.None);
+ while (queue._currentOp != Operation.None);
}
}
}
@@ -684,7 +684,7 @@ namespace System.Collections.Concurrent
/// Number of steals; needs to be combined with to get an actual Count.
private int _stealCount;
/// The current queue operation. Used to quiesce before performing operations from one thread onto another.
- internal volatile int _currentOp;
+ internal volatile Operation _currentOp;
/// true if this queue's lock is held as part of a global freeze.
internal bool _frozen;
/// Next queue in the 's set of thread-local queues.
@@ -726,14 +726,14 @@ namespace System.Collections.Concurrent
try
{
// Full fence to ensure subsequent reads don't get reordered before this
- Interlocked.Exchange(ref _currentOp, (int)Operation.Add);
+ Interlocked.Exchange(ref _currentOp, Operation.Add);
int tail = _tailIndex;
// Rare corner case (at most once every 2 billion pushes on this thread):
// We're going to increment the tail; if we'll overflow, then we need to reset our counts
if (tail == int.MaxValue)
{
- _currentOp = (int)Operation.None; // set back to None temporarily to avoid a deadlock
+ _currentOp = Operation.None; // set back to None temporarily to avoid a deadlock
lock (this)
{
Debug.Assert(_tailIndex == tail, "No other thread should be changing _tailIndex");
@@ -749,7 +749,7 @@ namespace System.Collections.Concurrent
_tailIndex = tail &= _mask;
Debug.Assert(_headIndex - _tailIndex <= 0);
- Interlocked.Exchange(ref _currentOp, (int)Operation.Add); // ensure subsequent reads aren't reordered before this
+ Interlocked.Exchange(ref _currentOp, Operation.Add); // ensure subsequent reads aren't reordered before this
}
}
@@ -778,7 +778,7 @@ namespace System.Collections.Concurrent
else
{
// We need to contend with foreign operations (e.g. steals, enumeration, etc.), so we lock.
- _currentOp = (int)Operation.None; // set back to None to avoid a deadlock
+ _currentOp = Operation.None; // set back to None to avoid a deadlock
Monitor.Enter(this, ref lockTaken);
head = _headIndex;
@@ -830,7 +830,7 @@ namespace System.Collections.Concurrent
}
finally
{
- _currentOp = (int)Operation.None;
+ _currentOp = Operation.None;
if (lockTaken)
{
Monitor.Exit(this);
@@ -874,7 +874,7 @@ namespace System.Collections.Concurrent
// If the read of _headIndex moved before this write to _tailIndex, we could erroneously end up
// popping an element that's concurrently being stolen, leading to the same element being
// dequeued from the bag twice.
- _currentOp = (int)Operation.Take;
+ _currentOp = Operation.Take;
Interlocked.Exchange(ref _tailIndex, --tail);
// If there is no interaction with a steal, we can head down the fast path.
@@ -895,7 +895,7 @@ namespace System.Collections.Concurrent
else
{
// Interaction with steals: 0 or 1 elements left.
- _currentOp = (int)Operation.None; // set back to None to avoid a deadlock
+ _currentOp = Operation.None; // set back to None to avoid a deadlock
Monitor.Enter(this, ref lockTaken);
if (_headIndex - tail <= 0)
{
@@ -920,7 +920,7 @@ namespace System.Collections.Concurrent
}
finally
{
- _currentOp = (int)Operation.None;
+ _currentOp = Operation.None;
if (lockTaken)
{
Monitor.Exit(this);
@@ -975,14 +975,14 @@ namespace System.Collections.Concurrent
// is in progress, as add operations need to accurately count transitions
// from empty to non-empty, and they can only do that if there are no concurrent
// steal operations happening at the time.
- if ((head - (_tailIndex - 2) >= 0) && _currentOp == (int)Operation.Add)
+ if ((head - (_tailIndex - 2) >= 0) && _currentOp == Operation.Add)
{
SpinWait spinner = default;
do
{
spinner.SpinOnce();
}
- while (_currentOp == (int)Operation.Add);
+ while (_currentOp == Operation.Add);
}
// Increment head to tentatively take an element: a full fence is used to ensure the read
diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs
index a9b6a2cd709..6fb9f57edc4 100644
--- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs
+++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs
@@ -1276,11 +1276,11 @@ namespace System.ComponentModel.Tests
{
}
- private long _concurrentError = 0;
+ private volatile bool _concurrentError;
private bool ConcurrentError
{
- get => Interlocked.Read(ref _concurrentError) == 1;
- set => Interlocked.Exchange(ref _concurrentError, value ? 1 : 0);
+ get => _concurrentError;
+ set => Interlocked.Exchange(ref _concurrentError, value);
}
private void ConcurrentTest(TypeWithProperty instance)
diff --git a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs
index a7a9e603f1a..002186c9b36 100644
--- a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs
+++ b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs
@@ -128,7 +128,7 @@ namespace System.IO.Compression
if (buffer != null)
{
_buffer = null!;
- if (!AsyncOperationIsActive)
+ if (!_activeAsyncOperation)
{
ArrayPool.Shared.Return(buffer);
}
@@ -170,18 +170,19 @@ namespace System.IO.Compression
/// The length of the stream.
public override void SetLength(long value) => throw new NotSupportedException();
- private int _activeAsyncOperation; // 1 == true, 0 == false
- private bool AsyncOperationIsActive => _activeAsyncOperation != 0;
+ private volatile bool _activeAsyncOperation;
private void EnsureNoActiveAsyncOperation()
{
- if (AsyncOperationIsActive)
+ if (_activeAsyncOperation)
+ {
ThrowInvalidBeginCall();
+ }
}
private void AsyncOperationStarting()
{
- if (Interlocked.Exchange(ref _activeAsyncOperation, 1) != 0)
+ if (Interlocked.Exchange(ref _activeAsyncOperation, true))
{
ThrowInvalidBeginCall();
}
@@ -189,8 +190,8 @@ namespace System.IO.Compression
private void AsyncOperationCompleting()
{
- Debug.Assert(_activeAsyncOperation == 1);
- Volatile.Write(ref _activeAsyncOperation, 0);
+ Debug.Assert(_activeAsyncOperation);
+ _activeAsyncOperation = false;
}
private static void ThrowInvalidBeginCall() =>
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs
index 07af0f3a93e..2ae2804ef6f 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs
@@ -20,7 +20,7 @@ namespace System.IO.Compression
private Inflater? _inflater;
private Deflater? _deflater;
private byte[]? _buffer;
- private int _activeAsyncOperation; // 1 == true, 0 == false
+ private volatile bool _activeAsyncOperation;
private bool _wroteBytes;
internal DeflateStream(Stream stream, CompressionMode mode, long uncompressedSize) : this(stream, mode, leaveOpen: false, ZLibNative.Deflate_DefaultWindowBits, uncompressedSize)
@@ -698,7 +698,7 @@ namespace System.IO.Compression
if (buffer != null)
{
_buffer = null;
- if (!AsyncOperationIsActive)
+ if (!_activeAsyncOperation)
{
ArrayPool.Shared.Return(buffer);
}
@@ -751,7 +751,7 @@ namespace System.IO.Compression
if (buffer != null)
{
_buffer = null;
- if (!AsyncOperationIsActive)
+ if (!_activeAsyncOperation)
{
ArrayPool.Shared.Return(buffer);
}
@@ -1059,24 +1059,27 @@ namespace System.IO.Compression
public override void SetLength(long value) { throw new NotSupportedException(); }
}
- private bool AsyncOperationIsActive => _activeAsyncOperation != 0;
-
private void EnsureNoActiveAsyncOperation()
{
- if (AsyncOperationIsActive)
- ThrowInvalidBeginCall();
- }
-
- private void AsyncOperationStarting()
- {
- if (Interlocked.Exchange(ref _activeAsyncOperation, 1) != 0)
+ if (_activeAsyncOperation)
{
ThrowInvalidBeginCall();
}
}
- private void AsyncOperationCompleting() =>
- Volatile.Write(ref _activeAsyncOperation, 0);
+ private void AsyncOperationStarting()
+ {
+ if (Interlocked.Exchange(ref _activeAsyncOperation, true))
+ {
+ ThrowInvalidBeginCall();
+ }
+ }
+
+ private void AsyncOperationCompleting()
+ {
+ Debug.Assert(_activeAsyncOperation);
+ _activeAsyncOperation = false;
+ }
private static void ThrowInvalidBeginCall() =>
throw new InvalidOperationException(SR.InvalidBeginCall);
diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Channels/AsynchronousChannel.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Channels/AsynchronousChannel.cs
index 775228fb66c..6bffc272ee8 100644
--- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Channels/AsynchronousChannel.cs
+++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Channels/AsynchronousChannel.cs
@@ -86,10 +86,10 @@ namespace System.Linq.Parallel
private ManualResetEventSlim? _producerEvent;
private IntValueEvent? _consumerEvent;
- // These two-valued ints track whether a producer or consumer _might_ be waiting. They are marked
+ // These bools track whether a producer or consumer _might_ be waiting. They are marked
// volatile because they are used in synchronization critical regions of code (see usage below).
- private volatile int _producerIsWaiting;
- private volatile int _consumerIsWaiting;
+ private volatile bool _producerIsWaiting;
+ private volatile bool _consumerIsWaiting;
private readonly CancellationToken _cancellationToken;
//-----------------------------------------------------------------------------------
@@ -309,11 +309,11 @@ namespace System.Linq.Parallel
// our producer index doesn't pass the read of the consumer waiting flags; the CLR memory
// model unfortunately permits this reordering. That is handled by using a CAS above.)
- if (_consumerIsWaiting == 1 && !IsChunkBufferEmpty)
+ if (_consumerIsWaiting && !IsChunkBufferEmpty)
{
TraceHelpers.TraceInfo("AsynchronousChannel::EnqueueChunk - producer waking consumer");
Debug.Assert(_consumerEvent != null);
- _consumerIsWaiting = 0;
+ _consumerIsWaiting = false;
_consumerEvent.Set(_index);
}
}
@@ -341,7 +341,7 @@ namespace System.Linq.Parallel
// very quickly, suddenly seeing an empty queue. This would lead to deadlock
// if we aren't careful. Therefore we check the empty/full state AGAIN after
// setting our flag to see if a real wait is warranted.
- Interlocked.Exchange(ref _producerIsWaiting, 1);
+ Interlocked.Exchange(ref _producerIsWaiting, true);
// (We have to prevent the reads that go into determining whether the buffer
// is full from moving before the write to the producer-wait flag. Hence the CAS.)
@@ -359,7 +359,7 @@ namespace System.Linq.Parallel
else
{
// Reset the flags, we don't actually have to wait after all.
- _producerIsWaiting = 0;
+ _producerIsWaiting = false;
}
}
while (IsFull);
@@ -558,7 +558,7 @@ namespace System.Linq.Parallel
// very quickly, suddenly seeing a full queue. This would lead to deadlock
// if we aren't careful. Therefore we check the empty/full state AGAIN after
// setting our flag to see if a real wait is warranted.
- Interlocked.Exchange(ref _consumerIsWaiting, 1);
+ Interlocked.Exchange(ref _consumerIsWaiting, true);
// (We have to prevent the reads that go into determining whether the buffer
// is full from moving before the write to the producer-wait flag. Hence the CAS.)
@@ -580,7 +580,7 @@ namespace System.Linq.Parallel
{
// Reset the wait flags, we don't need to wait after all. We loop back around
// and recheck that the queue isn't empty, done, etc.
- _consumerIsWaiting = 0;
+ _consumerIsWaiting = false;
}
}
@@ -624,11 +624,11 @@ namespace System.Linq.Parallel
// that the write to _consumerBufferIndex doesn't pass the read of the wait-flags; the CLR memory
// model sadly permits this reordering. Hence the CAS above.)
- if (_producerIsWaiting == 1 && !IsFull)
+ if (_producerIsWaiting && !IsFull)
{
TraceHelpers.TraceInfo("BoundedSingleLockFreeChannel::DequeueChunk - consumer waking producer");
Debug.Assert(_producerEvent != null);
- _producerIsWaiting = 0;
+ _producerIsWaiting = false;
_producerEvent.Set();
}
@@ -643,7 +643,7 @@ namespace System.Linq.Parallel
internal void DoneWithDequeueWait()
{
// On our way out, be sure to reset the flags.
- _consumerIsWaiting = 0;
+ _consumerIsWaiting = false;
}
//-----------------------------------------------------------------------------------
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs
index d2f1389ae12..379c97d5c70 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs
@@ -38,10 +38,10 @@ namespace System.Net.Http
// https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4.1-2.2.1
private uint _maxHeaderListSize = uint.MaxValue; // Defaults to infinite
- // Once the server's streams are received, these are set to 1. Further receipt of these streams results in a connection error.
- private int _haveServerControlStream;
- private int _haveServerQpackDecodeStream;
- private int _haveServerQpackEncodeStream;
+ // Once the server's streams are received, these are set to true. Further receipt of these streams results in a connection error.
+ private bool _haveServerControlStream;
+ private bool _haveServerQpackDecodeStream;
+ private bool _haveServerQpackEncodeStream;
// A connection-level error will abort any future operations.
private Exception? _abortException;
@@ -598,7 +598,7 @@ namespace System.Net.Http
switch (buffer.ActiveSpan[0])
{
case (byte)Http3StreamType.Control:
- if (Interlocked.Exchange(ref _haveServerControlStream, 1) != 0)
+ if (Interlocked.Exchange(ref _haveServerControlStream, true))
{
// A second control stream has been received.
throw HttpProtocolException.CreateHttp3ConnectionException(Http3ErrorCode.StreamCreationError);
@@ -614,7 +614,7 @@ namespace System.Net.Http
await ProcessServerControlStreamAsync(stream, bufferCopy).ConfigureAwait(false);
return;
case (byte)Http3StreamType.QPackDecoder:
- if (Interlocked.Exchange(ref _haveServerQpackDecodeStream, 1) != 0)
+ if (Interlocked.Exchange(ref _haveServerQpackDecodeStream, true))
{
// A second QPack decode stream has been received.
throw HttpProtocolException.CreateHttp3ConnectionException(Http3ErrorCode.StreamCreationError);
@@ -625,7 +625,7 @@ namespace System.Net.Http
await stream.CopyToAsync(Stream.Null).ConfigureAwait(false);
return;
case (byte)Http3StreamType.QPackEncoder:
- if (Interlocked.Exchange(ref _haveServerQpackEncodeStream, 1) != 0)
+ if (Interlocked.Exchange(ref _haveServerQpackEncodeStream, true))
{
// A second QPack encode stream has been received.
throw HttpProtocolException.CreateHttp3ConnectionException(Http3ErrorCode.StreamCreationError);
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs
index e71c4c743d6..0b03489d93a 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs
@@ -67,8 +67,7 @@ namespace System.Net.Http
private bool _canRetry;
private bool _connectionClose; // Connection: close was seen on last response
- private const int Status_Disposed = 1;
- private int _disposed;
+ private volatile bool _disposed;
public HttpConnection(
HttpConnectionPool pool,
@@ -98,7 +97,7 @@ namespace System.Net.Http
{
// Ensure we're only disposed once. Dispose could be called concurrently, for example,
// if the request and the response were running concurrently and both incurred an exception.
- if (Interlocked.Exchange(ref _disposed, Status_Disposed) != Status_Disposed)
+ if (!Interlocked.Exchange(ref _disposed, true))
{
if (NetEventSource.Log.IsEnabled()) Trace("Connection closing.");
@@ -873,7 +872,7 @@ namespace System.Net.Http
// In case the connection is disposed, it's most probable that
// expect100Continue timer expired and request content sending failed.
// We're awaiting the task to propagate the exception in this case.
- if (Volatile.Read(ref _disposed) == Status_Disposed)
+ if (_disposed)
{
try
{
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs
index 02ba78ab99c..bfdbfb780fb 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs
@@ -11,13 +11,13 @@ namespace System.Net.Http
{
internal abstract class HttpContentReadStream : HttpContentStream
{
- private int _disposed; // 0==no, 1==yes
+ private bool _disposed;
public HttpContentReadStream(HttpConnection connection) : base(connection)
{
}
- public sealed override bool CanRead => _disposed == 0;
+ public sealed override bool CanRead => !_disposed;
public sealed override bool CanWrite => false;
public sealed override void Write(ReadOnlySpan buffer) => throw new NotSupportedException(SR.net_http_content_readonly_stream);
@@ -26,7 +26,7 @@ namespace System.Net.Http
public virtual bool NeedsDrain => false;
- protected bool IsDisposed => _disposed == 1;
+ protected bool IsDisposed => _disposed;
protected bool CanReadFromConnection
{
@@ -35,7 +35,7 @@ namespace System.Net.Http
// _connection == null typically means that we have finished reading the response.
// Cancellation may lead to a state where a disposed _connection is not null.
HttpConnection? connection = _connection;
- return connection != null && connection._disposed != Status_Disposed;
+ return connection != null && !connection._disposed;
}
}
@@ -52,7 +52,7 @@ namespace System.Net.Http
// response stream and response content) will kick off multiple concurrent draining
// operations. Also don't delegate to the base if Dispose has already been called,
// as doing so will end up disposing of the connection before we're done draining.
- if (Interlocked.Exchange(ref _disposed, 1) != 0)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs
index 899fff5d788..9315b0c511f 100644
--- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs
+++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs
@@ -1535,7 +1535,15 @@ namespace System.Net
private readonly ulong _connectionId;
private readonly HttpListenerSession _listenerSession;
private readonly NativeOverlapped* _nativeOverlapped;
- private int _ownershipState; // 0 = normal, 1 = in HandleAuthentication(), 2 = disconnected, 3 = cleaned up
+ private OwnershipState _ownershipState;
+
+ private enum OwnershipState
+ {
+ Normal,
+ InHandleAuthentication,
+ Disconnected,
+ CleanedUp
+ }
internal NativeOverlapped* NativeOverlapped
{
@@ -1577,7 +1585,7 @@ namespace System.Net
internal unsafe DisconnectAsyncResult(HttpListenerSession session, ulong connectionId)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"HttpListener: {session.Listener}, ConnectionId: {connectionId}");
- _ownershipState = 1;
+ _ownershipState = OwnershipState.InHandleAuthentication;
_listenerSession = session;
_connectionId = connectionId;
@@ -1588,23 +1596,23 @@ namespace System.Net
internal bool StartOwningDisconnectHandling()
{
- int oldValue;
+ OwnershipState oldValue;
SpinWait spin = default;
- while ((oldValue = Interlocked.CompareExchange(ref _ownershipState, 1, 0)) == 2)
+ while ((oldValue = Interlocked.CompareExchange(ref _ownershipState, OwnershipState.InHandleAuthentication, OwnershipState.Normal)) == OwnershipState.Disconnected)
{
// Must block until it equals 3 - we must be in the callback right now.
spin.SpinOnce();
}
- Debug.Assert(oldValue != 1, "StartOwningDisconnectHandling called twice.");
- return oldValue < 2;
+ Debug.Assert(oldValue != OwnershipState.InHandleAuthentication, "StartOwningDisconnectHandling called twice.");
+ return oldValue < OwnershipState.Disconnected;
}
internal void FinishOwningDisconnectHandling()
{
// If it got disconnected, run the disconnect code.
- if (Interlocked.CompareExchange(ref _ownershipState, 0, 1) == 2)
+ if (Interlocked.CompareExchange(ref _ownershipState, OwnershipState.Normal, OwnershipState.InHandleAuthentication) == OwnershipState.Disconnected)
{
HandleDisconnect();
}
@@ -1620,7 +1628,7 @@ namespace System.Net
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, "_connectionId:" + asyncResult._connectionId);
asyncResult._listenerSession.RequestQueueBoundHandle.FreeNativeOverlapped(nativeOverlapped);
- if (Interlocked.Exchange(ref asyncResult._ownershipState, 2) == 0)
+ if (Interlocked.Exchange(ref asyncResult._ownershipState, OwnershipState.Disconnected) == OwnershipState.Normal)
{
asyncResult.HandleDisconnect();
}
@@ -1655,8 +1663,8 @@ namespace System.Net
identity.Dispose();
}
- int oldValue = Interlocked.Exchange(ref _ownershipState, 3);
- Debug.Assert(oldValue == 2, $"Expected OwnershipState of 2, saw {oldValue}.");
+ OwnershipState oldValue = Interlocked.Exchange(ref _ownershipState, OwnershipState.CleanedUp);
+ Debug.Assert(oldValue == OwnershipState.Disconnected, $"Expected OwnershipState of Disconnected, saw {oldValue}.");
}
internal WindowsPrincipal? AuthenticatedConnection { get; set; }
diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs
index c1d2e8f8132..982a4766538 100644
--- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs
+++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs
@@ -50,7 +50,7 @@ namespace System.Net.WebSockets
private volatile WebSocketOperation.CloseOutputOperation? _closeOutputOperation;
private Nullable _closeStatus;
private string? _closeStatusDescription;
- private int _receiveState;
+ private ReceiveState _receiveState;
private Exception? _pendingException;
protected WebSocketBase(Stream innerStream,
@@ -1199,9 +1199,9 @@ namespace System.Net.WebSockets
ObjectDisposedException.ThrowIf(_isDisposed, this);
}
- private void UpdateReceiveState(int newReceiveState, int expectedReceiveState)
+ private void UpdateReceiveState(ReceiveState newReceiveState, ReceiveState expectedReceiveState)
{
- int receiveState;
+ ReceiveState receiveState;
if ((receiveState = Interlocked.Exchange(ref _receiveState, newReceiveState)) != expectedReceiveState)
{
Debug.Fail($"'_receiveState' had an invalid value '{receiveState}'. The expected value was '{expectedReceiveState}'.");
@@ -1588,7 +1588,7 @@ namespace System.Net.WebSockets
public sealed class ReceiveOperation : WebSocketOperation
{
- private int _receiveState;
+ private ReceiveState _receiveState;
private bool _pongReceived;
private bool _receiveCompleted;
@@ -1614,8 +1614,7 @@ namespace System.Net.WebSockets
_receiveCompleted = false;
_webSocket.ThrowIfDisposed();
- int originalReceiveState = Interlocked.CompareExchange(ref _webSocket._receiveState,
- ReceiveState.Application, ReceiveState.Idle);
+ ReceiveState originalReceiveState = Interlocked.CompareExchange(ref _webSocket._receiveState, ReceiveState.Application, ReceiveState.Idle);
switch (originalReceiveState)
{
@@ -1709,7 +1708,7 @@ namespace System.Net.WebSockets
{
ArraySegment payload;
WebSocketMessageType messageType = GetMessageType(bufferType);
- int newReceiveState = ReceiveState.Idle;
+ ReceiveState newReceiveState = ReceiveState.Idle;
if (bufferType == WebSocketProtocolComponent.BufferType.Close)
{
@@ -2156,12 +2155,12 @@ namespace System.Net.WebSockets
Task CloseNetworkConnectionAsync(CancellationToken cancellationToken);
}
- private static class ReceiveState
+ private enum ReceiveState
{
- internal const int SendOperation = -1;
- internal const int Idle = 0;
- internal const int Application = 1;
- internal const int PayloadAvailable = 2;
+ SendOperation = -1,
+ Idle = 0,
+ Application = 1,
+ PayloadAvailable = 2,
}
}
}
diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs
index 245fb112d93..b1426b89dad 100644
--- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs
+++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs
@@ -44,7 +44,7 @@ namespace System.Net.WebSockets
private ArraySegment _pinnedSendBuffer;
private GCHandle _pinnedSendBufferHandle;
private int _stateWhenDisposing = int.MinValue;
- private int _sendBufferState;
+ private SendBufferState _sendBufferState;
private WebSocketBuffer(ArraySegment internalBuffer, int receiveBufferSize, int sendBufferSize)
{
@@ -170,7 +170,7 @@ namespace System.Net.WebSockets
{
bufferHasBeenPinned = false;
WebSocketValidate.ValidateBuffer(payload.Array!, payload.Offset, payload.Count);
- int previousState = Interlocked.Exchange(ref _sendBufferState, SendBufferState.SendPayloadSpecified);
+ SendBufferState previousState = Interlocked.Exchange(ref _sendBufferState, SendBufferState.SendPayloadSpecified);
if (previousState != SendBufferState.None)
{
@@ -274,7 +274,7 @@ namespace System.Net.WebSockets
// This method is only thread safe for races between Abort and at most 1 uncompleted send operation
internal void ReleasePinnedSendBuffer()
{
- int previousState = Interlocked.Exchange(ref _sendBufferState, SendBufferState.None);
+ SendBufferState previousState = Interlocked.Exchange(ref _sendBufferState, SendBufferState.None);
if (previousState != SendBufferState.SendPayloadSpecified)
{
@@ -662,10 +662,10 @@ namespace System.Net.WebSockets
return 2 * receiveBufferSize + nativeSendBufferSize + NativeOverheadBufferSize + s_PropertyBufferSize;
}
- private static class SendBufferState
+ private enum SendBufferState
{
- public const int None = 0;
- public const int SendPayloadSpecified = 1;
+ None = 0,
+ SendPayloadSpecified = 1,
}
private sealed class PayloadReceiveResult
diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs
index d694448435d..229d4bcba99 100644
--- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs
+++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs
@@ -30,7 +30,7 @@ namespace System.Net.WebSockets
private HttpListenerAsyncEventArgs? _readEventArgs;
private TaskCompletionSource? _writeTaskCompletionSource;
private TaskCompletionSource? _readTaskCompletionSource;
- private int _cleanedUp;
+ private bool _cleanedUp;
#if DEBUG
private sealed class OutstandingOperations
@@ -595,7 +595,7 @@ namespace System.Net.WebSockets
protected override void Dispose(bool disposing)
{
- if (disposing && Interlocked.Exchange(ref _cleanedUp, 1) == 0)
+ if (disposing && !Interlocked.Exchange(ref _cleanedUp, true))
{
_readTaskCompletionSource?.TrySetCanceled();
@@ -716,10 +716,14 @@ namespace System.Net.WebSockets
internal sealed class HttpListenerAsyncEventArgs : EventArgs, IDisposable
{
- private const int Free = 0;
- private const int InProgress = 1;
- private const int Disposed = 2;
- private int _operating;
+ private OperatingState _operating;
+
+ private enum OperatingState
+ {
+ Free = 0,
+ InProgress = 1,
+ Disposed = 2,
+ }
private bool _disposeCalled;
private unsafe NativeOverlapped* _ptrNativeOverlapped;
@@ -783,7 +787,7 @@ namespace System.Net.WebSockets
Debug.Assert(!_shouldCloseOutput, "'m_ShouldCloseOutput' MUST be 'false' at this point.");
Debug.Assert(value == null || _buffer == null,
"Either 'm_Buffer' or 'm_BufferList' MUST be NULL.");
- Debug.Assert(_operating == Free,
+ Debug.Assert(_operating == OperatingState.Free,
"This property can only be modified if no IO operation is outstanding.");
Debug.Assert(value == null || value.Count == 2,
"This list can only be 'NULL' or MUST have exactly '2' items.");
@@ -883,7 +887,7 @@ namespace System.Net.WebSockets
_disposeCalled = true;
// Check if this object is in-use for an async socket operation.
- if (Interlocked.CompareExchange(ref _operating, Disposed, Free) != Free)
+ if (Interlocked.CompareExchange(ref _operating, OperatingState.Disposed, OperatingState.Free) != OperatingState.Free)
{
// Either already disposed or will be disposed when current operation completes.
return;
@@ -930,7 +934,7 @@ namespace System.Net.WebSockets
internal void StartOperationCommon(ThreadPoolBoundHandle boundHandle)
{
// Change status to "in-use".
- if (Interlocked.CompareExchange(ref _operating, InProgress, Free) != Free)
+ if (Interlocked.CompareExchange(ref _operating, OperatingState.InProgress, OperatingState.Free) != OperatingState.Free)
{
// If it was already "in-use" check if Dispose was called.
ObjectDisposedException.ThrowIf(_disposeCalled, this);
@@ -1039,7 +1043,7 @@ namespace System.Net.WebSockets
{
FreeOverlapped(false);
// Mark as not in-use
- Interlocked.Exchange(ref _operating, Free);
+ Interlocked.Exchange(ref _operating, OperatingState.Free);
// Check for deferred Dispose().
// The deferred Dispose is not guaranteed if Dispose is called while an operation is in progress.
diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs
index 26a496f87a6..884d3b18a38 100644
--- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs
+++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs
@@ -739,7 +739,7 @@ namespace System.Net.Mail
CancellationTokenRegistration ctr = default;
// Indicates whether the CTR has been set - captured in handler
- int state = 0;
+ bool ctrSet = false;
// Register a handler that will transfer completion results to the TCS Task
SendCompletedEventHandler? handler = null;
@@ -750,7 +750,7 @@ namespace System.Net.Mail
try
{
((SmtpClient)sender).SendCompleted -= handler;
- if (Interlocked.Exchange(ref state, 1) != 0)
+ if (Interlocked.Exchange(ref ctrSet, true))
{
// A CTR has been set, we have to wait until it completes before completing the task
ctr.Dispose();
@@ -783,7 +783,7 @@ namespace System.Net.Mail
((SmtpClient)s!).SendAsyncCancel();
}, this);
- if (Interlocked.Exchange(ref state, 1) != 0)
+ if (Interlocked.Exchange(ref ctrSet, true))
{
// SendCompleted was already invoked, ensure the CTR completes before returning the task
ctr.Dispose();
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
index d5c76ed4b09..f57fd7651c3 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
@@ -109,9 +109,9 @@ public sealed partial class QuicConnection : IAsyncDisposable
private readonly MsQuicContextSafeHandle _handle;
///
- /// Set to non-zero once disposed. Prevents double and/or concurrent disposal.
+ /// Set to true once disposed. Prevents double and/or concurrent disposal.
///
- private int _disposed;
+ private bool _disposed;
private readonly ValueTaskSource _connectedTcs = new ValueTaskSource();
private readonly ResettableValueTaskSource _shutdownTcs = new ResettableValueTaskSource()
@@ -502,7 +502,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
/// An asynchronous task that completes with the opened .
public async ValueTask OpenOutboundStreamAsync(QuicStreamType type, CancellationToken cancellationToken = default)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
QuicStream? stream = null;
try
@@ -524,7 +524,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
}
// Propagate ODE if disposed in the meantime.
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
// Propagate connection error when the connection was closed (remotely = ABORTED / locally = INVALID_STATE).
if (ex is QuicException qex && qex.QuicError == QuicError.InternalError &&
@@ -544,7 +544,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
/// An asynchronous task that completes with the accepted .
public async ValueTask AcceptInboundStreamAsync(CancellationToken cancellationToken = default)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
if (!_canAccept)
{
@@ -583,7 +583,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
/// An asynchronous task that completes when the connection is closed.
public ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
ThrowHelper.ValidateErrorCode(nameof(errorCode), errorCode, $"{nameof(CloseAsync)}.{nameof(errorCode)}");
if (_shutdownTcs.TryGetValueTask(out ValueTask valueTask, this, cancellationToken))
@@ -645,7 +645,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
// make sure we log at least some secrets in case of shutdown before handshake completes.
_tlsSecret?.WriteSecret();
- Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(_disposed == 1 ? new ObjectDisposedException(GetType().FullName) : ThrowHelper.GetOperationAbortedException());
+ Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(_disposed ? new ObjectDisposedException(GetType().FullName) : ThrowHelper.GetOperationAbortedException());
_connectionCloseTcs.TrySetException(exception);
_acceptQueue.Writer.TryComplete(exception);
_connectedTcs.TrySetException(exception);
@@ -783,7 +783,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
/// A task that represents the asynchronous dispose operation.
public async ValueTask DisposeAsync()
{
- if (Interlocked.Exchange(ref _disposed, 1) != 0)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs
index 7e8acfda32c..92733f4bb39 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs
@@ -74,9 +74,9 @@ public sealed partial class QuicListener : IAsyncDisposable
private readonly MsQuicContextSafeHandle _handle;
///
- /// Set to non-zero once disposed. Prevents double and/or concurrent disposal.
+ /// Set to true once disposed. Prevents double and/or concurrent disposal.
///
- private int _disposed;
+ private bool _disposed;
///
/// Completed when SHUTDOWN_COMPLETE arrives.
@@ -175,7 +175,7 @@ public sealed partial class QuicListener : IAsyncDisposable
/// A task that will contain a fully connected which successfully finished the handshake and is ready to be used.
public async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
GCHandle keepObject = GCHandle.Alloc(this);
try
@@ -405,7 +405,7 @@ public sealed partial class QuicListener : IAsyncDisposable
/// A task that represents the asynchronous dispose operation.
public async ValueTask DisposeAsync()
{
- if (Interlocked.Exchange(ref _disposed, 1) != 0)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.Stream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.Stream.cs
index 8196c59a1c1..42824723a95 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.Stream.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.Stream.cs
@@ -50,12 +50,12 @@ public partial class QuicStream : Stream
{
get
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
return (int)_readTimeout.TotalMilliseconds;
}
set
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
if (value <= 0 && value != Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(value), SR.net_quic_timeout_use_gt_zero);
@@ -69,12 +69,12 @@ public partial class QuicStream : Stream
{
get
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
return (int)_writeTimeout.TotalMilliseconds;
}
set
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
if (value <= 0 && value != Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(value), SR.net_quic_timeout_use_gt_zero);
@@ -86,7 +86,7 @@ public partial class QuicStream : Stream
// Read boilerplate.
///
/// Gets a value indicating whether the supports reading.
- public override bool CanRead => Volatile.Read(ref _disposed) == 0 && _canRead;
+ public override bool CanRead => !Volatile.Read(ref _disposed) && _canRead;
///
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
@@ -113,7 +113,7 @@ public partial class QuicStream : Stream
///
public override int Read(Span buffer)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
byte[] rentedBuffer = ArrayPool.Shared.Rent(buffer.Length);
CancellationTokenSource? cts = null;
@@ -149,7 +149,7 @@ public partial class QuicStream : Stream
// Write boilerplate.
///
/// Gets a value indicating whether the supports writing.
- public override bool CanWrite => Volatile.Read(ref _disposed) == 0 && _canWrite;
+ public override bool CanWrite => !Volatile.Read(ref _disposed) && _canWrite;
///
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
@@ -175,7 +175,7 @@ public partial class QuicStream : Stream
///
public override void Write(ReadOnlySpan buffer)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
CancellationTokenSource? cts = null;
if (_writeTimeout > TimeSpan.Zero)
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs
index 66568b937ed..ce0128e779e 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs
@@ -60,9 +60,9 @@ public sealed partial class QuicStream
private readonly MsQuicContextSafeHandle _handle;
///
- /// Set to non-zero once disposed. Prevents double and/or concurrent disposal.
+ /// Set to true once disposed. Prevents double and/or concurrent disposal.
///
- private int _disposed;
+ private bool _disposed;
private readonly ValueTaskSource _startedTcs = new ValueTaskSource();
private readonly ValueTaskSource _shutdownTcs = new ValueTaskSource();
@@ -272,7 +272,7 @@ public sealed partial class QuicStream
///
public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
if (!_canRead)
{
@@ -359,7 +359,7 @@ public sealed partial class QuicStream
/// Notifies the peer about gracefully closing the write side, i.e.: sends FIN flag with the data.
public ValueTask WriteAsync(ReadOnlyMemory buffer, bool completeWrites, CancellationToken cancellationToken = default)
{
- if (_disposed == 1)
+ if (_disposed)
{
return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedException(nameof(QuicStream))));
}
@@ -451,7 +451,7 @@ public sealed partial class QuicStream
/// The error code with which to abort the stream, this value is application protocol (layer above QUIC) dependent.
public void Abort(QuicAbortDirection abortDirection, long errorCode)
{
- if (_disposed == 1)
+ if (_disposed)
{
return;
}
@@ -510,7 +510,7 @@ public sealed partial class QuicStream
///
public void CompleteWrites()
{
- ObjectDisposedException.ThrowIf(_disposed == 1, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
// Nothing to complete, the writing side is already closed.
if (_sendTcs.IsCompleted)
@@ -712,7 +712,7 @@ public sealed partial class QuicStream
/// A task that represents the asynchronous dispose operation.
public override async ValueTask DisposeAsync()
{
- if (Interlocked.Exchange(ref _disposed, 1) != 0)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
diff --git a/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs b/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs
index fbc32738ef2..ee2466d285a 100644
--- a/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs
+++ b/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs
@@ -51,10 +51,10 @@ namespace System.Net
private static int _defaultMaxResponseHeadersLength = HttpHandlerDefaults.DefaultMaxResponseHeadersLength;
private static int _defaultMaximumErrorResponseLength = -1;
- private int _beginGetRequestStreamCalled;
- private int _beginGetResponseCalled;
- private int _endGetRequestStreamCalled;
- private int _endGetResponseCalled;
+ private bool _beginGetRequestStreamCalled;
+ private bool _beginGetResponseCalled;
+ private bool _endGetRequestStreamCalled;
+ private bool _endGetResponseCalled;
private int _maximumAllowedRedirections = HttpHandlerDefaults.DefaultMaxAutomaticRedirections;
private int _maximumResponseHeadersLen = _defaultMaxResponseHeadersLength;
@@ -74,7 +74,7 @@ namespace System.Net
private TaskCompletionSource? _responseOperation;
private AsyncCallback? _requestStreamCallback;
private AsyncCallback? _responseCallback;
- private int _abortCalled;
+ private volatile bool _abortCalled;
private CancellationTokenSource? _sendRequestCts;
private X509CertificateCollection? _clientCertificates;
private Booleans _booleans = Booleans.Default;
@@ -995,7 +995,7 @@ namespace System.Net
public override void Abort()
{
- if (Interlocked.Exchange(ref _abortCalled, 1) != 0)
+ if (Interlocked.Exchange(ref _abortCalled, true))
{
return;
}
@@ -1125,7 +1125,7 @@ namespace System.Net
{
CheckAbort();
- if (Interlocked.Exchange(ref _beginGetRequestStreamCalled, 1) != 0)
+ if (Interlocked.Exchange(ref _beginGetRequestStreamCalled, true))
{
throw new InvalidOperationException(SR.net_repcall);
}
@@ -1147,7 +1147,7 @@ namespace System.Net
throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
}
- if (Interlocked.Exchange(ref _endGetRequestStreamCalled, 1) != 0)
+ if (Interlocked.Exchange(ref _endGetRequestStreamCalled, true))
{
throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndGetRequestStream"));
}
@@ -1398,7 +1398,7 @@ namespace System.Net
{
CheckAbort();
- if (Interlocked.Exchange(ref _beginGetResponseCalled, 1) != 0)
+ if (Interlocked.Exchange(ref _beginGetResponseCalled, true))
{
throw new InvalidOperationException(SR.net_repcall);
}
@@ -1418,7 +1418,7 @@ namespace System.Net
throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
}
- if (Interlocked.Exchange(ref _endGetResponseCalled, 1) != 0)
+ if (Interlocked.Exchange(ref _endGetResponseCalled, true))
{
throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndGetResponse"));
}
@@ -1565,7 +1565,7 @@ namespace System.Net
private void CheckAbort()
{
- if (Volatile.Read(ref _abortCalled) == 1)
+ if (_abortCalled)
{
throw new WebException(SR.net_reqaborted, WebExceptionStatus.RequestCanceled);
}
diff --git a/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs b/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs
index 3aef8b469fd..4bbfd0f4bb8 100644
--- a/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs
+++ b/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs
@@ -445,14 +445,14 @@ namespace System.Net
{
internal InfiniteTimer() : base(Timeout.Infinite) { }
- private int _cancelled;
+ private bool _canceled;
internal override bool HasExpired => false;
///
/// Cancels the timer. Returns true the first time, false after that.
///
- internal override bool Cancel() => Interlocked.Exchange(ref _cancelled, 1) == 0;
+ internal override bool Cancel() => !Interlocked.Exchange(ref _canceled, true);
}
///
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStream.cs
index 48e3923037c..b0f25d431b2 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStream.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStream.cs
@@ -42,9 +42,9 @@ namespace System.Net.Security
private int _readBufferCount;
private ArrayBufferWriter? _writeBuffer;
- private volatile int _writeInProgress;
- private volatile int _readInProgress;
- private volatile int _authInProgress;
+ private volatile bool _writeInProgress;
+ private volatile bool _readInProgress;
+ private volatile bool _authInProgress;
private ExceptionDispatchInfo? _exception;
private StreamFramer? _framer;
@@ -340,7 +340,7 @@ namespace System.Net.Security
{
Debug.Assert(_context is not null);
- if (Interlocked.Exchange(ref _readInProgress, 1) == 1)
+ if (Interlocked.Exchange(ref _readInProgress, true))
{
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
}
@@ -441,7 +441,7 @@ namespace System.Net.Security
}
finally
{
- _readInProgress = 0;
+ _readInProgress = false;
}
static async ValueTask ReadAllAsync(Stream stream, Memory buffer, bool allowZeroRead, CancellationToken cancellationToken)
@@ -506,7 +506,7 @@ namespace System.Net.Security
Debug.Assert(_context is not null);
Debug.Assert(_writeBuffer is not null);
- if (Interlocked.Exchange(ref _writeInProgress, 1) == 1)
+ if (Interlocked.Exchange(ref _writeInProgress, true))
{
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "write"));
}
@@ -556,7 +556,7 @@ namespace System.Net.Security
finally
{
_writeBuffer.Clear();
- _writeInProgress = 0;
+ _writeInProgress = false;
}
}
@@ -724,7 +724,7 @@ namespace System.Net.Security
Debug.Assert(_context != null);
ThrowIfFailed(authSuccessCheck: false);
- if (Interlocked.Exchange(ref _authInProgress, 1) == 1)
+ if (Interlocked.Exchange(ref _authInProgress, true))
{
throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "authenticate"));
}
@@ -742,7 +742,7 @@ namespace System.Net.Security
}
finally
{
- _authInProgress = 0;
+ _authInProgress = false;
}
}
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs
index 0275b8fa5cf..faf807ff260 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs
@@ -16,7 +16,7 @@ namespace System.Net.Security
public partial class SslStream
{
private readonly SslAuthenticationOptions _sslAuthenticationOptions = new SslAuthenticationOptions();
- private int _nestedAuth;
+ private NestedState _nestedAuth;
private bool _isRenego;
private TlsFrameHelper.TlsFrameInfo _lastFrame;
@@ -32,10 +32,14 @@ namespace System.Net.Security
private bool _receivedEOF;
// Used by Telemetry to ensure we log connection close exactly once
- // 0 = no handshake
- // 1 = handshake completed, connection opened
- // 2 = SslStream disposed, connection closed
- private int _connectionOpenedStatus;
+ private enum ConnectionStatus
+ {
+ NoHandshake = 0,
+ HandshakeCompleted = 1, // connection opened
+ Disposed = 2, // connection closed
+ }
+
+ private ConnectionStatus _connectionOpenedStatus;
private void SetException(Exception e)
{
@@ -56,10 +60,10 @@ namespace System.Net.Security
// Ensure a Read or Auth operation is not in progress,
// block potential future read and auth operations since SslStream is disposing.
- // This leaves the _nestedRead = 2 and _nestedAuth = 2, but that's ok, since
+ // This leaves the _nestedRead = StreamDisposed and _nestedAuth = StreamDisposed, but that's ok, since
// subsequent operations check the _exception sentinel first
- if (Interlocked.Exchange(ref _nestedRead, StreamDisposed) == StreamNotInUse &&
- Interlocked.Exchange(ref _nestedAuth, StreamDisposed) == StreamNotInUse)
+ if (Interlocked.Exchange(ref _nestedRead, NestedState.StreamDisposed) == NestedState.StreamNotInUse &&
+ Interlocked.Exchange(ref _nestedAuth, NestedState.StreamDisposed) == NestedState.StreamNotInUse)
{
_buffer.ReturnBuffer();
}
@@ -73,7 +77,7 @@ namespace System.Net.Security
if (NetSecurityTelemetry.Log.IsEnabled())
{
// Set the status to disposed. If it was opened before, log ConnectionClosed
- if (Interlocked.Exchange(ref _connectionOpenedStatus, 2) == 1)
+ if (Interlocked.Exchange(ref _connectionOpenedStatus, ConnectionStatus.Disposed) == ConnectionStatus.HandshakeCompleted)
{
NetSecurityTelemetry.Log.ConnectionClosed(GetSslProtocolInternal());
}
@@ -143,9 +147,9 @@ namespace System.Net.Security
if (startingTimestamp is not 0)
{
- // SslStream could already have been disposed at this point, in which case _connectionOpenedStatus == 2
+ // SslStream could already have been disposed at this point, in which case _connectionOpenedStatus == ConnectionStatus.Disposed
// Make sure that we increment the open connection counter only if it is guaranteed to be decremented in dispose/finalize
- bool connectionOpen = Interlocked.CompareExchange(ref _connectionOpenedStatus, 1, 0) == 0;
+ bool connectionOpen = Interlocked.CompareExchange(ref _connectionOpenedStatus, ConnectionStatus.HandshakeCompleted, ConnectionStatus.NoHandshake) == ConnectionStatus.NoHandshake;
SslProtocols protocol = GetSslProtocolInternal();
NetSecurityTelemetry.Log.HandshakeCompleted(protocol, startingTimestamp, connectionOpen);
}
@@ -187,22 +191,22 @@ namespace System.Net.Security
private async Task RenegotiateAsync(CancellationToken cancellationToken)
where TIOAdapter : IReadWriteAdapter
{
- if (Interlocked.CompareExchange(ref _nestedAuth, StreamInUse, StreamNotInUse) != StreamNotInUse)
+ if (Interlocked.CompareExchange(ref _nestedAuth, NestedState.StreamInUse, NestedState.StreamNotInUse) != NestedState.StreamNotInUse)
{
- ObjectDisposedException.ThrowIf(_nestedAuth == StreamDisposed, this);
+ ObjectDisposedException.ThrowIf(_nestedAuth == NestedState.StreamDisposed, this);
throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "authenticate"));
}
- if (Interlocked.CompareExchange(ref _nestedRead, StreamInUse, StreamNotInUse) != StreamNotInUse)
+ if (Interlocked.CompareExchange(ref _nestedRead, NestedState.StreamInUse, NestedState.StreamNotInUse) != NestedState.StreamNotInUse)
{
- ObjectDisposedException.ThrowIf(_nestedRead == StreamDisposed, this);
+ ObjectDisposedException.ThrowIf(_nestedRead == NestedState.StreamDisposed, this);
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
}
// Write is different since we do not do anything special in Dispose
- if (Interlocked.Exchange(ref _nestedWrite, StreamInUse) != StreamNotInUse)
+ if (Interlocked.Exchange(ref _nestedWrite, NestedState.StreamInUse) != NestedState.StreamNotInUse)
{
- _nestedRead = StreamNotInUse;
+ _nestedRead = NestedState.StreamNotInUse;
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "write"));
}
@@ -265,8 +269,8 @@ namespace System.Net.Security
token.ReleasePayload();
- _nestedRead = StreamNotInUse;
- _nestedWrite = StreamNotInUse;
+ _nestedRead = NestedState.StreamNotInUse;
+ _nestedWrite = NestedState.StreamNotInUse;
_isRenego = false;
// We will not release _nestedAuth at this point to prevent another renegotiation attempt.
}
@@ -284,7 +288,7 @@ namespace System.Net.Security
if (reAuthenticationData == null)
{
// prevent nesting only when authentication functions are called explicitly. e.g. handle renegotiation transparently.
- if (Interlocked.Exchange(ref _nestedAuth, StreamInUse) == StreamInUse)
+ if (Interlocked.Exchange(ref _nestedAuth, NestedState.StreamInUse) == NestedState.StreamInUse)
{
throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "authenticate"));
}
@@ -378,7 +382,7 @@ namespace System.Net.Security
{
if (reAuthenticationData == null)
{
- _nestedAuth = StreamNotInUse;
+ _nestedAuth = NestedState.StreamNotInUse;
_isRenego = false;
}
@@ -547,7 +551,7 @@ namespace System.Net.Security
{
ProcessHandshakeSuccess();
- if (_nestedAuth != StreamInUse)
+ if (_nestedAuth != NestedState.StreamInUse)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Ignoring unsolicited renegotiated certificate.");
// ignore certificates received outside of handshake or requested renegotiation.
@@ -797,7 +801,7 @@ namespace System.Net.Security
// If that happen before EncryptData() runs, _handshakeWaiter will be set to null
// and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData()
- if (_sslAuthenticationOptions.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0)
+ if (_sslAuthenticationOptions.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != NestedState.StreamNotInUse)
{
// create TCS only if we plan to proceed. If not, we will throw later outside of the lock.
// Tls1.3 does not have renegotiation. However on Windows this error code is used
@@ -820,9 +824,9 @@ namespace System.Net.Security
// Check for disposal is not atomic so we will check again below.
ThrowIfExceptionalOrNotAuthenticated();
- if (Interlocked.CompareExchange(ref _nestedRead, StreamInUse, StreamNotInUse) != StreamNotInUse)
+ if (Interlocked.CompareExchange(ref _nestedRead, NestedState.StreamInUse, NestedState.StreamNotInUse) != NestedState.StreamNotInUse)
{
- ObjectDisposedException.ThrowIf(_nestedRead == StreamDisposed, this);
+ ObjectDisposedException.ThrowIf(_nestedRead == NestedState.StreamDisposed, this);
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
}
@@ -947,7 +951,7 @@ namespace System.Net.Security
finally
{
ReturnReadBufferIfEmpty();
- _nestedRead = StreamNotInUse;
+ _nestedRead = NestedState.StreamNotInUse;
}
}
@@ -962,7 +966,7 @@ namespace System.Net.Security
return;
}
- if (Interlocked.Exchange(ref _nestedWrite, StreamInUse) == StreamInUse)
+ if (Interlocked.Exchange(ref _nestedWrite, NestedState.StreamInUse) == NestedState.StreamInUse)
{
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "write"));
}
@@ -985,7 +989,7 @@ namespace System.Net.Security
}
finally
{
- _nestedWrite = StreamNotInUse;
+ _nestedWrite = NestedState.StreamNotInUse;
}
}
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs
index 5b991106dbc..47ee34664a4 100644
--- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs
@@ -167,13 +167,15 @@ namespace System.Net.Security
}
}
- // used to track ussage in _nested* variables bellow
- private const int StreamNotInUse = 0;
- private const int StreamInUse = 1;
- private const int StreamDisposed = 2;
+ private enum NestedState
+ {
+ StreamNotInUse = 0,
+ StreamInUse = 1,
+ StreamDisposed = 2,
+ }
- private int _nestedWrite;
- private int _nestedRead;
+ private NestedState _nestedWrite;
+ private NestedState _nestedRead;
private PoolingPointerMemoryManager? _readPointerMemoryManager;
private PoolingPointerMemoryManager? _writePointerMemoryManager;
@@ -735,7 +737,7 @@ namespace System.Net.Security
public override int ReadByte()
{
ThrowIfExceptionalOrNotAuthenticated();
- if (Interlocked.Exchange(ref _nestedRead, StreamInUse) == StreamInUse)
+ if (Interlocked.Exchange(ref _nestedRead, NestedState.StreamInUse) == NestedState.StreamInUse)
{
throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
}
@@ -756,7 +758,7 @@ namespace System.Net.Security
// Regardless of whether we were able to read a byte from the buffer,
// reset the read tracking. If we weren't able to read a byte, the
// subsequent call to Read will set the flag again.
- _nestedRead = StreamNotInUse;
+ _nestedRead = NestedState.StreamNotInUse;
}
// Otherwise, fall back to reading a byte via Read, the same way Stream.ReadByte does.
diff --git a/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs b/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs
index 3ad6be92039..31346cc9715 100644
--- a/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs
+++ b/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs
@@ -54,12 +54,12 @@ namespace System.Net.Security
// Without setting (or using) these members you will get a build exception in the unit test project.
// The code that normally uses these in the main solution is in the implementation of SslStream.
- if (_nestedWrite == 0)
+ if (_nestedWrite == NestedState.StreamNotInUse)
{
}
_exception = null;
- _nestedWrite = 0;
+ _nestedWrite = NestedState.StreamNotInUse;
_handshakeCompleted = false;
}
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs
index 9cb523300e8..4b94be9b1df 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs
@@ -23,8 +23,8 @@ namespace System.Net.Sockets
// Used by the class to indicate that the stream is writable.
private bool _writeable;
- // Whether Dispose has been called. 0 == false, 1 == true
- private int _disposed;
+ // Whether Dispose has been called.
+ private bool _disposed;
// Creates a new instance of the System.Net.Sockets.NetworkStream class for the specified System.Net.Sockets.Socket.
public NetworkStream(Socket socket)
@@ -369,7 +369,7 @@ namespace System.Net.Sockets
protected override void Dispose(bool disposing)
{
- if (Interlocked.Exchange(ref _disposed, 1) != 0)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
@@ -685,7 +685,7 @@ namespace System.Net.Sockets
private void ThrowIfDisposed()
{
- ObjectDisposedException.ThrowIf(_disposed != 0, this);
+ ObjectDisposedException.ThrowIf(_disposed, this);
}
private static IOException WrapException(string resourceFormatString, Exception innerException)
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.cs
index f427aa88cc1..7f653dab759 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.cs
@@ -28,7 +28,7 @@ namespace System.Net.Sockets
private int _closeSocketThread;
private int _closeSocketTick;
#endif
- private int _ownClose;
+ private bool _ownClose;
///
/// Creates a .
@@ -54,8 +54,7 @@ namespace System.Net.Sockets
internal bool HasShutdownSend => _hasShutdownSend;
- private bool TryOwnClose()
- => Interlocked.CompareExchange(ref _ownClose, 1, 0) == 0;
+ private bool TryOwnClose() => !Interlocked.Exchange(ref _ownClose, true);
private volatile bool _released;
private bool _hasShutdownSend;
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs
index 73ff6521aa3..3e4650f547c 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs
@@ -148,7 +148,7 @@ namespace System.Net.Sockets
return errorCode;
}
- if (Volatile.Read(ref _disposed) != 0)
+ if (Volatile.Read(ref _disposed))
{
_handle.Dispose();
throw new ObjectDisposedException(GetType().FullName);
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
index 02edb8853f9..e1f19c03d6d 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
@@ -67,7 +67,7 @@ namespace System.Net.Sockets
private bool _receivingPacketInformation;
private int _closeTimeout = Socket.DefaultCloseTimeout;
- private int _disposed; // 0 == false, anything else == true
+ private bool _disposed;
public Socket(SocketType socketType, ProtocolType protocolType)
: this(OSSupportsIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, socketType, protocolType)
@@ -3148,7 +3148,7 @@ namespace System.Net.Sockets
// Internal and private properties
//
- internal bool Disposed => _disposed != 0;
+ internal bool Disposed => _disposed;
//
// Internal and private methods
@@ -3240,7 +3240,7 @@ namespace System.Net.Sockets
}
// Make sure we're the first call to Dispose
- if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1)
+ if (Interlocked.Exchange(ref _disposed, true))
{
return;
}
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs
index dd50676ac55..d5613dc91f4 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs
@@ -119,10 +119,10 @@ namespace System.Net.Sockets
Canceled
}
- private int _state; // Actually AsyncOperation.State.
+ private volatile AsyncOperation.State _state;
#if DEBUG
- private int _callbackQueued; // When non-zero, the callback has been queued.
+ private bool _callbackQueued; // When true, the callback has been queued.
#endif
public readonly SocketAsyncContext AssociatedContext;
@@ -141,11 +141,11 @@ namespace System.Net.Sockets
public void Reset()
{
- _state = (int)State.Waiting;
+ _state = State.Waiting;
Event = null;
Next = this;
#if DEBUG
- _callbackQueued = 0;
+ _callbackQueued = false;
#endif
}
@@ -154,34 +154,34 @@ namespace System.Net.Sockets
TraceWithContext(context, "Enter");
// Set state to Running, unless we've been canceled
- int oldState = Interlocked.CompareExchange(ref _state, (int)State.Running, (int)State.Waiting);
- if (oldState == (int)State.Canceled)
+ State oldState = Interlocked.CompareExchange(ref _state, State.Running, State.Waiting);
+ if (oldState == State.Canceled)
{
TraceWithContext(context, "Exit, Previously canceled");
return OperationResult.Cancelled;
}
- Debug.Assert(oldState == (int)State.Waiting, $"Unexpected operation state: {(State)oldState}");
+ Debug.Assert(oldState == State.Waiting, $"Unexpected operation state: {(State)oldState}");
// Try to perform the IO
if (DoTryComplete(context))
{
- Debug.Assert((State)Volatile.Read(ref _state) is State.Running or State.RunningWithPendingCancellation, "Unexpected operation state");
+ Debug.Assert(_state is State.Running or State.RunningWithPendingCancellation, "Unexpected operation state");
- Volatile.Write(ref _state, (int)State.Complete);
+ _state = State.Complete;
TraceWithContext(context, "Exit, Completed");
return OperationResult.Completed;
}
// Set state back to Waiting, unless we were canceled, in which case we have to process cancellation now
- int newState;
+ State newState;
while (true)
{
- int state = Volatile.Read(ref _state);
- Debug.Assert(state is (int)State.Running or (int)State.RunningWithPendingCancellation, $"Unexpected operation state: {(State)state}");
+ State state = _state;
+ Debug.Assert(state is State.Running or State.RunningWithPendingCancellation, $"Unexpected operation state: {(State)state}");
- newState = (state == (int)State.Running ? (int)State.Waiting : (int)State.Canceled);
+ newState = (state == State.Running ? State.Waiting : State.Canceled);
if (state == Interlocked.CompareExchange(ref _state, newState, state))
{
break;
@@ -190,7 +190,7 @@ namespace System.Net.Sockets
// Race to update the state. Loop and try again.
}
- if (newState == (int)State.Canceled)
+ if (newState == State.Canceled)
{
ProcessCancellation();
TraceWithContext(context, "Exit, Newly cancelled");
@@ -208,16 +208,16 @@ namespace System.Net.Sockets
// Note we could be cancelling because of socket close. Regardless, we don't need the registration anymore.
CancellationRegistration.Dispose();
- int newState;
+ State newState;
while (true)
{
- int state = Volatile.Read(ref _state);
- if (state is (int)State.Complete or (int)State.Canceled or (int)State.RunningWithPendingCancellation)
+ State state = _state;
+ if (state is State.Complete or State.Canceled or State.RunningWithPendingCancellation)
{
return false;
}
- newState = (state == (int)State.Waiting ? (int)State.Canceled : (int)State.RunningWithPendingCancellation);
+ newState = (state == State.Waiting ? State.Canceled : State.RunningWithPendingCancellation);
if (state == Interlocked.CompareExchange(ref _state, newState, state))
{
break;
@@ -226,7 +226,7 @@ namespace System.Net.Sockets
// Race to update the state. Loop and try again.
}
- if (newState == (int)State.RunningWithPendingCancellation)
+ if (newState == State.RunningWithPendingCancellation)
{
// TryComplete will either succeed, or it will see the pending cancellation and deal with it.
return false;
@@ -243,7 +243,7 @@ namespace System.Net.Sockets
{
Trace("Enter");
- Debug.Assert(_state == (int)State.Canceled);
+ Debug.Assert(_state == State.Canceled);
ErrorCode = SocketError.OperationAborted;
@@ -255,7 +255,7 @@ namespace System.Net.Sockets
else
{
#if DEBUG
- Debug.Assert(Interlocked.CompareExchange(ref _callbackQueued, 1, 0) == 0, $"Unexpected _callbackQueued: {_callbackQueued}");
+ Debug.Assert(!Interlocked.Exchange(ref _callbackQueued, true), $"Unexpected _callbackQueued: {_callbackQueued}");
#endif
// We've marked the operation as canceled, and so should invoke the callback, but
// we can't pool the object, as ProcessQueue may still have a reference to it, due to
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs
index f14c6753e93..43364203118 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs
@@ -103,7 +103,7 @@ namespace System.Net.Sockets
Scheduled
}
- private int _eventQueueProcessingStage;
+ private EventQueueProcessingStage _eventQueueProcessingStage;
//
// Registers the Socket with a SocketAsyncEngine, and returns the associated engine.
@@ -206,7 +206,7 @@ namespace System.Net.Sockets
if (handler.HandleSocketEvents(numEvents) &&
Interlocked.Exchange(
ref _eventQueueProcessingStage,
- (int)EventQueueProcessingStage.Scheduled) == (int)EventQueueProcessingStage.NotScheduled)
+ EventQueueProcessingStage.Scheduled) == EventQueueProcessingStage.NotScheduled)
{
ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false);
}
@@ -223,20 +223,20 @@ namespace System.Net.Sockets
if (!isEventQueueEmpty)
{
// There are more events to process, set stage to Scheduled and enqueue a work item.
- _eventQueueProcessingStage = (int)EventQueueProcessingStage.Scheduled;
+ _eventQueueProcessingStage = EventQueueProcessingStage.Scheduled;
}
else
{
// The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining
// otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer
// would not have scheduled a work item to process the work, so schedule one now.
- int stageBeforeUpdate =
+ EventQueueProcessingStage stageBeforeUpdate =
Interlocked.CompareExchange(
ref _eventQueueProcessingStage,
- (int)EventQueueProcessingStage.NotScheduled,
- (int)EventQueueProcessingStage.Determining);
- Debug.Assert(stageBeforeUpdate != (int)EventQueueProcessingStage.NotScheduled);
- if (stageBeforeUpdate == (int)EventQueueProcessingStage.Determining)
+ EventQueueProcessingStage.NotScheduled,
+ EventQueueProcessingStage.Determining);
+ Debug.Assert(stageBeforeUpdate != EventQueueProcessingStage.NotScheduled);
+ if (stageBeforeUpdate == EventQueueProcessingStage.Determining)
{
return;
}
@@ -251,14 +251,14 @@ namespace System.Net.Sockets
SocketIOEvent ev;
while (true)
{
- Debug.Assert(_eventQueueProcessingStage == (int)EventQueueProcessingStage.Scheduled);
+ Debug.Assert(_eventQueueProcessingStage == EventQueueProcessingStage.Scheduled);
// The change needs to be visible to other threads that may request a worker thread before a work item is attempted
// to be dequeued by the current thread. In particular, if an enqueuer queues a work item and does not request a
// thread because it sees a Determining or Scheduled stage, and the current thread is the last thread processing
// work items, the current thread must either see the work item queued by the enqueuer, or it must see a stage of
// Scheduled, and try to dequeue again or request another thread.
- _eventQueueProcessingStage = (int)EventQueueProcessingStage.Determining;
+ _eventQueueProcessingStage = EventQueueProcessingStage.Determining;
Interlocked.MemoryBarrier();
if (eventQueue.TryDequeue(out ev))
@@ -269,13 +269,13 @@ namespace System.Net.Sockets
// The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining
// otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer
// would not have scheduled a work item to process the work, so try to dequeue a work item again.
- int stageBeforeUpdate =
+ EventQueueProcessingStage stageBeforeUpdate =
Interlocked.CompareExchange(
ref _eventQueueProcessingStage,
- (int)EventQueueProcessingStage.NotScheduled,
- (int)EventQueueProcessingStage.Determining);
- Debug.Assert(stageBeforeUpdate != (int)EventQueueProcessingStage.NotScheduled);
- if (stageBeforeUpdate == (int)EventQueueProcessingStage.Determining)
+ EventQueueProcessingStage.NotScheduled,
+ EventQueueProcessingStage.Determining);
+ Debug.Assert(stageBeforeUpdate != EventQueueProcessingStage.NotScheduled);
+ if (stageBeforeUpdate == EventQueueProcessingStage.Determining)
{
return;
}
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
index 19aa0fa8ab4..c98d0cede11 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
@@ -100,7 +100,7 @@ namespace System.Net.Sockets
private unsafe NativeOverlapped* AllocateNativeOverlapped()
{
Debug.Assert(OperatingSystem.IsWindows());
- Debug.Assert(_operating == InProgress, $"Expected {nameof(_operating)} == {nameof(InProgress)}, got {_operating}");
+ Debug.Assert(_operating == OperationState.InProgress, $"Expected {nameof(_operating)} == {nameof(OperationState.InProgress)}, got {_operating}");
Debug.Assert(_currentSocket != null, "_currentSocket is null");
Debug.Assert(_currentSocket.SafeHandle != null, "_currentSocket.SafeHandle is null");
Debug.Assert(_preAllocatedOverlapped != null, "_preAllocatedOverlapped is null");
@@ -113,7 +113,7 @@ namespace System.Net.Sockets
{
Debug.Assert(OperatingSystem.IsWindows());
Debug.Assert(overlapped != null, "overlapped is null");
- Debug.Assert(_operating == InProgress, $"Expected _operating == InProgress, got {_operating}");
+ Debug.Assert(_operating == OperationState.InProgress, $"Expected _operating == OperationState.InProgress, got {_operating}");
Debug.Assert(_currentSocket != null, "_currentSocket is null");
Debug.Assert(_currentSocket.SafeHandle != null, "_currentSocket.SafeHandle is null");
Debug.Assert(_currentSocket.SafeHandle.IOCPBoundHandle != null, "_currentSocket.SafeHandle.IOCPBoundHandle is null");
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
index 158d56182aa..b635d27da44 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
@@ -77,12 +77,14 @@ namespace System.Net.Sockets
private bool _userSocket; // if false when performing Connect, _currentSocket should be disposed
private bool _disposeCalled;
- // Controls thread safety via Interlocked.
- private const int Configuring = -1;
- private const int Free = 0;
- private const int InProgress = 1;
- private const int Disposed = 2;
- private int _operating;
+ private enum OperationState
+ {
+ Configuring = -1,
+ Free = 0,
+ InProgress = 1,
+ Disposed = 2,
+ }
+ private OperationState _operating;
private CancellationTokenSource? _multipleConnectCancellation;
@@ -480,7 +482,7 @@ namespace System.Net.Sockets
_context = null;
// Mark as not in-use.
- _operating = Free;
+ _operating = OperationState.Free;
// Check for deferred Dispose().
// The deferred Dispose is not guaranteed if Dispose is called while an operation is in progress.
@@ -498,7 +500,7 @@ namespace System.Net.Sockets
_disposeCalled = true;
// Check if this object is in-use for an async socket operation.
- if (Interlocked.CompareExchange(ref _operating, Disposed, Free) != Free)
+ if (Interlocked.CompareExchange(ref _operating, OperationState.Disposed, OperationState.Free) != OperationState.Free)
{
// Either already disposed or will be disposed when current operation completes.
return;
@@ -525,17 +527,17 @@ namespace System.Net.Sockets
// NOTE: Use a try/finally to make sure Complete is called when you're done
private void StartConfiguring()
{
- int status = Interlocked.CompareExchange(ref _operating, Configuring, Free);
- if (status != Free)
+ OperationState status = Interlocked.CompareExchange(ref _operating, OperationState.Configuring, OperationState.Free);
+ if (status != OperationState.Free)
{
ThrowForNonFreeStatus(status);
}
}
- private void ThrowForNonFreeStatus(int status)
+ private void ThrowForNonFreeStatus(OperationState status)
{
- Debug.Assert(status == InProgress || status == Configuring || status == Disposed, $"Unexpected status: {status}");
- ObjectDisposedException.ThrowIf(status == Disposed, this);
+ Debug.Assert(status == OperationState.InProgress || status == OperationState.Configuring || status == OperationState.Disposed, $"Unexpected status: {status}");
+ ObjectDisposedException.ThrowIf(status == OperationState.Disposed, this);
throw new InvalidOperationException(SR.net_socketopinprogress);
}
@@ -544,8 +546,8 @@ namespace System.Net.Sockets
internal void StartOperationCommon(Socket? socket, SocketAsyncOperation operation)
{
// Change status to "in-use".
- int status = Interlocked.CompareExchange(ref _operating, InProgress, Free);
- if (status != Free)
+ OperationState status = Interlocked.CompareExchange(ref _operating, OperationState.InProgress, OperationState.Free);
+ if (status != OperationState.Free)
{
ThrowForNonFreeStatus(status);
}
@@ -606,7 +608,7 @@ namespace System.Net.Sockets
internal void CancelConnectAsync()
{
- if (_operating == InProgress && _completedOperation == SocketAsyncOperation.Connect)
+ if (_operating == OperationState.InProgress && _completedOperation == SocketAsyncOperation.Connect)
{
CancellationTokenSource? multipleConnectCancellation = _multipleConnectCancellation;
if (multipleConnectCancellation != null)
@@ -865,7 +867,7 @@ namespace System.Net.Sockets
private sealed class MultiConnectSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource
{
private ManualResetValueTaskSourceCore _mrvtsc;
- private int _isCompleted;
+ private bool _isCompleted;
public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false) { }
@@ -878,7 +880,7 @@ namespace System.Net.Sockets
protected override void OnCompleted(SocketAsyncEventArgs e) => _mrvtsc.SetResult(true);
- public bool ReachedCoordinationPointFirst() => Interlocked.Exchange(ref _isCompleted, 1) == 0;
+ public bool ReachedCoordinationPointFirst() => !Interlocked.Exchange(ref _isCompleted, true);
}
internal void FinishOperationSyncSuccess(int bytesTransferred, SocketFlags flags)
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs
index 4d60b197f7a..54998d3e02e 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs
@@ -17,10 +17,10 @@ namespace System.Net.Sockets
private AddressFamily _family;
private Socket _clientSocket = null!; // initialized by helper called from ctor
private NetworkStream? _dataStream;
- private volatile int _disposed;
+ private volatile bool _disposed;
private bool _active;
- private bool Disposed => _disposed != 0;
+ private bool Disposed => _disposed;
// Initializes a new instance of the System.Net.Sockets.TcpClient class.
public TcpClient() : this(AddressFamily.Unknown)
@@ -252,7 +252,7 @@ namespace System.Net.Sockets
// Disposes the Tcp connection.
protected virtual void Dispose(bool disposing)
{
- if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
+ if (!Interlocked.Exchange(ref _disposed, true))
{
if (disposing)
{
diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs
index 8716439eac8..b8657321176 100644
--- a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs
+++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs
@@ -11,7 +11,7 @@ namespace System.Net
{
private static volatile string? s_domainName;
private static volatile IPAddress[]? s_localAddresses;
- private static int s_networkChangeRegistered;
+ private static bool s_networkChangeRegistered;
private static bool IsLocal(Uri host)
{
@@ -47,13 +47,13 @@ namespace System.Net
/// Ensures we've registered with NetworkChange to clear out statically-cached state upon a network change notification.
private static void EnsureNetworkChangeRegistration()
{
- if (s_networkChangeRegistered == 0)
+ if (!s_networkChangeRegistered)
{
Register();
static void Register()
{
- if (Interlocked.Exchange(ref s_networkChangeRegistered, 1) != 0)
+ if (Interlocked.Exchange(ref s_networkChangeRegistered, true))
{
return;
}
diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs
index 5780555470c..37b5565b746 100644
--- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs
+++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs
@@ -12,12 +12,12 @@ namespace System.Net.WebSockets
public sealed partial class ClientWebSocket : WebSocket
{
/// This is really an InternalState value, but Interlocked doesn't support operations on values of enum types.
- private int _state;
+ private InternalState _state;
private WebSocketHandle? _innerWebSocket;
public ClientWebSocket()
{
- _state = (int)InternalState.Created;
+ _state = InternalState.Created;
Options = WebSocketHandle.CreateDefaultOptions();
}
@@ -39,14 +39,14 @@ namespace System.Net.WebSockets
return _innerWebSocket.State;
}
- switch ((InternalState)_state)
+ switch (_state)
{
case InternalState.Created:
return WebSocketState.None;
case InternalState.Connecting:
return WebSocketState.Connecting;
default: // We only get here if disposed before connecting
- Debug.Assert((InternalState)_state == InternalState.Disposed);
+ Debug.Assert(_state == InternalState.Disposed);
return WebSocketState.Closed;
}
}
@@ -105,7 +105,7 @@ namespace System.Net.WebSockets
}
// Check that we have not started already.
- switch ((InternalState)Interlocked.CompareExchange(ref _state, (int)InternalState.Connecting, (int)InternalState.Created))
+ switch (Interlocked.CompareExchange(ref _state, InternalState.Connecting, InternalState.Created))
{
case InternalState.Disposed:
throw new ObjectDisposedException(GetType().FullName);
@@ -135,9 +135,9 @@ namespace System.Net.WebSockets
throw;
}
- if ((InternalState)Interlocked.CompareExchange(ref _state, (int)InternalState.Connected, (int)InternalState.Connecting) != InternalState.Connecting)
+ if (Interlocked.CompareExchange(ref _state, InternalState.Connected, InternalState.Connecting) != InternalState.Connecting)
{
- Debug.Assert(_state == (int)InternalState.Disposed);
+ Debug.Assert(_state == InternalState.Disposed);
throw new ObjectDisposedException(GetType().FullName);
}
}
@@ -167,9 +167,9 @@ namespace System.Net.WebSockets
{
get
{
- ObjectDisposedException.ThrowIf((InternalState)_state == InternalState.Disposed, this);
+ ObjectDisposedException.ThrowIf(_state == InternalState.Disposed, this);
- if ((InternalState)_state != InternalState.Connected)
+ if (_state != InternalState.Connected)
{
throw new InvalidOperationException(SR.net_WebSockets_NotConnected);
}
@@ -183,7 +183,7 @@ namespace System.Net.WebSockets
public override void Abort()
{
- if ((InternalState)_state != InternalState.Disposed)
+ if (_state != InternalState.Disposed)
{
_innerWebSocket?.Abort();
Dispose();
@@ -192,7 +192,7 @@ namespace System.Net.WebSockets
public override void Dispose()
{
- if ((InternalState)Interlocked.Exchange(ref _state, (int)InternalState.Disposed) != InternalState.Disposed)
+ if (Interlocked.Exchange(ref _state, InternalState.Disposed) != InternalState.Disposed)
{
_innerWebSocket?.Dispose();
}
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index ad6476701c3..7fbe6aa0963 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -4328,10 +4328,13 @@
Emitting debug info is not supported for this member.
+
+ The specified type must be a reference type, a primitive type, or an enum type.
+
The field is invalid for initializing array or span.
Only array or span of primitive or enum types can be initialized from static data.
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs
index 3fa044d074d..8c6ffe9b61c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs
@@ -35,7 +35,7 @@ namespace System.Buffers
///
private readonly SharedArrayPoolPartitions?[] _buckets = new SharedArrayPoolPartitions[NumBuckets];
/// Whether the callback to trim arrays in response to memory pressure has been created.
- private int _trimCallbackCreated;
+ private bool _trimCallbackCreated;
/// Allocate a new and try to store it into the array.
private unsafe SharedArrayPoolPartitions CreatePerCorePartitions(int bucketIndex)
@@ -283,7 +283,7 @@ namespace System.Buffers
t_tlsBuckets = tlsBuckets;
_allTlsBuckets.Add(tlsBuckets, null);
- if (Interlocked.Exchange(ref _trimCallbackCreated, 1) == 0)
+ if (!Interlocked.Exchange(ref _trimCallbackCreated, true))
{
Gen2GcCallback.Register(s => ((SharedArrayPool)s).Trim(), this);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs
index 6bba8725c84..8700c8bd52c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs
@@ -165,7 +165,7 @@ namespace System.Diagnostics.Tracing
ActivityInfo? orphan = currentActivity;
while (orphan != activityToStop && orphan != null)
{
- if (orphan.m_stopped != 0) // Skip dead activities.
+ if (orphan.m_stopped) // Skip dead activities.
{
orphan = orphan.m_creator;
continue;
@@ -177,14 +177,13 @@ namespace System.Diagnostics.Tracing
}
else
{
- orphan.m_stopped = 1;
- Debug.Assert(orphan.m_stopped != 0);
+ orphan.m_stopped = true;
}
orphan = orphan.m_creator;
}
// try to Stop the activity atomically. Other threads may be trying to do this as well.
- if (Interlocked.CompareExchange(ref activityToStop.m_stopped, 1, 0) == 0)
+ if (!Interlocked.Exchange(ref activityToStop.m_stopped, true))
{
// I succeeded stopping this activity. Now we update our m_current pointer
@@ -239,7 +238,7 @@ namespace System.Diagnostics.Tracing
ActivityInfo? activity = startLocation;
while (activity != null)
{
- if (name == activity.m_name && activity.m_stopped == 0)
+ if (name == activity.m_name && !activity.m_stopped)
return activity;
activity = activity.m_creator;
}
@@ -309,7 +308,7 @@ namespace System.Diagnostics.Tracing
public override string ToString()
{
- return m_name + "(" + Path(this) + (m_stopped != 0 ? ",DEAD)" : ")");
+ return m_name + "(" + Path(this) + (m_stopped ? ",DEAD)" : ")");
}
public static string LiveActivities(ActivityInfo? list)
@@ -520,7 +519,7 @@ namespace System.Diagnostics.Tracing
internal readonly int m_level; // current depth of the Path() of the activity (used to keep recursion under control)
internal readonly EventActivityOptions m_eventOptions; // Options passed to start.
internal long m_lastChildID; // used to create a unique ID for my children activities
- internal int m_stopped; // This work item has stopped
+ internal bool m_stopped; // This work item has stopped
internal readonly ActivityInfo? m_creator; // My parent (creator). Forms the Path() for the activity.
internal readonly Guid m_activityIdToRestore; // The Guid to restore after a stop.
#endregion
@@ -578,7 +577,7 @@ namespace System.Diagnostics.Tracing
while (cur != null)
{
// We found a live activity (typically the first time), set it to that.
- if (cur.m_stopped == 0)
+ if (!cur.m_stopped)
{
EventSource.SetCurrentThreadActivityId(cur.ActivityId);
return;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs
index f1358083a22..26c2eb70785 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs
@@ -25,7 +25,7 @@ namespace System.Threading
public class CancellationTokenSource : IDisposable
{
/// A that's already canceled.
- internal static readonly CancellationTokenSource s_canceledSource = new CancellationTokenSource() { _state = NotifyingCompleteState };
+ internal static readonly CancellationTokenSource s_canceledSource = new CancellationTokenSource() { _state = States.NotifyingCompleteState };
/// A that's never canceled. This isn't enforced programmatically, only by usage. Do not cancel!
internal static readonly CancellationTokenSource s_neverCanceledSource = new CancellationTokenSource();
@@ -35,7 +35,7 @@ namespace System.Threading
((CancellationTokenSource)state!).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel()
/// The current state of the CancellationTokenSource.
- private volatile int _state;
+ private volatile States _state;
/// Whether this has been disposed.
private bool _disposed;
/// ITimer used by CancelAfter and Timer-related ctors. Used instead of Timer to avoid extra allocations and because the rooted behavior is desired.
@@ -46,10 +46,13 @@ namespace System.Threading
/// Lazily-initialized, also serving as the lock to protect its contained state.
private Registrations? _registrations;
- // legal values for _state
- private const int NotCanceledState = 0; // default value of _state
- private const int NotifyingState = 1;
- private const int NotifyingCompleteState = 2;
+ /// Legal values for .
+ private enum States
+ {
+ NotCanceledState = 0, // default value of _state
+ NotifyingState = 1,
+ NotifyingCompleteState = 2,
+ }
/// Gets whether cancellation has been requested for this .
/// Whether cancellation has been requested for this .
@@ -66,10 +69,10 @@ namespace System.Threading
/// canceled concurrently.
///
///
- public bool IsCancellationRequested => _state != NotCanceledState;
+ public bool IsCancellationRequested => _state != States.NotCanceledState;
/// A simple helper to determine whether cancellation has finished.
- internal bool IsCancellationCompleted => _state == NotifyingCompleteState;
+ internal bool IsCancellationCompleted => _state == States.NotifyingCompleteState;
/// Gets the associated with this .
/// The associated with this .
@@ -201,7 +204,7 @@ namespace System.Threading
{
if (millisecondsDelay == TimeSpan.Zero)
{
- _state = NotifyingCompleteState;
+ _state = States.NotifyingCompleteState;
}
else
{
@@ -480,7 +483,7 @@ namespace System.Threading
// We can only reset if cancellation has not yet been requested: we never want to allow a CancellationToken
// to transition from canceled to non-canceled.
- if (_state == NotCanceledState)
+ if (_state == States.NotCanceledState)
{
// If there is no timer, then we're free to reset. If there is a timer, then we need to first try
// to reset it to be infinite so that it won't fire, and then recognize that it could have already
@@ -556,7 +559,7 @@ namespace System.Threading
if (_kernelEvent != null)
{
ManualResetEvent? mre = Interlocked.Exchange(ref _kernelEvent!, null);
- if (mre != null && _state != NotifyingState)
+ if (mre != null && _state != States.NotifyingState)
{
mre.Dispose();
}
@@ -698,13 +701,13 @@ namespace System.Threading
}
}
- /// Transitions from to .
+ /// Transitions from to .
/// true if it successfully transitioned; otherwise, false.
/// If it successfully transitions, it will also have disposed of and set .
private bool TransitionToCancellationRequested()
{
if (!IsCancellationRequested &&
- Interlocked.CompareExchange(ref _state, NotifyingState, NotCanceledState) == NotCanceledState)
+ Interlocked.CompareExchange(ref _state, States.NotifyingState, States.NotCanceledState) == States.NotCanceledState)
{
// Dispose of the timer, if any. Dispose may be running concurrently here, but ITimer.Dispose is thread-safe.
ITimer? timer = _timer;
@@ -737,7 +740,7 @@ namespace System.Threading
Registrations? registrations = Interlocked.Exchange(ref _registrations, null);
if (registrations is null)
{
- Interlocked.Exchange(ref _state, NotifyingCompleteState);
+ Interlocked.Exchange(ref _state, States.NotifyingCompleteState);
return;
}
@@ -825,7 +828,7 @@ namespace System.Threading
}
finally
{
- _state = NotifyingCompleteState;
+ _state = States.NotifyingCompleteState;
Interlocked.Exchange(ref registrations.ExecutingCallbackId, 0); // for safety, prevent reorderings crossing this point and seeing inconsistent state.
}
@@ -1021,7 +1024,7 @@ namespace System.Threading
///
public volatile int ThreadIDExecutingCallbacks = -1;
/// Spin lock that protects state in the instance.
- private int _lock;
+ private volatile bool _locked;
/// Initializes the instance.
/// The associated source.
@@ -1030,7 +1033,7 @@ namespace System.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)] // used in only two places, one of which is a hot path
private void Recycle(CallbackNode node)
{
- Debug.Assert(_lock == 1);
+ Debug.Assert(_locked);
// Clear out the unused node and put it on the singly-linked free list.
// The only field we don't clear out is the associated Registrations, as that's fixed
@@ -1163,16 +1166,16 @@ namespace System.Threading
/// Enters the lock for this instance. The current thread must not be holding the lock, but that is not validated.
public void EnterLock()
{
- ref int value = ref _lock;
- if (Interlocked.Exchange(ref value, 1) != 0)
+ ref bool value = ref _locked;
+ if (Interlocked.Exchange(ref value, true))
{
Contention(ref value);
[MethodImpl(MethodImplOptions.NoInlining)]
- static void Contention(ref int value)
+ static void Contention(ref bool value)
{
SpinWait sw = default;
- do { sw.SpinOnce(); } while (Interlocked.Exchange(ref value, 1) == 1);
+ do { sw.SpinOnce(); } while (Interlocked.Exchange(ref value, true));
}
}
}
@@ -1180,8 +1183,8 @@ namespace System.Threading
/// Exits the lock for this instance. The current thread must be holding the lock, but that is not validated.
public void ExitLock()
{
- Debug.Assert(_lock == 1);
- Volatile.Write(ref _lock, 0);
+ Debug.Assert(_locked);
+ _locked = false;
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs
index 6e33ce716fd..f22109a0e11 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs
@@ -2,7 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#pragma warning disable CS8500 // takes the address of, gets the size of, or declares a pointer to a managed type ('T')
namespace System.Threading
{
@@ -222,6 +226,66 @@ namespace System.Threading
return (UIntPtr)Exchange(ref Unsafe.As(ref location1), (int)value);
#endif
}
+
+ /// Sets a variable of the specified type to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ /// An unsupported is specified.
+ ///
+ /// The type to be used for and .
+ /// This type must be a reference type, an enum type (i.e. typeof(T).IsEnum is true), or a primitive type (i.e. typeof(T).IsPrimitive is true).
+ ///
+ [Intrinsic]
+ [return: NotNullIfNotNull(nameof(location1))]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe T Exchange([NotNullIfNotNull(nameof(value))] ref T location1, T value)
+ {
+ // Handle all reference types with CompareExchange(ref object, ...).
+ if (!typeof(T).IsValueType)
+ {
+ object? result = Exchange(ref Unsafe.As(ref location1), value);
+ return Unsafe.As