mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-10 01:50:53 +09:00
Remove class constraint from Interlocked.{Compare}Exchange (#104558)
* Remove class constraint from Interlocked.{Compare}Exchange Today `Interlocked.CompareExchange<T>` and `Interlocked.Exchange<T>` support only reference type `T`s. Now that we have corresponding {Compare}Exchange methods that support types of size 1, 2, 4, and 8, we can remove the constraint and support any `T` that's either a reference type, a primitive type, or an enum type, making the generic overload more useful and avoiding consumers needing to choose less-than-ideal types just because of the need for atomicity with Interlocked.{Compare}Exchange. --------- Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
This commit is contained in:
parent
6aa28625e3
commit
41e02e5549
62 changed files with 975 additions and 581 deletions
|
@ -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.
|
||||
|
||||
/// <summary>Sets a variable of the specified type <typeparamref name="T"/> to a specified value and returns the original value, as an atomic operation.</summary>
|
||||
/// <param name="location1">The variable to set to the specified value.</param>
|
||||
/// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
|
||||
/// <returns>The original value of <paramref name="location1"/>.</returns>
|
||||
/// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
|
||||
/// <typeparam name="T">The type to be used for <paramref name="location1"/> and <paramref name="value"/>. This type must be a reference type.</typeparam>
|
||||
[Intrinsic]
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Exchange<T>([NotNullIfNotNull(nameof(value))] ref T location1, T value) where T : class? =>
|
||||
Unsafe.As<T>(Exchange(ref Unsafe.As<T, object?>(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.
|
||||
|
||||
/// <summary>Compares two instances of the specified reference type <typeparamref name="T"/> for reference equality and, if they are equal, replaces the first one.</summary>
|
||||
/// <param name="location1">The destination, whose value is compared by reference with <paramref name="comparand"/> and possibly replaced.</param>
|
||||
/// <param name="value">The value that replaces the destination value if the comparison by reference results in equality.</param>
|
||||
/// <param name="comparand">The object that is compared by reference to the value at <paramref name="location1"/>.</param>
|
||||
/// <returns>The original value in <paramref name="location1"/>.</returns>
|
||||
/// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
|
||||
/// <typeparam name="T">The type to be used for <paramref name="location1"/>, <paramref name="value"/>, and <paramref name="comparand"/>. This type must be a reference type.</typeparam>
|
||||
[Intrinsic]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class? =>
|
||||
Unsafe.As<T>(CompareExchange(ref Unsafe.As<T, object?>(ref location1), value, comparand));
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -36,14 +36,6 @@ namespace System.Threading
|
|||
#endif
|
||||
}
|
||||
|
||||
[Intrinsic]
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class?
|
||||
{
|
||||
return Unsafe.As<T>(CompareExchange(ref Unsafe.As<T, object?>(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<T>([NotNullIfNotNull(nameof(value))] ref T location1, T value) where T : class?
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref location1))
|
||||
ThrowHelper.ThrowNullReferenceException();
|
||||
return Unsafe.As<T>(RuntimeImports.InterlockedExchange(ref Unsafe.As<T, object?>(ref location1), value));
|
||||
}
|
||||
|
||||
[Intrinsic]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<BYTE*>(il);
|
||||
methInfo->ILCodeSize = sizeof(il);
|
||||
methInfo->ILCode = const_cast<BYTE*>(il[ilIndex]);
|
||||
methInfo->ILCodeSize = sizeof(il[ilIndex]);
|
||||
methInfo->maxStack = 3;
|
||||
methInfo->EHcount = 0;
|
||||
methInfo->options = (CorInfoOptions)0;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -292,7 +292,7 @@ namespace System.IO
|
|||
|
||||
private ManualResetValueTaskSourceCore<bool> _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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|||
/// <summary>Number of steals; needs to be combined with <see cref="_addTakeCount"/> to get an actual Count.</summary>
|
||||
private int _stealCount;
|
||||
/// <summary>The current queue operation. Used to quiesce before performing operations from one thread onto another.</summary>
|
||||
internal volatile int _currentOp;
|
||||
internal volatile Operation _currentOp;
|
||||
/// <summary>true if this queue's lock is held as part of a global freeze.</summary>
|
||||
internal bool _frozen;
|
||||
/// <summary>Next queue in the <see cref="ConcurrentBag{T}"/>'s set of thread-local queues.</summary>
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -128,7 +128,7 @@ namespace System.IO.Compression
|
|||
if (buffer != null)
|
||||
{
|
||||
_buffer = null!;
|
||||
if (!AsyncOperationIsActive)
|
||||
if (!_activeAsyncOperation)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
@ -170,18 +170,19 @@ namespace System.IO.Compression
|
|||
/// <param name="value">The length of the stream.</param>
|
||||
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() =>
|
||||
|
|
|
@ -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<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
@ -751,7 +751,7 @@ namespace System.IO.Compression
|
|||
if (buffer != null)
|
||||
{
|
||||
_buffer = null;
|
||||
if (!AsyncOperationIsActive)
|
||||
if (!_activeAsyncOperation)
|
||||
{
|
||||
ArrayPool<byte>.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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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<byte> 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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace System.Net.WebSockets
|
|||
private volatile WebSocketOperation.CloseOutputOperation? _closeOutputOperation;
|
||||
private Nullable<WebSocketCloseStatus> _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<byte> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace System.Net.WebSockets
|
|||
private ArraySegment<byte> _pinnedSendBuffer;
|
||||
private GCHandle _pinnedSendBufferHandle;
|
||||
private int _stateWhenDisposing = int.MinValue;
|
||||
private int _sendBufferState;
|
||||
private SendBufferState _sendBufferState;
|
||||
|
||||
private WebSocketBuffer(ArraySegment<byte> 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
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace System.Net.WebSockets
|
|||
private HttpListenerAsyncEventArgs? _readEventArgs;
|
||||
private TaskCompletionSource? _writeTaskCompletionSource;
|
||||
private TaskCompletionSource<int>? _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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -109,9 +109,9 @@ public sealed partial class QuicConnection : IAsyncDisposable
|
|||
private readonly MsQuicContextSafeHandle _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Set to non-zero once disposed. Prevents double and/or concurrent disposal.
|
||||
/// Set to true once disposed. Prevents double and/or concurrent disposal.
|
||||
/// </summary>
|
||||
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
|
|||
/// <returns>An asynchronous task that completes with the opened <see cref="QuicStream" />.</returns>
|
||||
public async ValueTask<QuicStream> 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
|
|||
/// <returns>An asynchronous task that completes with the accepted <see cref="QuicStream" />.</returns>
|
||||
public async ValueTask<QuicStream> 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
|
|||
/// <returns>An asynchronous task that completes when the connection is closed.</returns>
|
||||
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
|
|||
/// <returns>A task that represents the asynchronous dispose operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, 1) != 0)
|
||||
if (Interlocked.Exchange(ref _disposed, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -74,9 +74,9 @@ public sealed partial class QuicListener : IAsyncDisposable
|
|||
private readonly MsQuicContextSafeHandle _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Set to non-zero once disposed. Prevents double and/or concurrent disposal.
|
||||
/// Set to true once disposed. Prevents double and/or concurrent disposal.
|
||||
/// </summary>
|
||||
private int _disposed;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Completed when SHUTDOWN_COMPLETE arrives.
|
||||
|
@ -175,7 +175,7 @@ public sealed partial class QuicListener : IAsyncDisposable
|
|||
/// <returns>A task that will contain a fully connected <see cref="QuicConnection" /> which successfully finished the handshake and is ready to be used.</returns>
|
||||
public async ValueTask<QuicConnection> 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
|
|||
/// <returns>A task that represents the asynchronous dispose operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, 1) != 0)
|
||||
if (Interlocked.Exchange(ref _disposed, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
/// <inheritdoc />
|
||||
/// <summary>Gets a value indicating whether the <see cref="QuicStream" /> supports reading.</summary>
|
||||
public override bool CanRead => Volatile.Read(ref _disposed) == 0 && _canRead;
|
||||
public override bool CanRead => !Volatile.Read(ref _disposed) && _canRead;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
|
||||
|
@ -113,7 +113,7 @@ public partial class QuicStream : Stream
|
|||
/// <inheritdoc />
|
||||
public override int Read(Span<byte> buffer)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed == 1, this);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
byte[] rentedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
||||
CancellationTokenSource? cts = null;
|
||||
|
@ -149,7 +149,7 @@ public partial class QuicStream : Stream
|
|||
// Write boilerplate.
|
||||
/// <inheritdoc />
|
||||
/// <summary>Gets a value indicating whether the <see cref="QuicStream" /> supports writing.</summary>
|
||||
public override bool CanWrite => Volatile.Read(ref _disposed) == 0 && _canWrite;
|
||||
public override bool CanWrite => !Volatile.Read(ref _disposed) && _canWrite;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
|
||||
|
@ -175,7 +175,7 @@ public partial class QuicStream : Stream
|
|||
/// <inheritdoc />
|
||||
public override void Write(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed == 1, this);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
CancellationTokenSource? cts = null;
|
||||
if (_writeTimeout > TimeSpan.Zero)
|
||||
|
|
|
@ -60,9 +60,9 @@ public sealed partial class QuicStream
|
|||
private readonly MsQuicContextSafeHandle _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Set to non-zero once disposed. Prevents double and/or concurrent disposal.
|
||||
/// Set to true once disposed. Prevents double and/or concurrent disposal.
|
||||
/// </summary>
|
||||
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
|
|||
/// <inheritdoc />
|
||||
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed == 1, this);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!_canRead)
|
||||
{
|
||||
|
@ -359,7 +359,7 @@ public sealed partial class QuicStream
|
|||
/// <param name="completeWrites">Notifies the peer about gracefully closing the write side, i.e.: sends FIN flag with the data.</param>
|
||||
public ValueTask WriteAsync(ReadOnlyMemory<byte> 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
|
|||
/// <param name="errorCode">The error code with which to abort the stream, this value is application protocol (layer above QUIC) dependent.</param>
|
||||
public void Abort(QuicAbortDirection abortDirection, long errorCode)
|
||||
{
|
||||
if (_disposed == 1)
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -510,7 +510,7 @@ public sealed partial class QuicStream
|
|||
/// </remarks>
|
||||
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
|
|||
/// <returns>A task that represents the asynchronous dispose operation.</returns>
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, 1) != 0)
|
||||
if (Interlocked.Exchange(ref _disposed, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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<WebResponse>? _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);
|
||||
}
|
||||
|
|
|
@ -445,14 +445,14 @@ namespace System.Net
|
|||
{
|
||||
internal InfiniteTimer() : base(Timeout.Infinite) { }
|
||||
|
||||
private int _cancelled;
|
||||
private bool _canceled;
|
||||
|
||||
internal override bool HasExpired => false;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Cancels the timer. Returns true the first time, false after that.</para>
|
||||
/// </summary>
|
||||
internal override bool Cancel() => Interlocked.Exchange(ref _cancelled, 1) == 0;
|
||||
internal override bool Cancel() => !Interlocked.Exchange(ref _canceled, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -42,9 +42,9 @@ namespace System.Net.Security
|
|||
private int _readBufferCount;
|
||||
private ArrayBufferWriter<byte>? _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<int> ReadAllAsync(Stream stream, Memory<byte> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<TIOAdapter>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace System.Net.Sockets
|
|||
private int _closeSocketThread;
|
||||
private int _closeSocketTick;
|
||||
#endif
|
||||
private int _ownClose;
|
||||
private bool _ownClose;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="T:System.Net.Sockets.SafeSocketHandle" />.
|
||||
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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<bool> _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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
|||
/// <summary>Ensures we've registered with NetworkChange to clear out statically-cached state upon a network change notification.</summary>
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@ namespace System.Net.WebSockets
|
|||
public sealed partial class ClientWebSocket : WebSocket
|
||||
{
|
||||
/// <summary>This is really an InternalState value, but Interlocked doesn't support operations on values of enum types.</summary>
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -4328,10 +4328,13 @@
|
|||
<data name="NotSupported_EmitDebugInfo" xml:space="preserve">
|
||||
<value>Emitting debug info is not supported for this member.</value>
|
||||
</data>
|
||||
<data name="NotSupported_ReferenceEnumOrPrimitiveTypeRequired" xml:space="preserve">
|
||||
<value>The specified type must be a reference type, a primitive type, or an enum type.</value>
|
||||
</data>
|
||||
<data name="Argument_BadFieldForInitializeArray" xml:space="preserve">
|
||||
<value>The field is invalid for initializing array or span.</value>
|
||||
</data>
|
||||
<data name="Argument_BadArrayForInitializeArray" xml:space="preserve">
|
||||
<value>Only array or span of primitive or enum types can be initialized from static data.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace System.Buffers
|
|||
/// </summary>
|
||||
private readonly SharedArrayPoolPartitions?[] _buckets = new SharedArrayPoolPartitions[NumBuckets];
|
||||
/// <summary>Whether the callback to trim arrays in response to memory pressure has been created.</summary>
|
||||
private int _trimCallbackCreated;
|
||||
private bool _trimCallbackCreated;
|
||||
|
||||
/// <summary>Allocate a new <see cref="SharedArrayPoolPartitions"/> and try to store it into the <see cref="_buckets"/> array.</summary>
|
||||
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<T>)s).Trim(), this);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace System.Threading
|
|||
public class CancellationTokenSource : IDisposable
|
||||
{
|
||||
/// <summary>A <see cref="CancellationTokenSource"/> that's already canceled.</summary>
|
||||
internal static readonly CancellationTokenSource s_canceledSource = new CancellationTokenSource() { _state = NotifyingCompleteState };
|
||||
internal static readonly CancellationTokenSource s_canceledSource = new CancellationTokenSource() { _state = States.NotifyingCompleteState };
|
||||
/// <summary>A <see cref="CancellationTokenSource"/> that's never canceled. This isn't enforced programmatically, only by usage. Do not cancel!</summary>
|
||||
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()
|
||||
|
||||
/// <summary>The current state of the CancellationTokenSource.</summary>
|
||||
private volatile int _state;
|
||||
private volatile States _state;
|
||||
/// <summary>Whether this <see cref="CancellationTokenSource"/> has been disposed.</summary>
|
||||
private bool _disposed;
|
||||
/// <summary>ITimer used by CancelAfter and Timer-related ctors. Used instead of Timer to avoid extra allocations and because the rooted behavior is desired.</summary>
|
||||
|
@ -46,10 +46,13 @@ namespace System.Threading
|
|||
/// <remarks>Lazily-initialized, also serving as the lock to protect its contained state.</remarks>
|
||||
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;
|
||||
/// <summary>Legal values for <see cref="_state"/>.</summary>
|
||||
private enum States
|
||||
{
|
||||
NotCanceledState = 0, // default value of _state
|
||||
NotifyingState = 1,
|
||||
NotifyingCompleteState = 2,
|
||||
}
|
||||
|
||||
/// <summary>Gets whether cancellation has been requested for this <see cref="CancellationTokenSource" />.</summary>
|
||||
/// <value>Whether cancellation has been requested for this <see cref="CancellationTokenSource" />.</value>
|
||||
|
@ -66,10 +69,10 @@ namespace System.Threading
|
|||
/// canceled concurrently.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool IsCancellationRequested => _state != NotCanceledState;
|
||||
public bool IsCancellationRequested => _state != States.NotCanceledState;
|
||||
|
||||
/// <summary>A simple helper to determine whether cancellation has finished.</summary>
|
||||
internal bool IsCancellationCompleted => _state == NotifyingCompleteState;
|
||||
internal bool IsCancellationCompleted => _state == States.NotifyingCompleteState;
|
||||
|
||||
/// <summary>Gets the <see cref="CancellationToken"/> associated with this <see cref="CancellationTokenSource"/>.</summary>
|
||||
/// <value>The <see cref="CancellationToken"/> associated with this <see cref="CancellationTokenSource"/>.</value>
|
||||
|
@ -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<ManualResetEvent?>(ref _kernelEvent!, null);
|
||||
if (mre != null && _state != NotifyingState)
|
||||
if (mre != null && _state != States.NotifyingState)
|
||||
{
|
||||
mre.Dispose();
|
||||
}
|
||||
|
@ -698,13 +701,13 @@ namespace System.Threading
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Transitions from <see cref="NotCanceledState"/> to <see cref="NotifyingState"/>.</summary>
|
||||
/// <summary>Transitions from <see cref="States.NotCanceledState"/> to <see cref="States.NotifyingState"/>.</summary>
|
||||
/// <returns>true if it successfully transitioned; otherwise, false.</returns>
|
||||
/// <remarks>If it successfully transitions, it will also have disposed of <see cref="_timer"/> and set <see cref="_kernelEvent"/>.</remarks>
|
||||
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
|
|||
/// </remarks>
|
||||
public volatile int ThreadIDExecutingCallbacks = -1;
|
||||
/// <summary>Spin lock that protects state in the instance.</summary>
|
||||
private int _lock;
|
||||
private volatile bool _locked;
|
||||
|
||||
/// <summary>Initializes the instance.</summary>
|
||||
/// <param name="source">The associated source.</param>
|
||||
|
@ -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
|
|||
/// <summary>Enters the lock for this instance. The current thread must not be holding the lock, but that is not validated.</summary>
|
||||
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
|
|||
/// <summary>Exits the lock for this instance. The current thread must be holding the lock, but that is not validated.</summary>
|
||||
public void ExitLock()
|
||||
{
|
||||
Debug.Assert(_lock == 1);
|
||||
Volatile.Write(ref _lock, 0);
|
||||
Debug.Assert(_locked);
|
||||
_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<UIntPtr, int>(ref location1), (int)value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Sets a variable of the specified type <typeparamref name="T"/> to a specified value and returns the original value, as an atomic operation.</summary>
|
||||
/// <param name="location1">The variable to set to the specified value.</param>
|
||||
/// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
|
||||
/// <returns>The original value of <paramref name="location1"/>.</returns>
|
||||
/// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
|
||||
/// <exception cref="NotSupportedException">An unsupported <typeparamref name="T"/> is specified.</exception>
|
||||
/// <typeparam name="T">
|
||||
/// The type to be used for <paramref name="location1"/> and <paramref name="value"/>.
|
||||
/// 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).
|
||||
/// </typeparam>
|
||||
[Intrinsic]
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe T Exchange<T>([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<T, object?>(ref location1), value);
|
||||
return Unsafe.As<object?, T>(ref result);
|
||||
}
|
||||
|
||||
// Handle everything else with a CompareExchange overload for the unsigned integral type of the corresponding size.
|
||||
// Only primitive types and enum types (which are backed by primitive types) are supported.
|
||||
if (!typeof(T).IsPrimitive && !typeof(T).IsEnum)
|
||||
{
|
||||
throw new NotSupportedException(SR.NotSupported_ReferenceEnumOrPrimitiveTypeRequired);
|
||||
}
|
||||
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
return Unsafe.BitCast<byte, T>(
|
||||
Exchange(
|
||||
ref Unsafe.As<T, byte>(ref location1),
|
||||
Unsafe.BitCast<T, byte>(value)));
|
||||
}
|
||||
|
||||
if (sizeof(T) == 2)
|
||||
{
|
||||
return Unsafe.BitCast<ushort, T>(
|
||||
Exchange(
|
||||
ref Unsafe.As<T, ushort>(ref location1),
|
||||
Unsafe.BitCast<T, ushort>(value)));
|
||||
}
|
||||
|
||||
if (sizeof(T) == 4)
|
||||
{
|
||||
return Unsafe.BitCast<int, T>(
|
||||
Exchange(
|
||||
ref Unsafe.As<T, int>(ref location1),
|
||||
Unsafe.BitCast<T, int>(value)));
|
||||
}
|
||||
|
||||
Debug.Assert(sizeof(T) == 8);
|
||||
return Unsafe.BitCast<long, T>(
|
||||
Exchange(
|
||||
ref Unsafe.As<T, long>(ref location1),
|
||||
Unsafe.BitCast<T, long>(value)));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CompareExchange
|
||||
|
@ -413,6 +477,71 @@ namespace System.Threading
|
|||
return (UIntPtr)CompareExchange(ref Unsafe.As<UIntPtr, int>(ref location1), (int)value, (int)comparand);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Compares two instances of the specified type <typeparamref name="T"/> for equality and, if they are equal, replaces the first one.</summary>
|
||||
/// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
|
||||
/// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
|
||||
/// <param name="comparand">The object that is compared to the value at <paramref name="location1"/>.</param>
|
||||
/// <returns>The original value in <paramref name="location1"/>.</returns>
|
||||
/// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
|
||||
/// <exception cref="NotSupportedException">An unsupported <typeparamref name="T"/> is specified.</exception>
|
||||
/// <typeparam name="T">
|
||||
/// The type to be used for <paramref name="location1"/>, <paramref name="value"/>, and <paramref name="comparand"/>.
|
||||
/// 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).
|
||||
/// </typeparam>
|
||||
[Intrinsic]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
public static unsafe T CompareExchange<T>(ref T location1, T value, T comparand)
|
||||
{
|
||||
// Handle all reference types with CompareExchange(ref object, ...).
|
||||
if (!typeof(T).IsValueType)
|
||||
{
|
||||
object? result = CompareExchange(ref Unsafe.As<T, object?>(ref location1), value, comparand);
|
||||
return Unsafe.As<object?, T>(ref result);
|
||||
}
|
||||
|
||||
// Handle everything else with a CompareExchange overload for the unsigned integral type of the corresponding size.
|
||||
// Only primitive types and enum types (which are backed by primitive types) are supported.
|
||||
if (!typeof(T).IsPrimitive && !typeof(T).IsEnum)
|
||||
{
|
||||
throw new NotSupportedException(SR.NotSupported_ReferenceEnumOrPrimitiveTypeRequired);
|
||||
}
|
||||
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
return Unsafe.BitCast<byte, T>(
|
||||
CompareExchange(
|
||||
ref Unsafe.As<T, byte>(ref location1),
|
||||
Unsafe.BitCast<T, byte>(value),
|
||||
Unsafe.BitCast<T, byte>(comparand)));
|
||||
}
|
||||
|
||||
if (sizeof(T) == 2)
|
||||
{
|
||||
return Unsafe.BitCast<ushort, T>(
|
||||
CompareExchange(
|
||||
ref Unsafe.As<T, ushort>(ref location1),
|
||||
Unsafe.BitCast<T, ushort>(value),
|
||||
Unsafe.BitCast<T, ushort>(comparand)));
|
||||
}
|
||||
|
||||
if (sizeof(T) == 4)
|
||||
{
|
||||
return Unsafe.BitCast<int, T>(
|
||||
CompareExchange(
|
||||
ref Unsafe.As<T, int>(ref location1),
|
||||
Unsafe.BitCast<T, int>(value),
|
||||
Unsafe.BitCast<T, int>(comparand)));
|
||||
}
|
||||
|
||||
Debug.Assert(sizeof(T) == 8);
|
||||
return Unsafe.BitCast<long, T>(
|
||||
CompareExchange(
|
||||
ref Unsafe.As<T, long>(ref location1),
|
||||
Unsafe.BitCast<T, long>(value),
|
||||
Unsafe.BitCast<T, long>(comparand)));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
|
|
|
@ -1416,7 +1416,7 @@ namespace System.Threading
|
|||
|
||||
private struct SpinLock
|
||||
{
|
||||
private int _isLocked;
|
||||
private bool _isLocked;
|
||||
|
||||
/// <summary>
|
||||
/// Used to deprioritize threads attempting to enter the lock when they would not make progress after doing so.
|
||||
|
@ -1535,7 +1535,7 @@ namespace System.Threading
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool TryEnter()
|
||||
{
|
||||
return Interlocked.CompareExchange(ref _isLocked, 1, 0) == 0;
|
||||
return !Interlocked.Exchange(ref _isLocked, true);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
@ -1577,7 +1577,7 @@ namespace System.Threading
|
|||
|
||||
if (!IsEnterDeprioritized(reason))
|
||||
{
|
||||
if (_isLocked == 0 && TryEnter())
|
||||
if (!_isLocked && TryEnter())
|
||||
{
|
||||
if (deprioritizationStateChange != 0)
|
||||
{
|
||||
|
@ -1606,12 +1606,12 @@ namespace System.Threading
|
|||
|
||||
public void Exit()
|
||||
{
|
||||
Debug.Assert(_isLocked != 0, "Exiting spin lock that is not held");
|
||||
Volatile.Write(ref _isLocked, 0);
|
||||
Debug.Assert(_isLocked, "Exiting spin lock that is not held");
|
||||
Volatile.Write(ref _isLocked, false);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public bool IsHeld => _isLocked != 0;
|
||||
public bool IsHeld => _isLocked;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -399,7 +399,7 @@ namespace System.Threading
|
|||
|
||||
private bool _loggingEnabled;
|
||||
private bool _dispatchNormalPriorityWorkFirst;
|
||||
private int _mayHaveHighPriorityWorkItems;
|
||||
private bool _mayHaveHighPriorityWorkItems;
|
||||
|
||||
// SOS's ThreadPool command depends on the following names
|
||||
internal readonly ConcurrentQueue<object> workItems = new ConcurrentQueue<object>();
|
||||
|
@ -443,7 +443,7 @@ namespace System.Threading
|
|||
{
|
||||
private readonly Internal.PaddingFor32 pad1;
|
||||
|
||||
public int queueProcessingStage;
|
||||
public QueueProcessingStage queueProcessingStage;
|
||||
|
||||
private readonly Internal.PaddingFor32 pad2;
|
||||
}
|
||||
|
@ -611,7 +611,7 @@ namespace System.Threading
|
|||
// Otherwise let the current requested thread handle parallelization.
|
||||
if (Interlocked.Exchange(
|
||||
ref _separated.queueProcessingStage,
|
||||
(int)QueueProcessingStage.Scheduled) == (int)QueueProcessingStage.NotScheduled)
|
||||
QueueProcessingStage.Scheduled) == QueueProcessingStage.NotScheduled)
|
||||
{
|
||||
ThreadPool.RequestWorkerThread();
|
||||
}
|
||||
|
@ -696,7 +696,7 @@ namespace System.Threading
|
|||
highPriorityWorkItems.Enqueue(workItem);
|
||||
|
||||
// If the change below is seen by another thread, ensure that the enqueued work item will also be visible
|
||||
Volatile.Write(ref _mayHaveHighPriorityWorkItems, 1);
|
||||
Volatile.Write(ref _mayHaveHighPriorityWorkItems, true);
|
||||
|
||||
EnsureThreadRequested();
|
||||
}
|
||||
|
@ -736,8 +736,8 @@ namespace System.Threading
|
|||
tl.isProcessingHighPriorityWorkItems = false;
|
||||
}
|
||||
else if (
|
||||
_mayHaveHighPriorityWorkItems != 0 &&
|
||||
Interlocked.CompareExchange(ref _mayHaveHighPriorityWorkItems, 0, 1) != 0 &&
|
||||
_mayHaveHighPriorityWorkItems &&
|
||||
Interlocked.CompareExchange(ref _mayHaveHighPriorityWorkItems, false, true) &&
|
||||
TryStartProcessingHighPriorityWorkItemsAndDequeue(tl, out workItem))
|
||||
{
|
||||
return workItem;
|
||||
|
@ -816,7 +816,7 @@ namespace System.Threading
|
|||
}
|
||||
|
||||
tl.isProcessingHighPriorityWorkItems = true;
|
||||
_mayHaveHighPriorityWorkItems = 1;
|
||||
_mayHaveHighPriorityWorkItems = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -906,8 +906,8 @@ namespace System.Threading
|
|||
// 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.
|
||||
Debug.Assert(workQueue._separated.queueProcessingStage == (int)QueueProcessingStage.Scheduled);
|
||||
workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Determining;
|
||||
Debug.Assert(workQueue._separated.queueProcessingStage == QueueProcessingStage.Scheduled);
|
||||
workQueue._separated.queueProcessingStage = QueueProcessingStage.Determining;
|
||||
Interlocked.MemoryBarrier();
|
||||
|
||||
object? workItem = null;
|
||||
|
@ -935,8 +935,8 @@ namespace System.Threading
|
|||
workQueue.UnassignWorkItemQueue(tl);
|
||||
}
|
||||
|
||||
Debug.Assert(workQueue._separated.queueProcessingStage != (int)QueueProcessingStage.NotScheduled);
|
||||
workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Scheduled;
|
||||
Debug.Assert(workQueue._separated.queueProcessingStage != QueueProcessingStage.NotScheduled);
|
||||
workQueue._separated.queueProcessingStage = QueueProcessingStage.Scheduled;
|
||||
ThreadPool.RequestWorkerThread();
|
||||
return true;
|
||||
}
|
||||
|
@ -944,13 +944,13 @@ namespace System.Threading
|
|||
// 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 =
|
||||
QueueProcessingStage stageBeforeUpdate =
|
||||
Interlocked.CompareExchange(
|
||||
ref workQueue._separated.queueProcessingStage,
|
||||
(int)QueueProcessingStage.NotScheduled,
|
||||
(int)QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == (int)QueueProcessingStage.Determining)
|
||||
QueueProcessingStage.NotScheduled,
|
||||
QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == QueueProcessingStage.Determining)
|
||||
{
|
||||
if (s_assignableWorkItemQueueCount > 0)
|
||||
{
|
||||
|
@ -964,7 +964,7 @@ namespace System.Threading
|
|||
// by the enqueuer. Set the stage back to Determining and try to dequeue a work item again.
|
||||
//
|
||||
// See the first similarly used memory barrier in the method for why it's necessary.
|
||||
workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Determining;
|
||||
workQueue._separated.queueProcessingStage = QueueProcessingStage.Determining;
|
||||
Interlocked.MemoryBarrier();
|
||||
}
|
||||
}
|
||||
|
@ -976,7 +976,7 @@ namespace System.Threading
|
|||
// be able to detect if an enqueue races with the dequeue below.
|
||||
//
|
||||
// See the first similarly used memory barrier in the method for why it's necessary.
|
||||
workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Determining;
|
||||
workQueue._separated.queueProcessingStage = QueueProcessingStage.Determining;
|
||||
Interlocked.MemoryBarrier();
|
||||
|
||||
object? secondWorkItem = DequeueWithPriorityAlternation(workQueue, tl, out bool missedSteal);
|
||||
|
@ -993,8 +993,8 @@ namespace System.Threading
|
|||
// responsibility of the new thread and other enqueuers to request more threads as necessary. The
|
||||
// parallelization may be necessary here for correctness (aside from perf) if the work item blocks for some
|
||||
// reason that may have a dependency on other queued work items.
|
||||
Debug.Assert(workQueue._separated.queueProcessingStage != (int)QueueProcessingStage.NotScheduled);
|
||||
workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Scheduled;
|
||||
Debug.Assert(workQueue._separated.queueProcessingStage != QueueProcessingStage.NotScheduled);
|
||||
workQueue._separated.queueProcessingStage = QueueProcessingStage.Scheduled;
|
||||
ThreadPool.RequestWorkerThread();
|
||||
}
|
||||
else
|
||||
|
@ -1002,13 +1002,13 @@ namespace System.Threading
|
|||
// 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 requested a thread, so request one now.
|
||||
int stageBeforeUpdate =
|
||||
QueueProcessingStage stageBeforeUpdate =
|
||||
Interlocked.CompareExchange(
|
||||
ref workQueue._separated.queueProcessingStage,
|
||||
(int)QueueProcessingStage.NotScheduled,
|
||||
(int)QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == (int)QueueProcessingStage.Scheduled)
|
||||
QueueProcessingStage.NotScheduled,
|
||||
QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == QueueProcessingStage.Scheduled)
|
||||
{
|
||||
// A work item was enqueued after the stage was set to Determining earlier, and a thread was not
|
||||
// requested by the enqueuer, so request a thread now. An alternate is to retry dequeuing, as requesting
|
||||
|
@ -1269,7 +1269,7 @@ namespace System.Threading
|
|||
Scheduled
|
||||
}
|
||||
|
||||
private int _queueProcessingStage;
|
||||
private QueueProcessingStage _queueProcessingStage;
|
||||
private readonly ConcurrentQueue<T> _workItems = new ConcurrentQueue<T>();
|
||||
|
||||
public int Count => _workItems.Count;
|
||||
|
@ -1287,7 +1287,7 @@ namespace System.Threading
|
|||
// Otherwise there must be a work item already queued or another thread already handling parallelization.
|
||||
if (Interlocked.Exchange(
|
||||
ref _queueProcessingStage,
|
||||
(int)QueueProcessingStage.Scheduled) == (int)QueueProcessingStage.NotScheduled)
|
||||
QueueProcessingStage.Scheduled) == QueueProcessingStage.NotScheduled)
|
||||
{
|
||||
ThreadPool.UnsafeQueueHighPriorityWorkItemInternal(this);
|
||||
}
|
||||
|
@ -1298,20 +1298,20 @@ namespace System.Threading
|
|||
if (!isQueueEmpty)
|
||||
{
|
||||
// There are more items to process, set stage to Scheduled and enqueue a TP work item.
|
||||
_queueProcessingStage = (int)QueueProcessingStage.Scheduled;
|
||||
_queueProcessingStage = QueueProcessingStage.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 one.
|
||||
int stageBeforeUpdate =
|
||||
QueueProcessingStage stageBeforeUpdate =
|
||||
Interlocked.CompareExchange(
|
||||
ref _queueProcessingStage,
|
||||
(int)QueueProcessingStage.NotScheduled,
|
||||
(int)QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == (int)QueueProcessingStage.Determining)
|
||||
QueueProcessingStage.NotScheduled,
|
||||
QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == QueueProcessingStage.Determining)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1325,14 +1325,14 @@ namespace System.Threading
|
|||
T workItem;
|
||||
while (true)
|
||||
{
|
||||
Debug.Assert(_queueProcessingStage == (int)QueueProcessingStage.Scheduled);
|
||||
Debug.Assert(_queueProcessingStage == QueueProcessingStage.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.
|
||||
_queueProcessingStage = (int)QueueProcessingStage.Determining;
|
||||
_queueProcessingStage = QueueProcessingStage.Determining;
|
||||
Interlocked.MemoryBarrier();
|
||||
|
||||
if (_workItems.TryDequeue(out workItem))
|
||||
|
@ -1343,13 +1343,13 @@ namespace System.Threading
|
|||
// 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 =
|
||||
QueueProcessingStage stageBeforeUpdate =
|
||||
Interlocked.CompareExchange(
|
||||
ref _queueProcessingStage,
|
||||
(int)QueueProcessingStage.NotScheduled,
|
||||
(int)QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == (int)QueueProcessingStage.Determining)
|
||||
QueueProcessingStage.NotScheduled,
|
||||
QueueProcessingStage.Determining);
|
||||
Debug.Assert(stageBeforeUpdate != QueueProcessingStage.NotScheduled);
|
||||
if (stageBeforeUpdate == QueueProcessingStage.Determining)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1401,13 +1401,12 @@ namespace System.Threading
|
|||
internal abstract class QueueUserWorkItemCallbackBase : IThreadPoolWorkItem
|
||||
{
|
||||
#if DEBUG
|
||||
private int executed;
|
||||
private bool _executed;
|
||||
|
||||
~QueueUserWorkItemCallbackBase()
|
||||
{
|
||||
Interlocked.MemoryBarrier(); // ensure that an old cached value is not read below
|
||||
Debug.Assert(
|
||||
executed != 0, "A QueueUserWorkItemCallback was never called!");
|
||||
Debug.Assert(_executed, "A QueueUserWorkItemCallback was never called!");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1415,9 +1414,7 @@ namespace System.Threading
|
|||
{
|
||||
#if DEBUG
|
||||
GC.SuppressFinalize(this);
|
||||
Debug.Assert(
|
||||
0 == Interlocked.Exchange(ref executed, 1),
|
||||
"A QueueUserWorkItemCallback was called twice!");
|
||||
Debug.Assert(!Interlocked.Exchange(ref _executed, true), "A QueueUserWorkItemCallback was called twice!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace System.Threading
|
|||
/// - Sleep(0) intentionally does not acquire any lock, so it uses an interlocked compare-exchange for the read and
|
||||
/// reset, see <see cref="CheckAndResetPendingInterrupt_NotLocked"/>
|
||||
/// </summary>
|
||||
private int _isPendingInterrupt;
|
||||
private bool _isPendingInterrupt;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -508,7 +508,7 @@ namespace System.Threading
|
|||
s_lock.VerifyIsLocked();
|
||||
_waitMonitor.VerifyIsLocked();
|
||||
|
||||
_isPendingInterrupt = 1;
|
||||
_isPendingInterrupt = true;
|
||||
}
|
||||
|
||||
public bool CheckAndResetPendingInterrupt
|
||||
|
@ -519,11 +519,11 @@ namespace System.Threading
|
|||
Debug.Assert(s_lock.IsLocked || _waitMonitor.IsLocked);
|
||||
#endif
|
||||
|
||||
if (_isPendingInterrupt == 0)
|
||||
if (!_isPendingInterrupt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_isPendingInterrupt = 0;
|
||||
_isPendingInterrupt = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -535,7 +535,7 @@ namespace System.Threading
|
|||
s_lock.VerifyIsNotLocked();
|
||||
_waitMonitor.VerifyIsNotLocked();
|
||||
|
||||
return Interlocked.CompareExchange(ref _isPendingInterrupt, 0, 1) != 0;
|
||||
return Interlocked.CompareExchange(ref _isPendingInterrupt, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2396,16 +2396,13 @@ namespace System
|
|||
Done:
|
||||
cF |= Flags.MinimalUriInfoSet;
|
||||
|
||||
Debug.Assert(sizeof(Flags) == sizeof(ulong));
|
||||
|
||||
Interlocked.CompareExchange(ref _info, info, null!);
|
||||
|
||||
Flags current = _flags;
|
||||
while ((current & Flags.MinimalUriInfoSet) == 0)
|
||||
{
|
||||
Flags newValue = (current & ~Flags.IndexMask) | cF;
|
||||
ulong oldValue = Interlocked.CompareExchange(ref Unsafe.As<Flags, ulong>(ref _flags), (ulong)newValue, (ulong)current);
|
||||
if (oldValue == (ulong)current)
|
||||
Flags oldValue = Interlocked.CompareExchange(ref _flags, (current & ~Flags.IndexMask) | cF, current);
|
||||
if (oldValue == current)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
|
||||
private readonly object? _runtimeCallableWrapper;
|
||||
|
||||
// This is an int so we can use the Interlocked APIs to update it.
|
||||
private volatile int _released;
|
||||
private volatile bool _released;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize ComObject instance.
|
||||
|
@ -81,7 +80,7 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// </remarks>
|
||||
public void FinalRelease()
|
||||
{
|
||||
if (UniqueInstance && Interlocked.CompareExchange(ref _released, 1, 0) == 0)
|
||||
if (UniqueInstance && !Interlocked.Exchange(ref _released, true))
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
CacheStrategy.Clear(IUnknownStrategy);
|
||||
|
@ -115,7 +114,7 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
|
||||
private bool LookUpVTableInfo(RuntimeTypeHandle handle, out IIUnknownCacheStrategy.TableInfo result, out int qiHResult)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_released != 0, this);
|
||||
ObjectDisposedException.ThrowIf(_released, this);
|
||||
|
||||
qiHResult = 0;
|
||||
if (!CacheStrategy.TryGetTableInfo(handle, out result))
|
||||
|
|
|
@ -92,32 +92,10 @@ namespace System.Threading.Tasks
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static bool Interlockable() =>
|
||||
typeof(T) == typeof(sbyte) ||
|
||||
typeof(T) == typeof(byte) ||
|
||||
typeof(T) == typeof(short) ||
|
||||
typeof(T) == typeof(ushort) ||
|
||||
typeof(T) == typeof(char) ||
|
||||
typeof(T) == typeof(int) ||
|
||||
typeof(T) == typeof(uint) ||
|
||||
typeof(T) == typeof(long) ||
|
||||
typeof(T) == typeof(ulong) ||
|
||||
typeof(T) == typeof(nint) ||
|
||||
typeof(T) == typeof(nuint);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static unsafe bool CompareExchange(ref T location, T value, T comparand) =>
|
||||
sizeof(T) == sizeof(byte) ? Interlocked.CompareExchange(ref Unsafe.As<T, byte>(ref location), Unsafe.As<T, byte>(ref value), Unsafe.As<T, byte>(ref comparand)) == Unsafe.As<T, byte>(ref comparand) :
|
||||
sizeof(T) == sizeof(ushort) ? Interlocked.CompareExchange(ref Unsafe.As<T, ushort>(ref location), Unsafe.As<T, ushort>(ref value), Unsafe.As<T, ushort>(ref comparand)) == Unsafe.As<T, ushort>(ref comparand) :
|
||||
sizeof(T) == sizeof(uint) ? Interlocked.CompareExchange(ref Unsafe.As<T, uint>(ref location), Unsafe.As<T, uint>(ref value), Unsafe.As<T, uint>(ref comparand)) == Unsafe.As<T, uint>(ref comparand) :
|
||||
sizeof(T) == sizeof(ulong) ? Interlocked.CompareExchange(ref Unsafe.As<T, ulong>(ref location), Unsafe.As<T, ulong>(ref value), Unsafe.As<T, ulong>(ref comparand)) == Unsafe.As<T, ulong>(ref comparand) :
|
||||
throw new UnreachableException();
|
||||
|
||||
// The worker body. Each worker will execute this same body.
|
||||
Func<object, Task> taskBody = static async o =>
|
||||
{
|
||||
var state = (ForEachState<T>)o;
|
||||
var state = (ForAsyncState<T>)o;
|
||||
bool launchedNext = false;
|
||||
|
||||
#pragma warning disable CA2007 // Explicitly don't use ConfigureAwait, as we want to perform all work on the specified scheduler that's now current
|
||||
|
@ -126,10 +104,10 @@ namespace System.Threading.Tasks
|
|||
// Continue to loop while there are more elements to be processed.
|
||||
while (!state.Cancellation.IsCancellationRequested)
|
||||
{
|
||||
// Get the next element from the enumerator. For some types, we can get the next element with just
|
||||
// Get the next element from the enumerator. For primitive types, we can get the next element with just
|
||||
// interlocked operations, avoiding the need to take a lock. For other types, we need to take a lock.
|
||||
T element;
|
||||
if (Interlockable())
|
||||
if (typeof(T).IsPrimitive)
|
||||
{
|
||||
TryAgain:
|
||||
element = state.NextAvailable;
|
||||
|
@ -138,7 +116,7 @@ namespace System.Threading.Tasks
|
|||
break;
|
||||
}
|
||||
|
||||
if (!CompareExchange(ref state.NextAvailable, element + T.One, element))
|
||||
if (Interlocked.CompareExchange(ref state.NextAvailable, element + T.One, element) != element)
|
||||
{
|
||||
goto TryAgain;
|
||||
}
|
||||
|
@ -200,7 +178,7 @@ namespace System.Threading.Tasks
|
|||
{
|
||||
// Construct a state object that encapsulates all state to be passed and shared between
|
||||
// the workers, and queues the first worker.
|
||||
var state = new ForEachState<T>(fromInclusive, toExclusive, taskBody, !Interlockable(), dop, scheduler, cancellationToken, body);
|
||||
var state = new ForAsyncState<T>(fromInclusive, toExclusive, taskBody, dop, scheduler, cancellationToken, body);
|
||||
state.QueueWorkerIfDopAvailable();
|
||||
return state.Task;
|
||||
}
|
||||
|
@ -747,18 +725,18 @@ namespace System.Threading.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Stores the state associated with an IAsyncEnumerable ForEachAsync operation, shared between all its workers.</summary>
|
||||
/// <summary>Stores the state associated with an IAsyncEnumerable ForAsyncState operation, shared between all its workers.</summary>
|
||||
/// <typeparam name="T">Specifies the type of data being enumerated.</typeparam>
|
||||
private sealed class ForEachState<T> : ForEachAsyncState<T>, IDisposable
|
||||
private sealed class ForAsyncState<T> : ForEachAsyncState<T>, IDisposable
|
||||
{
|
||||
public T NextAvailable;
|
||||
public readonly T ToExclusive;
|
||||
|
||||
public ForEachState(
|
||||
public ForAsyncState(
|
||||
T fromExclusive, T toExclusive, Func<object, Task> taskBody,
|
||||
bool needsLock, int dop, TaskScheduler scheduler, CancellationToken cancellationToken,
|
||||
int dop, TaskScheduler scheduler, CancellationToken cancellationToken,
|
||||
Func<T, CancellationToken, ValueTask> body) :
|
||||
base(taskBody, needsLock, dop, scheduler, cancellationToken, body)
|
||||
base(taskBody, !typeof(T).IsPrimitive, dop, scheduler, cancellationToken, body)
|
||||
{
|
||||
NextAvailable = fromExclusive;
|
||||
ToExclusive = toExclusive;
|
||||
|
|
|
@ -211,9 +211,7 @@ namespace System.Threading.Tasks
|
|||
if (iteration < oldLBI)
|
||||
{
|
||||
SpinWait wait = default;
|
||||
while (typeof(TInt) == typeof(int) ?
|
||||
Interlocked.CompareExchange(ref Unsafe.As<TInt, int>(ref pflags._lowestBreakIteration), Unsafe.As<TInt, int>(ref iteration), Unsafe.As<TInt, int>(ref oldLBI)) != Unsafe.As<TInt, int>(ref oldLBI) :
|
||||
Interlocked.CompareExchange(ref Unsafe.As<TInt, long>(ref pflags._lowestBreakIteration), Unsafe.As<TInt, long>(ref iteration), Unsafe.As<TInt, long>(ref oldLBI)) != Unsafe.As<TInt, long>(ref oldLBI))
|
||||
while (Interlocked.CompareExchange(ref pflags._lowestBreakIteration, iteration, oldLBI) != oldLBI)
|
||||
{
|
||||
wait.SpinOnce();
|
||||
oldLBI = pflags.LowestBreakIteration;
|
||||
|
|
|
@ -33,8 +33,8 @@ namespace System.Threading.Tasks
|
|||
// that range, minimizing the chances it'll be near the objects from other threads.
|
||||
internal volatile StrongBox<long>? _nSharedCurrentIndexOffset;
|
||||
|
||||
// to be set to 1 by the worker that finishes this range. It's OK to do a non-interlocked write here.
|
||||
internal int _bRangeFinished;
|
||||
// to be set to true by the worker that finishes this range. It's OK to do a non-interlocked write here.
|
||||
internal bool _bRangeFinished;
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,7 +101,7 @@ namespace System.Threading.Tasks
|
|||
// local snap to save array access bounds checks in places where we only read fields
|
||||
IndexRange currentRange = _indexRanges[_nCurrentIndexRange];
|
||||
|
||||
if (currentRange._bRangeFinished == 0)
|
||||
if (!currentRange._bRangeFinished)
|
||||
{
|
||||
StrongBox<long>? sharedCurrentIndexOffset = _indexRanges[_nCurrentIndexRange]._nSharedCurrentIndexOffset;
|
||||
if (sharedCurrentIndexOffset == null)
|
||||
|
@ -157,7 +157,7 @@ namespace System.Threading.Tasks
|
|||
else
|
||||
{
|
||||
// this index range is completed, mark it so that others can skip it quickly
|
||||
Interlocked.Exchange(ref _indexRanges[_nCurrentIndexRange]._bRangeFinished, 1);
|
||||
Interlocked.Exchange(ref _indexRanges[_nCurrentIndexRange]._bRangeFinished, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ namespace System.Threading.Tasks
|
|||
// the fromInclusive of the new index range is always on nCurrentIndex
|
||||
_indexRanges[i]._nFromInclusive = nCurrentIndex;
|
||||
_indexRanges[i]._nSharedCurrentIndexOffset = null;
|
||||
_indexRanges[i]._bRangeFinished = 0;
|
||||
_indexRanges[i]._bRangeFinished = false;
|
||||
|
||||
// now increment it to find the toExclusive value for our range
|
||||
nCurrentIndex = unchecked(nCurrentIndex + nRangeSize);
|
||||
|
|
|
@ -452,7 +452,7 @@ namespace System.Threading.ThreadPools.Tests
|
|||
bool waitForWorkStart = false;
|
||||
var workStarted = new AutoResetEvent(false);
|
||||
var localWorkScheduled = new AutoResetEvent(false);
|
||||
int completeWork = 0;
|
||||
bool completeWork = false;
|
||||
int queuedWorkCount = 0;
|
||||
var allWorkCompleted = new ManualResetEvent(false);
|
||||
Exception backgroundEx = null;
|
||||
|
@ -467,7 +467,7 @@ namespace System.Threading.ThreadPools.Tests
|
|||
// Blocking can affect thread pool thread injection heuristics, so don't block, pretend like a
|
||||
// long-running CPU-bound work item
|
||||
ThreadTestHelpers.WaitForConditionWithoutRelinquishingTimeSlice(
|
||||
() => Interlocked.CompareExchange(ref completeWork, 0, 0) != 0);
|
||||
() => Interlocked.CompareExchange(ref completeWork, false, false));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -557,7 +557,7 @@ namespace System.Threading.ThreadPools.Tests
|
|||
finally
|
||||
{
|
||||
// Complete the work
|
||||
Interlocked.Exchange(ref completeWork, 1);
|
||||
Interlocked.Exchange(ref completeWork, true);
|
||||
}
|
||||
|
||||
// Wait for work items to exit, for counting
|
||||
|
|
|
@ -269,7 +269,7 @@ namespace System.Threading
|
|||
[System.CLSCompliantAttribute(false)]
|
||||
public static ulong CompareExchange(ref ulong location1, ulong value, ulong comparand) { throw null; }
|
||||
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
|
||||
public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class? { throw null; }
|
||||
public static T CompareExchange<T>(ref T location1, T value, T comparand) { throw null; }
|
||||
public static int Decrement(ref int location) { throw null; }
|
||||
public static long Decrement(ref long location) { throw null; }
|
||||
[System.CLSCompliantAttribute(false)]
|
||||
|
@ -296,7 +296,7 @@ namespace System.Threading
|
|||
[System.CLSCompliantAttribute(false)]
|
||||
public static ulong Exchange(ref ulong location1, ulong value) { throw null; }
|
||||
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
|
||||
public static T Exchange<T>([System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("value")] ref T location1, T value) where T : class? { throw null; }
|
||||
public static T Exchange<T>([System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("value")] ref T location1, T value) { throw null; }
|
||||
public static int Increment(ref int location) { throw null; }
|
||||
public static long Increment(ref long location) { throw null; }
|
||||
[System.CLSCompliantAttribute(false)]
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Buffers;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
@ -145,9 +146,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<sbyte> memory = BoundedMemory.Allocate<sbyte>(1);
|
||||
ref sbyte value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange(ref value, 123));
|
||||
Assert.Equal(123, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange<sbyte>(ref value, 123));
|
||||
Assert.Equal(123, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<byte>(), 123));
|
||||
}
|
||||
|
||||
|
@ -156,9 +163,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<byte> memory = BoundedMemory.Allocate<byte>(1);
|
||||
ref byte value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange(ref value, 123));
|
||||
Assert.Equal(123u, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange<byte>(ref value, 123));
|
||||
Assert.Equal(123u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<sbyte>(), 123));
|
||||
}
|
||||
|
||||
|
@ -167,9 +180,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<short> memory = BoundedMemory.Allocate<short>(1);
|
||||
ref short value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange(ref value, 12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange<short>(ref value, 12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<short>(), 12345));
|
||||
}
|
||||
|
||||
|
@ -178,9 +197,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<ushort> memory = BoundedMemory.Allocate<ushort>(1);
|
||||
ref ushort value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange(ref value, 12345));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange<ushort>(ref value, 12345));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<ushort>(), 12345));
|
||||
}
|
||||
|
||||
|
@ -189,9 +214,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<int> memory = BoundedMemory.Allocate<int>(1);
|
||||
ref int value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange(ref value, 12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange<int>(ref value, 12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<int>(), 12345));
|
||||
}
|
||||
|
||||
|
@ -200,9 +231,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<uint> memory = BoundedMemory.Allocate<uint>(1);
|
||||
ref uint value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange(ref value, 12345u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange<uint>(ref value, 12345u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<uint>(), 12345));
|
||||
}
|
||||
|
||||
|
@ -211,9 +248,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<long> memory = BoundedMemory.Allocate<long>(1);
|
||||
ref long value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange(ref value, 12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, Interlocked.Exchange<long>(ref value, 12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<long>(), 12345));
|
||||
}
|
||||
|
||||
|
@ -222,14 +265,22 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<nint> memory = BoundedMemory.Allocate<nint>(1);
|
||||
ref nint value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, (nint)Interlocked.Exchange(ref value, (nint)12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42, (nint)Interlocked.Exchange<nint>(ref value, (nint)12345));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
Assert.Equal(12345, (nint)Interlocked.Exchange(ref value, unchecked((nint)1 + int.MaxValue)));
|
||||
Assert.Equal(unchecked((nint)1 + int.MaxValue), value);
|
||||
|
||||
Assert.Equal(unchecked((nint)1 + int.MaxValue), (nint)Interlocked.Exchange(ref value, unchecked((nint)2 + int.MaxValue)));
|
||||
Assert.Equal(unchecked((nint)2 + int.MaxValue), value);
|
||||
}
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<nint>(), 12345));
|
||||
}
|
||||
|
@ -239,9 +290,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<ulong> memory = BoundedMemory.Allocate<ulong>(1);
|
||||
ref ulong value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange(ref value, 12345u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, Interlocked.Exchange<ulong>(ref value, 12345u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<ulong>(), 12345));
|
||||
}
|
||||
|
||||
|
@ -250,14 +307,22 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<nuint> memory = BoundedMemory.Allocate<nuint>(1);
|
||||
ref nuint value = ref memory.Span[0];
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, (nuint)Interlocked.Exchange(ref value, (nuint)12345u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
Assert.Equal(42u, (nuint)Interlocked.Exchange<nuint>(ref value, (nuint)12345u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
Assert.Equal(12345u, (nuint)Interlocked.Exchange(ref value, unchecked((nuint)1 + uint.MaxValue)));
|
||||
Assert.Equal(unchecked((nuint)1 + uint.MaxValue), value);
|
||||
|
||||
Assert.Equal(unchecked((nuint)1 + uint.MaxValue), (nuint)Interlocked.Exchange(ref value, unchecked((nuint)2 + uint.MaxValue)));
|
||||
Assert.Equal(unchecked((nuint)2 + uint.MaxValue), value);
|
||||
}
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<nuint>(), 12345));
|
||||
}
|
||||
|
@ -267,9 +332,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<float> memory = BoundedMemory.Allocate<float>(1);
|
||||
ref float value = ref memory.Span[0];
|
||||
|
||||
value = 42.1f;
|
||||
Assert.Equal(42.1f, Interlocked.Exchange(ref value, 12345.1f));
|
||||
Assert.Equal(12345.1f, value);
|
||||
|
||||
value = 42.1f;
|
||||
Assert.Equal(42.1f, Interlocked.Exchange<float>(ref value, 12345.1f));
|
||||
Assert.Equal(12345.1f, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<float>(), 12345.1f));
|
||||
}
|
||||
|
||||
|
@ -278,9 +349,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
using BoundedMemory<double> memory = BoundedMemory.Allocate<double>(1);
|
||||
ref double value = ref memory.Span[0];
|
||||
|
||||
value = 42.1;
|
||||
Assert.Equal(42.1, Interlocked.Exchange(ref value, 12345.1));
|
||||
Assert.Equal(12345.1, value);
|
||||
|
||||
value = 42.1;
|
||||
Assert.Equal(42.1, Interlocked.Exchange<double>(ref value, 12345.1));
|
||||
Assert.Equal(12345.1, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<double>(), 12345.1));
|
||||
}
|
||||
|
||||
|
@ -289,10 +366,15 @@ namespace System.Threading.Tests
|
|||
{
|
||||
var oldValue = new object();
|
||||
var newValue = new object();
|
||||
object value = oldValue;
|
||||
|
||||
object value = oldValue;
|
||||
Assert.Same(oldValue, Interlocked.Exchange(ref value, newValue));
|
||||
Assert.Same(newValue, value);
|
||||
|
||||
value = oldValue;
|
||||
Assert.Same(oldValue, Interlocked.Exchange<object>(ref value, newValue));
|
||||
Assert.Same(newValue, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<object>(), null));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.Exchange(ref Unsafe.NullRef<object>(), newValue));
|
||||
}
|
||||
|
@ -311,6 +393,20 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(12345, (int)value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedExchange_Unsupported()
|
||||
{
|
||||
DateTime value1 = default;
|
||||
TimeSpan value2 = default;
|
||||
Rune value3 = default;
|
||||
ValueTask value4 = default;
|
||||
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.Exchange(ref value1, default));
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.Exchange(ref value2, default));
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.Exchange(ref value3, default));
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.Exchange(ref value4, default));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedCompareExchange_Int8()
|
||||
{
|
||||
|
@ -324,7 +420,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42, Interlocked.CompareExchange(ref value, 123, 42));
|
||||
Assert.Equal(123, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<byte>(), 123, 41));
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<sbyte>(ref value, 123, 41));
|
||||
Assert.Equal(42, value);
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<sbyte>(ref value, 123, 42));
|
||||
Assert.Equal(123, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<sbyte>(), 123, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<sbyte>(ref Unsafe.NullRef<sbyte>(), 123, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -340,7 +445,32 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42u, Interlocked.CompareExchange(ref value, 123, 42));
|
||||
Assert.Equal(123u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<sbyte>(), 123, 41));
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<byte>(ref value, 123, 41));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<byte>(ref value, 123, 42));
|
||||
Assert.Equal(123u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<byte>(), 123, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<byte>(ref Unsafe.NullRef<byte>(), 123, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedCompareExchange_Bool()
|
||||
{
|
||||
using BoundedMemory<bool> memory = BoundedMemory.Allocate<bool>(1);
|
||||
ref bool value = ref memory.Span[0];
|
||||
value = false;
|
||||
|
||||
Assert.False(Interlocked.CompareExchange<bool>(ref value, true, false));
|
||||
Assert.True(value);
|
||||
|
||||
Assert.True(Interlocked.CompareExchange<bool>(ref value, false, false));
|
||||
Assert.True(value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<bool>(ref Unsafe.NullRef<bool>(), false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -356,7 +486,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<short>(ref value, 12345, 41));
|
||||
Assert.Equal(42, value);
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<short>(ref value, 12345, 42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<short>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<short>(ref Unsafe.NullRef<short>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -372,7 +511,32 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345, 42));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<ushort>(ref value, 12345, 41));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<ushort>(ref value, 12345, 42));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<ushort>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<ushort>(ref Unsafe.NullRef<ushort>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedCompareExchange_Char()
|
||||
{
|
||||
using BoundedMemory<char> memory = BoundedMemory.Allocate<char>(1);
|
||||
ref char value = ref memory.Span[0];
|
||||
value = (char)42;
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<char>(ref value, (char)12345, (char)41));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<char>(ref value, (char)12345, (char)42));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<char>(ref Unsafe.NullRef<char>(), (char)12345, (char)41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -388,7 +552,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<int>(ref value, 12345, 41));
|
||||
Assert.Equal(42, value);
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<int>(ref value, 12345, 42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<int>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<int>(ref Unsafe.NullRef<int>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -404,7 +577,32 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345u, 42u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<uint>(ref value, 12345u, 41u));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<uint>(ref value, 12345u, 42u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<uint>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<uint>(ref Unsafe.NullRef<uint>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedCompareExchange_Enum()
|
||||
{
|
||||
using BoundedMemory<DayOfWeek> memory = BoundedMemory.Allocate<DayOfWeek>(1);
|
||||
ref DayOfWeek value = ref memory.Span[0];
|
||||
value = DayOfWeek.Monday;
|
||||
|
||||
Assert.Equal(DayOfWeek.Monday, Interlocked.CompareExchange<DayOfWeek>(ref value, DayOfWeek.Tuesday, DayOfWeek.Monday));
|
||||
Assert.Equal(DayOfWeek.Tuesday, value);
|
||||
|
||||
Assert.Equal(DayOfWeek.Tuesday, Interlocked.CompareExchange<DayOfWeek>(ref value, DayOfWeek.Wednesday, DayOfWeek.Monday));
|
||||
Assert.Equal(DayOfWeek.Tuesday, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<DayOfWeek>(ref Unsafe.NullRef<DayOfWeek>(), DayOfWeek.Monday, DayOfWeek.Tuesday));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -420,7 +618,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<long>(ref value, 12345, 41));
|
||||
Assert.Equal(42, value);
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<long>(ref value, 12345, 42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<long>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<long>(ref Unsafe.NullRef<long>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -430,19 +637,31 @@ namespace System.Threading.Tests
|
|||
ref nint value = ref memory.Span[0];
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42, (nint)Interlocked.CompareExchange(ref value, (nint)12345, (nint)41));
|
||||
Assert.Equal(42, Interlocked.CompareExchange(ref value, (nint)12345, (nint)41));
|
||||
Assert.Equal(42, value);
|
||||
|
||||
Assert.Equal(42, (nint)Interlocked.CompareExchange(ref value, (nint)12345, (nint)42));
|
||||
Assert.Equal(42, Interlocked.CompareExchange(ref value, (nint)12345, (nint)42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<nint>(ref value, (nint)12345, (nint)41));
|
||||
Assert.Equal(42, value);
|
||||
|
||||
Assert.Equal(42, Interlocked.CompareExchange<nint>(ref value, (nint)12345, (nint)42));
|
||||
Assert.Equal(12345, value);
|
||||
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
Assert.Equal(12345, (nint)Interlocked.CompareExchange(ref value, unchecked((nint)1 + int.MaxValue), (nint)12345u));
|
||||
Assert.Equal(12345, Interlocked.CompareExchange(ref value, unchecked((nint)1 + int.MaxValue), (nint)12345));
|
||||
Assert.Equal(unchecked((nint)1 + int.MaxValue), value);
|
||||
|
||||
Assert.Equal(unchecked((nint)1 + int.MaxValue), Interlocked.CompareExchange<nint>(ref value, unchecked((nint)2 + int.MaxValue), unchecked((nint)1 + int.MaxValue)));
|
||||
Assert.Equal(unchecked((nint)2 + int.MaxValue), value);
|
||||
}
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<nint>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<nint>(ref Unsafe.NullRef<nint>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -458,7 +677,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345u, 42u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<ulong>(ref value, 12345u, 41u));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<ulong>(ref value, 12345u, 42u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<ulong>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<ulong>(ref Unsafe.NullRef<ulong>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -468,19 +696,31 @@ namespace System.Threading.Tests
|
|||
ref nuint value = ref memory.Span[0];
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42u, (nuint)Interlocked.CompareExchange(ref value, (nuint)12345u, (nuint)41u));
|
||||
Assert.Equal(42u, Interlocked.CompareExchange(ref value, (nuint)12345u, (nuint)41u));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, (nuint)Interlocked.CompareExchange(ref value, (nuint)12345u, (nuint)42u));
|
||||
Assert.Equal(42u, Interlocked.CompareExchange(ref value, (nuint)12345u, (nuint)42u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
value = 42;
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<nuint>(ref value, (nuint)12345u, (nuint)41u));
|
||||
Assert.Equal(42u, value);
|
||||
|
||||
Assert.Equal(42u, Interlocked.CompareExchange<nuint>(ref value, (nuint)12345u, (nuint)42u));
|
||||
Assert.Equal(12345u, value);
|
||||
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
Assert.Equal(12345u, (nuint)Interlocked.CompareExchange(ref value, unchecked((nuint)1 + uint.MaxValue), (nuint)12345u));
|
||||
Assert.Equal(unchecked((nuint)1 + uint.MaxValue), value);
|
||||
|
||||
Assert.Equal(unchecked((nuint)1 + uint.MaxValue), Interlocked.CompareExchange<nuint>(ref value, unchecked((nuint)2 + uint.MaxValue), unchecked((nuint)1 + uint.MaxValue)));
|
||||
Assert.Equal(unchecked((nuint)2 + uint.MaxValue), value);
|
||||
}
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<nuint>(), 12345, 41));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<nuint>(ref Unsafe.NullRef<nuint>(), 12345, 41));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -496,7 +736,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42.1f, Interlocked.CompareExchange(ref value, 12345.1f, 42.1f));
|
||||
Assert.Equal(12345.1f, value);
|
||||
|
||||
value = 42.1f;
|
||||
|
||||
Assert.Equal(42.1f, Interlocked.CompareExchange<float>(ref value, 12345.1f, 41.1f));
|
||||
Assert.Equal(42.1f, value);
|
||||
|
||||
Assert.Equal(42.1f, Interlocked.CompareExchange<float>(ref value, 12345.1f, 42.1f));
|
||||
Assert.Equal(12345.1f, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<float>(), 12345.1f, 41.1f));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<float>(ref Unsafe.NullRef<float>(), 12345.1f, 41.1f));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -512,7 +761,16 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(42.1, Interlocked.CompareExchange(ref value, 12345.1, 42.1));
|
||||
Assert.Equal(12345.1, value);
|
||||
|
||||
value = 42.1;
|
||||
|
||||
Assert.Equal(42.1, Interlocked.CompareExchange<double>(ref value, 12345.1, 41.1));
|
||||
Assert.Equal(42.1, value);
|
||||
|
||||
Assert.Equal(42.1, Interlocked.CompareExchange<double>(ref value, 12345.1, 42.1));
|
||||
Assert.Equal(12345.1, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<double>(), 12345.1, 41.1));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<double>(ref Unsafe.NullRef<double>(), 12345.1, 41.1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -528,8 +786,18 @@ namespace System.Threading.Tests
|
|||
Assert.Same(oldValue, Interlocked.CompareExchange(ref value, newValue, oldValue));
|
||||
Assert.Same(newValue, value);
|
||||
|
||||
value = oldValue;
|
||||
|
||||
Assert.Same(oldValue, Interlocked.CompareExchange<object>(ref value, newValue, new object()));
|
||||
Assert.Same(oldValue, value);
|
||||
|
||||
Assert.Same(oldValue, Interlocked.CompareExchange<object>(ref value, newValue, oldValue));
|
||||
Assert.Same(newValue, value);
|
||||
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<object>(), null, null));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange(ref Unsafe.NullRef<object>(), newValue, oldValue));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<object>(ref Unsafe.NullRef<object>(), null, null));
|
||||
Assert.Throws<NullReferenceException>(() => Interlocked.CompareExchange<object>(ref Unsafe.NullRef<object>(), newValue, oldValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -552,6 +820,20 @@ namespace System.Threading.Tests
|
|||
Assert.Equal(12345, (int)value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedCompareExchange_Unsupported()
|
||||
{
|
||||
DateTime value1 = default;
|
||||
TimeSpan value2 = default;
|
||||
Rune value3 = default;
|
||||
ValueTask value4 = default;
|
||||
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.CompareExchange(ref value1, default, default));
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.CompareExchange(ref value2, default, default));
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.CompareExchange(ref value3, default, default));
|
||||
Assert.Throws<NotSupportedException>(() => Interlocked.CompareExchange(ref value4, default, default));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InterlockedRead_Int64()
|
||||
{
|
||||
|
|
|
@ -72,47 +72,10 @@ namespace System.Threading
|
|||
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||||
public static extern long CompareExchange(ref long location1, long value, long comparand);
|
||||
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
[Intrinsic]
|
||||
public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class?
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref location1))
|
||||
throw new NullReferenceException();
|
||||
// Besides avoiding coop handles for efficiency,
|
||||
// and correctness, this also appears needed to
|
||||
// avoid an assertion failure in the runtime, related to
|
||||
// coop handles over generics.
|
||||
//
|
||||
// See CompareExchange(object) for comments.
|
||||
//
|
||||
// This is not entirely convincing due to lack of volatile.
|
||||
//
|
||||
T? result = null;
|
||||
// T : class so call the object overload.
|
||||
CompareExchange(ref Unsafe.As<T, object?>(ref location1), ref Unsafe.As<T, object?>(ref value), ref Unsafe.As<T, object?>(ref comparand), ref Unsafe.As<T, object?>(ref result!));
|
||||
return result;
|
||||
}
|
||||
|
||||
[Intrinsic]
|
||||
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||||
public static extern long Exchange(ref long location1, long value);
|
||||
|
||||
[return: NotNullIfNotNull(nameof(location1))]
|
||||
[Intrinsic]
|
||||
public static T Exchange<T>([NotNullIfNotNull(nameof(value))] ref T location1, T value) where T : class?
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref location1))
|
||||
throw new NullReferenceException();
|
||||
// See CompareExchange(T) for comments.
|
||||
//
|
||||
// This is not entirely convincing due to lack of volatile.
|
||||
//
|
||||
T? result = null;
|
||||
// T : class so call the object overload.
|
||||
Exchange(ref Unsafe.As<T, object?>(ref location1), ref Unsafe.As<T, object?>(ref value), ref Unsafe.As<T, object?>(ref result!));
|
||||
return result;
|
||||
}
|
||||
|
||||
[Intrinsic]
|
||||
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||||
public static extern long Read(ref long location);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue