diff options
Diffstat (limited to 'src/mscorlib/src/System/Threading')
63 files changed, 3138 insertions, 3725 deletions
diff --git a/src/mscorlib/src/System/Threading/AbandonedMutexException.cs b/src/mscorlib/src/System/Threading/AbandonedMutexException.cs deleted file mode 100644 index 6b4977fbc5..0000000000 --- a/src/mscorlib/src/System/Threading/AbandonedMutexException.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -// -// AbandonedMutexException -// Thrown when a wait completes because one or more mutexes was abandoned. -// AbandonedMutexs indicate serious error in user code or machine state. -//////////////////////////////////////////////////////////////////////////////// - -namespace System.Threading { - - using System; - using System.Runtime.Serialization; - using System.Threading; - using System.Runtime.InteropServices; - - [Serializable] - [ComVisibleAttribute(false)] - public class AbandonedMutexException : SystemException { - - private int m_MutexIndex = -1; - private Mutex m_Mutex = null; - - public AbandonedMutexException() - : base(Environment.GetResourceString("Threading.AbandonedMutexException")) { - SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX); - } - - public AbandonedMutexException(String message) - : base(message) { - SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX); - } - - public AbandonedMutexException(String message, Exception inner ) - : base(message, inner) { - SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX); - } - - public AbandonedMutexException(int location, WaitHandle handle) - : base(Environment.GetResourceString("Threading.AbandonedMutexException")) { - SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX); - SetupException(location,handle); - } - - public AbandonedMutexException(String message,int location, WaitHandle handle) - : base(message) { - SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX); - SetupException(location,handle); - } - - public AbandonedMutexException(String message, Exception inner,int location, WaitHandle handle ) - : base(message, inner) { - SetErrorCode(__HResults.COR_E_ABANDONEDMUTEX); - SetupException(location,handle); - } - - private void SetupException(int location, WaitHandle handle) - { - m_MutexIndex = location; - if(handle != null) - m_Mutex = handle as Mutex; - } - - protected AbandonedMutexException(SerializationInfo info, StreamingContext context) : base(info, context) { - } - - public Mutex Mutex - { - get { - return m_Mutex; - } - } - - public int MutexIndex - { - get{ - return m_MutexIndex; - } - } - - } -} - diff --git a/src/mscorlib/src/System/Threading/ApartmentState.cs b/src/mscorlib/src/System/Threading/ApartmentState.cs deleted file mode 100644 index 1edf0af98a..0000000000 --- a/src/mscorlib/src/System/Threading/ApartmentState.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: Enum to represent the different threading models -** -** -=============================================================================*/ - -namespace System.Threading { - - [Serializable] - public enum ApartmentState - { - /*========================================================================= - ** Constants for thread apartment states. - =========================================================================*/ - STA = 0, - MTA = 1, - Unknown = 2 - } -} diff --git a/src/mscorlib/src/System/Threading/AsyncLocal.cs b/src/mscorlib/src/System/Threading/AsyncLocal.cs deleted file mode 100644 index 8c4319bd2c..0000000000 --- a/src/mscorlib/src/System/Threading/AsyncLocal.cs +++ /dev/null @@ -1,487 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Security; - -namespace System.Threading -{ - // - // AsyncLocal<T> represents "ambient" data that is local to a given asynchronous control flow, such as an - // async method. For example, say you want to associate a culture with a given async flow: - // - // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(); - // - // static async Task SomeOperationAsync(Culture culture) - // { - // s_currentCulture.Value = culture; - // - // await FooAsync(); - // } - // - // static async Task FooAsync() - // { - // PrintStringWithCulture(s_currentCulture.Value); - // } - // - // AsyncLocal<T> also provides optional notifications when the value associated with the current thread - // changes, either because it was explicitly changed by setting the Value property, or implicitly changed - // when the thread encountered an "await" or other context transition. For example, we might want our - // current culture to be communicated to the OS as well: - // - // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>( - // args => - // { - // NativeMethods.SetThreadCulture(args.CurrentValue.LCID); - // }); - // - public sealed class AsyncLocal<T> : IAsyncLocal - { - private readonly Action<AsyncLocalValueChangedArgs<T>> m_valueChangedHandler; - - // - // Constructs an AsyncLocal<T> that does not receive change notifications. - // - public AsyncLocal() - { - } - - // - // Constructs an AsyncLocal<T> with a delegate that is called whenever the current value changes - // on any thread. - // - public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler) - { - m_valueChangedHandler = valueChangedHandler; - } - - public T Value - { - get - { - object obj = ExecutionContext.GetLocalValue(this); - return (obj == null) ? default(T) : (T)obj; - } - set - { - ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null); - } - } - - void IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged) - { - Debug.Assert(m_valueChangedHandler != null); - T previousValue = previousValueObj == null ? default(T) : (T)previousValueObj; - T currentValue = currentValueObj == null ? default(T) : (T)currentValueObj; - m_valueChangedHandler(new AsyncLocalValueChangedArgs<T>(previousValue, currentValue, contextChanged)); - } - } - - // - // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal<T> type. - // - internal interface IAsyncLocal - { - void OnValueChanged(object previousValue, object currentValue, bool contextChanged); - } - - public struct AsyncLocalValueChangedArgs<T> - { - public T PreviousValue { get; private set; } - public T CurrentValue { get; private set; } - - // - // If the value changed because we changed to a different ExecutionContext, this is true. If it changed - // because someone set the Value property, this is false. - // - public bool ThreadContextChanged { get; private set; } - - internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged) - : this() - { - PreviousValue = previousValue; - CurrentValue = currentValue; - ThreadContextChanged = contextChanged; - } - } - - // - // Interface used to store an IAsyncLocal => object mapping in ExecutionContext. - // Implementations are specialized based on the number of elements in the immutable - // map in order to minimize memory consumption and look-up times. - // - interface IAsyncLocalValueMap - { - bool TryGetValue(IAsyncLocal key, out object value); - IAsyncLocalValueMap Set(IAsyncLocal key, object value); - } - - // - // Utility functions for getting/creating instances of IAsyncLocalValueMap - // - internal static class AsyncLocalValueMap - { - public static IAsyncLocalValueMap Empty { get; } = new EmptyAsyncLocalValueMap(); - - // Instance without any key/value pairs. Used as a singleton/ - private sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap - { - public IAsyncLocalValueMap Set(IAsyncLocal key, object value) - { - // If the value isn't null, then create a new one-element map to store - // the key/value pair. If it is null, then we're still empty. - return value != null ? - new OneElementAsyncLocalValueMap(key, value) : - (IAsyncLocalValueMap)this; - } - - public bool TryGetValue(IAsyncLocal key, out object value) - { - value = null; - return false; - } - } - - // Instance with one key/value pair. - private sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap - { - private readonly IAsyncLocal _key1; - private readonly object _value1; - - public OneElementAsyncLocalValueMap(IAsyncLocal key, object value) - { - _key1 = key; _value1 = value; - } - - public IAsyncLocalValueMap Set(IAsyncLocal key, object value) - { - if (value != null) - { - // The value is non-null. If the key matches one already contained in this map, - // then create a new one-element map with the updated value, otherwise create - // a two-element map with the additional key/value. - return ReferenceEquals(key, _key1) ? - new OneElementAsyncLocalValueMap(key, value) : - (IAsyncLocalValueMap)new TwoElementAsyncLocalValueMap(_key1, _value1, key, value); - } - else - { - // The value is null. If the key exists in this map, remove it by downgrading to an empty map. - // Otherwise, there's nothing to add or remove, so just return this map. - return ReferenceEquals(key, _key1) ? - Empty : - (IAsyncLocalValueMap)this; - } - } - - public bool TryGetValue(IAsyncLocal key, out object value) - { - if (ReferenceEquals(key, _key1)) - { - value = _value1; - return true; - } - else - { - value = null; - return false; - } - } - } - - // Instance with two key/value pairs. - private sealed class TwoElementAsyncLocalValueMap : IAsyncLocalValueMap - { - private readonly IAsyncLocal _key1, _key2; - private readonly object _value1, _value2; - - public TwoElementAsyncLocalValueMap(IAsyncLocal key1, object value1, IAsyncLocal key2, object value2) - { - _key1 = key1; _value1 = value1; - _key2 = key2; _value2 = value2; - } - - public IAsyncLocalValueMap Set(IAsyncLocal key, object value) - { - if (value != null) - { - // The value is non-null. If the key matches one already contained in this map, - // then create a new two-element map with the updated value, otherwise create - // a three-element map with the additional key/value. - return - ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(key, value, _key2, _value2) : - ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, key, value) : - (IAsyncLocalValueMap)new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value); - } - else - { - // The value is null. If the key exists in this map, remove it by downgrading to a one-element map - // without the key. Otherwise, there's nothing to add or remove, so just return this map. - return - ReferenceEquals(key, _key1) ? new OneElementAsyncLocalValueMap(_key2, _value2) : - ReferenceEquals(key, _key2) ? new OneElementAsyncLocalValueMap(_key1, _value1) : - (IAsyncLocalValueMap)this; - } - } - - public bool TryGetValue(IAsyncLocal key, out object value) - { - if (ReferenceEquals(key, _key1)) - { - value = _value1; - return true; - } - else if (ReferenceEquals(key, _key2)) - { - value = _value2; - return true; - } - else - { - value = null; - return false; - } - } - } - - // Instance with three key/value pairs. - private sealed class ThreeElementAsyncLocalValueMap : IAsyncLocalValueMap - { - private readonly IAsyncLocal _key1, _key2, _key3; - private readonly object _value1, _value2, _value3; - - public ThreeElementAsyncLocalValueMap(IAsyncLocal key1, object value1, IAsyncLocal key2, object value2, IAsyncLocal key3, object value3) - { - _key1 = key1; _value1 = value1; - _key2 = key2; _value2 = value2; - _key3 = key3; _value3 = value3; - } - - public IAsyncLocalValueMap Set(IAsyncLocal key, object value) - { - if (value != null) - { - // The value is non-null. If the key matches one already contained in this map, - // then create a new three-element map with the updated value. - if (ReferenceEquals(key, _key1)) return new ThreeElementAsyncLocalValueMap(key, value, _key2, _value2, _key3, _value3); - if (ReferenceEquals(key, _key2)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, key, value, _key3, _value3); - if (ReferenceEquals(key, _key3)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value); - - // The key doesn't exist in this map, so upgrade to a multi map that contains - // the additional key/value pair. - var multi = new MultiElementAsyncLocalValueMap(4); - multi.UnsafeStore(0, _key1, _value1); - multi.UnsafeStore(1, _key2, _value2); - multi.UnsafeStore(2, _key3, _value3); - multi.UnsafeStore(3, key, value); - return multi; - } - else - { - // The value is null. If the key exists in this map, remove it by downgrading to a two-element map - // without the key. Otherwise, there's nothing to add or remove, so just return this map. - return - ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(_key2, _value2, _key3, _value3) : - ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key3, _value3) : - ReferenceEquals(key, _key3) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key2, _value2) : - (IAsyncLocalValueMap)this; - } - } - - public bool TryGetValue(IAsyncLocal key, out object value) - { - if (ReferenceEquals(key, _key1)) - { - value = _value1; - return true; - } - else if (ReferenceEquals(key, _key2)) - { - value = _value2; - return true; - } - else if (ReferenceEquals(key, _key3)) - { - value = _value3; - return true; - } - else - { - value = null; - return false; - } - } - } - - // Instance with up to 16 key/value pairs. - private sealed class MultiElementAsyncLocalValueMap : IAsyncLocalValueMap - { - internal const int MaxMultiElements = 16; - private readonly KeyValuePair<IAsyncLocal, object>[] _keyValues; - - internal MultiElementAsyncLocalValueMap(int count) - { - Debug.Assert(count <= MaxMultiElements); - _keyValues = new KeyValuePair<IAsyncLocal, object>[count]; - } - - internal void UnsafeStore(int index, IAsyncLocal key, object value) - { - Debug.Assert(index < _keyValues.Length); - _keyValues[index] = new KeyValuePair<IAsyncLocal, object>(key, value); - } - - public IAsyncLocalValueMap Set(IAsyncLocal key, object value) - { - // Find the key in this map. - for (int i = 0; i < _keyValues.Length; i++) - { - if (ReferenceEquals(key, _keyValues[i].Key)) - { - // The key is in the map. If the value isn't null, then create a new map of the same - // size that has all of the same pairs, with this new key/value pair overwriting the old. - if (value != null) - { - var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length); - Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length); - multi._keyValues[i] = new KeyValuePair<IAsyncLocal, object>(key, value); - return multi; - } - else if (_keyValues.Length == 4) - { - // The value is null, and we only have four elements, one of which we're removing, - // so downgrade to a three-element map, without the matching element. - return - i == 0 ? new ThreeElementAsyncLocalValueMap(_keyValues[1].Key, _keyValues[1].Value, _keyValues[2].Key, _keyValues[2].Value, _keyValues[3].Key, _keyValues[3].Value) : - i == 1 ? new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[2].Key, _keyValues[2].Value, _keyValues[3].Key, _keyValues[3].Value) : - i == 2 ? new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[1].Key, _keyValues[1].Value, _keyValues[3].Key, _keyValues[3].Value) : - (IAsyncLocalValueMap)new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[1].Key, _keyValues[1].Value, _keyValues[2].Key, _keyValues[2].Value); - } - else - { - // The value is null, and we have enough elements remaining to warrant a multi map. - // Create a new one and copy all of the elements from this one, except the one to be removed. - var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length - 1); - if (i != 0) Array.Copy(_keyValues, 0, multi._keyValues, 0, i); - if (i != _keyValues.Length - 1) Array.Copy(_keyValues, i + 1, multi._keyValues, i, _keyValues.Length - i - 1); - return multi; - } - } - } - - // The key does not already exist in this map. - - // If the value is null, then we can simply return this same map, as there's nothing to add or remove. - if (value == null) - { - return this; - } - - // We need to create a new map that has the additional key/value pair. - // If with the addition we can still fit in a multi map, create one. - if (_keyValues.Length < MaxMultiElements) - { - var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length + 1); - Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length); - multi._keyValues[_keyValues.Length] = new KeyValuePair<IAsyncLocal, object>(key, value); - return multi; - } - - // Otherwise, upgrade to a many map. - var many = new ManyElementAsyncLocalValueMap(MaxMultiElements + 1); - foreach (KeyValuePair<IAsyncLocal, object> pair in _keyValues) - { - many[pair.Key] = pair.Value; - } - many[key] = value; - return many; - } - - public bool TryGetValue(IAsyncLocal key, out object value) - { - foreach (KeyValuePair<IAsyncLocal, object> pair in _keyValues) - { - if (ReferenceEquals(key, pair.Key)) - { - value = pair.Value; - return true; - } - } - value = null; - return false; - } - } - - // Instance with any number of key/value pairs. - private sealed class ManyElementAsyncLocalValueMap : Dictionary<IAsyncLocal, object>, IAsyncLocalValueMap - { - public ManyElementAsyncLocalValueMap(int capacity) : base(capacity) { } - - public IAsyncLocalValueMap Set(IAsyncLocal key, object value) - { - int count = Count; - bool containsKey = ContainsKey(key); - - // If the value being set exists, create a new many map, copy all of the elements from this one, - // and then store the new key/value pair into it. This is the most common case. - if (value != null) - { - var map = new ManyElementAsyncLocalValueMap(count + (containsKey ? 0 : 1)); - foreach (KeyValuePair<IAsyncLocal, object> pair in this) - { - map[pair.Key] = pair.Value; - } - map[key] = value; - return map; - } - - // Otherwise, the value is null, which means null is being stored into an AsyncLocal.Value. - // Since there's no observable difference at the API level between storing null and the key - // not existing at all, we can downgrade to a smaller map rather than storing null. - - // If the key is contained in this map, we're going to create a new map that's one pair smaller. - if (containsKey) - { - // If the new count would be within range of a multi map instead of a many map, - // downgrade to the many map, which uses less memory and is faster to access. - // Otherwise, just create a new many map that's missing this key. - if (count == MultiElementAsyncLocalValueMap.MaxMultiElements + 1) - { - var multi = new MultiElementAsyncLocalValueMap(MultiElementAsyncLocalValueMap.MaxMultiElements); - int index = 0; - foreach (KeyValuePair<IAsyncLocal, object> pair in this) - { - if (!ReferenceEquals(key, pair.Key)) - { - multi.UnsafeStore(index++, pair.Key, pair.Value); - } - } - Debug.Assert(index == MultiElementAsyncLocalValueMap.MaxMultiElements); - return multi; - } - else - { - var map = new ManyElementAsyncLocalValueMap(count - 1); - foreach (KeyValuePair<IAsyncLocal, object> pair in this) - { - if (!ReferenceEquals(key, pair.Key)) - { - map[pair.Key] = pair.Value; - } - } - Debug.Assert(map.Count == count - 1); - return map; - } - } - - // We were storing null, but the key wasn't in the map, so there's nothing to change. - // Just return this instance. - return this; - } - } - } -} diff --git a/src/mscorlib/src/System/Threading/AutoResetEvent.cs b/src/mscorlib/src/System/Threading/AutoResetEvent.cs deleted file mode 100644 index fc6b2301ca..0000000000 --- a/src/mscorlib/src/System/Threading/AutoResetEvent.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: An example of a WaitHandle class -** -** -=============================================================================*/ -namespace System.Threading { - - using System; - using System.Runtime.InteropServices; - - public sealed class AutoResetEvent : EventWaitHandle - { - public AutoResetEvent(bool initialState) : base(initialState,EventResetMode.AutoReset){ } - } -} - diff --git a/src/mscorlib/src/System/Threading/CancellationToken.cs b/src/mscorlib/src/System/Threading/CancellationToken.cs index b68ba4c046..8bddfc90dd 100644 --- a/src/mscorlib/src/System/Threading/CancellationToken.cs +++ b/src/mscorlib/src/System/Threading/CancellationToken.cs @@ -78,14 +78,14 @@ namespace System.Threading /// particularly in situations where related objects are being canceled concurrently. /// </para> /// </remarks> - public bool IsCancellationRequested + public bool IsCancellationRequested { get { return m_source != null && m_source.IsCancellationRequested; } } - + /// <summary> /// Gets whether this token is capable of being in the canceled state. /// </summary> @@ -153,12 +153,12 @@ namespace System.Threading public CancellationToken(bool canceled) : this() { - if(canceled) + if (canceled) m_source = CancellationTokenSource.InternalGetStaticSource(canceled); } /* Methods */ - + private readonly static Action<Object> s_ActionToActionObjShunt = new Action<Object>(ActionToActionObjShunt); private static void ActionToActionObjShunt(object obj) @@ -190,7 +190,7 @@ namespace System.Threading { if (callback == null) throw new ArgumentNullException(nameof(callback)); - + return Register( s_ActionToActionObjShunt, callback, @@ -225,7 +225,7 @@ namespace System.Threading { if (callback == null) throw new ArgumentNullException(nameof(callback)); - + return Register( s_ActionToActionObjShunt, callback, @@ -301,7 +301,7 @@ namespace System.Threading true // useExecutionContext=true ); } - + // helper for internal registration needs that don't require an EC capture (e.g. creating linked token sources, or registering unstarted TPL tasks) // has a handy signature, and skips capturing execution context. internal CancellationTokenRegistration InternalRegisterWithoutEC(Action<object> callback, Object state) @@ -366,14 +366,14 @@ namespace System.Threading { return other.m_source == CancellationTokenSource.InternalGetStaticSource(false); } - + if (other.m_source == null) { return m_source == CancellationTokenSource.InternalGetStaticSource(false); } // general case, we check if the sources are identical - + return m_source == other.m_source; } @@ -392,7 +392,7 @@ namespace System.Threading { if (other is CancellationToken) { - return Equals((CancellationToken) other); + return Equals((CancellationToken)other); } return false; @@ -410,9 +410,9 @@ namespace System.Threading return CancellationTokenSource.InternalGetStaticSource(false).GetHashCode(); } - return m_source.GetHashCode(); + return m_source.GetHashCode(); } - + /// <summary> /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal. /// </summary> @@ -455,7 +455,7 @@ namespace System.Threading /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception> public void ThrowIfCancellationRequested() { - if (IsCancellationRequested) + if (IsCancellationRequested) ThrowOperationCanceledException(); } @@ -469,17 +469,17 @@ namespace System.Threading // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested private void ThrowOperationCanceledException() { - throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this); + throw new OperationCanceledException(SR.OperationCanceled, this); } private static void ThrowObjectDisposedException() { - throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationToken_SourceDisposed")); + throw new ObjectDisposedException(null, SR.CancellationToken_SourceDisposed); } // ----------------------------------- // Private helpers - + private void InitializeDefaultSource() { // Lazy is slower, and although multiple threads may try and set m_source repeatedly, the race condition is benign. diff --git a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs index 89e98fa3c8..be760fab3f 100644 --- a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs +++ b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs @@ -62,7 +62,7 @@ namespace System.Threading // Remove the entry from the array. // This call includes a full memory fence which prevents potential reorderings of the reads below bool deregisterOccurred = TryDeregister(); - + // We guarantee that we will not return if the callback is being executed (assuming we are not currently called by the callback itself) // We achieve this by the following rules: // 1. if we are called in the context of an executing callback, no need to wait (determined by tracking callback-executor threadID) @@ -124,7 +124,7 @@ namespace System.Threading /// </returns> public override bool Equals(object obj) { - return ((obj is CancellationTokenRegistration) && Equals((CancellationTokenRegistration) obj)); + return ((obj is CancellationTokenRegistration) && Equals((CancellationTokenRegistration)obj)); } /// <summary> @@ -152,7 +152,7 @@ namespace System.Threading { if (m_registrationInfo.Source != null) return m_registrationInfo.Source.GetHashCode() ^ m_registrationInfo.Index.GetHashCode(); - + return m_registrationInfo.Index.GetHashCode(); } } diff --git a/src/mscorlib/src/System/Threading/CancellationTokenSource.cs b/src/mscorlib/src/System/Threading/CancellationTokenSource.cs index 1e70d6f30f..2707292ed2 100644 --- a/src/mscorlib/src/System/Threading/CancellationTokenSource.cs +++ b/src/mscorlib/src/System/Threading/CancellationTokenSource.cs @@ -45,18 +45,18 @@ namespace System.Threading // the actual callback lists are only created on demand. // Storing a registered callback costs around >60bytes, hence some overhead for the lists array is OK // At most 24 lists seems reasonable, and caps the cost of the listsArray to 96bytes(32-bit,24-way) or 192bytes(64-bit,24-way). - private static readonly int s_nLists = (PlatformHelper.ProcessorCount > 24) ? 24 : PlatformHelper.ProcessorCount; + private static readonly int s_nLists = (PlatformHelper.ProcessorCount > 24) ? 24 : PlatformHelper.ProcessorCount; private volatile ManualResetEvent m_kernelEvent; //lazily initialized if required. private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists; - + // legal values for m_state private const int CANNOT_BE_CANCELED = 0; private const int NOT_CANCELED = 1; private const int NOTIFYING = 2; private const int NOTIFYINGCOMPLETE = 3; - + //m_state uses the pattern "volatile int32 reads, with cmpxch writes" which is safe for updates and cannot suffer torn reads. private volatile int m_state; @@ -68,13 +68,13 @@ namespace System.Threading private volatile int m_threadIDExecutingCallbacks = -1; private bool m_disposed; - + // we track the running callback to assist ctr.Dispose() to wait for the target callback to complete. private volatile CancellationCallbackInfo m_executingCallback; // provided for CancelAfter and timer-related constructors private volatile Timer m_timer; - + // ---------------------- // ** public properties @@ -168,11 +168,11 @@ namespace System.Threading // fast path if already allocated. if (m_kernelEvent != null) return m_kernelEvent; - + // lazy-init the mre. ManualResetEvent mre = new ManualResetEvent(false); if (Interlocked.CompareExchange(ref m_kernelEvent, mre, null) != null) - { + { ((IDisposable)mre).Dispose(); } @@ -211,9 +211,9 @@ namespace System.Threading return 0; int count = 0; - foreach(SparselyPopulatedArray<CancellationCallbackInfo> sparseArray in callbackLists) + foreach (SparselyPopulatedArray<CancellationCallbackInfo> sparseArray in callbackLists) { - if(sparseArray != null) + if (sparseArray != null) { SparselyPopulatedArrayFragment<CancellationCallbackInfo> currCallbacks = sparseArray.Head; while (currCallbacks != null) @@ -379,7 +379,7 @@ namespace System.Threading public void Cancel(bool throwOnFirstException) { ThrowIfDisposed(); - NotifyCancellation(throwOnFirstException); + NotifyCancellation(throwOnFirstException); } /// <summary> @@ -476,7 +476,7 @@ namespace System.Threading } } - + // It is possible that m_timer has already been disposed, so we must do // the following in a try/catch block. try @@ -490,7 +490,6 @@ namespace System.Threading // would not be a good way to deal with the observe/dispose // race condition. } - } private static readonly TimerCallback s_timerCallback = new TimerCallback(TimerCallbackLogic); @@ -601,7 +600,7 @@ namespace System.Threading // separation enables inlining of ThrowIfDisposed private static void ThrowObjectDisposedException() { - throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationTokenSource_Disposed")); + throw new ObjectDisposedException(null, SR.CancellationTokenSource_Disposed); } /// <summary> @@ -716,7 +715,7 @@ namespace System.Threading // Record the threadID being used for running the callbacks. ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId; - + // Set the event if it's been lazily initialized and hasn't yet been disposed of. Dispose may // be running concurrently, in which case either it'll have set m_kernelEvent back to null and // we won't see it here, or it'll see that we've transitioned to NOTIFYING and will skip disposing it, @@ -755,7 +754,7 @@ namespace System.Threading Interlocked.Exchange(ref m_state, NOTIFYINGCOMPLETE); return; } - + try { for (int index = 0; index < callbackLists.Length; index++) @@ -799,13 +798,13 @@ namespace System.Threading CancellationCallbackCoreWork(args); } } - catch(Exception ex) + catch (Exception ex) { if (throwOnFirstException) throw; - + // Otherwise, log it and proceed. - if(exceptionList == null) + if (exceptionList == null) exceptionList = new List<Exception>(); exceptionList.Add(ex); } @@ -821,7 +820,7 @@ namespace System.Threading { m_state = NOTIFYINGCOMPLETE; m_executingCallback = null; - Thread.MemoryBarrier(); // for safety, prevent reorderings crossing this point and seeing inconsistent state. + Interlocked.MemoryBarrier(); // for safety, prevent reorderings crossing this point and seeing inconsistent state. } if (exceptionList != null) @@ -892,7 +891,7 @@ namespace System.Threading switch (tokens.Length) { case 0: - throw new ArgumentException(Environment.GetResourceString("CancellationToken_CreateLinkedToken_TokensIsEmpty")); + throw new ArgumentException(SR.CancellationToken_CreateLinkedToken_TokensIsEmpty); case 1: return CreateLinkedTokenSource(tokens[0]); case 2: @@ -1004,7 +1003,7 @@ namespace System.Threading { internal SparselyPopulatedArrayFragment<CancellationCallbackInfo> m_currArrayFragment; internal int m_currArrayIndex; - + public CancellationCallbackCoreWorkArguments(SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment, int currArrayIndex) { m_currArrayFragment = currArrayFragment; @@ -1040,7 +1039,6 @@ namespace System.Threading { TargetSyncContext = targetSyncContext; } - } internal CancellationCallbackInfo( @@ -1066,7 +1064,7 @@ namespace System.Threading // Lazily initialize the callback delegate; benign race condition var callback = s_executionContextCallback; if (callback == null) s_executionContextCallback = callback = new ContextCallback(ExecutionContextCallback); - + ExecutionContext.Run( TargetExecutionContext, callback, @@ -1176,7 +1174,7 @@ namespace System.Threading // If the slot is null, try to CAS our element into it. int tryIndex = (start + i) % c; Debug.Assert(tryIndex >= 0 && tryIndex < curr.m_elements.Length, "tryIndex is outside of bounds"); - + if (curr.m_elements[tryIndex] == null && Interlocked.CompareExchange(ref curr.m_elements[tryIndex], element, null) == null) { // We adjust the free count by --. Note: if this drops to 0, we will skip @@ -1282,7 +1280,7 @@ namespace System.Threading internal T SafeAtomicRemove(int index, T expectedElement) { T prevailingValue = Interlocked.CompareExchange(ref m_elements[index], null, expectedElement); - if (prevailingValue != null) + if (prevailingValue != null) ++m_freeCount; return prevailingValue; } diff --git a/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs new file mode 100644 index 0000000000..d0cc5afbae --- /dev/null +++ b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs @@ -0,0 +1,319 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + // + // Implementation of ThreadPoolBoundHandle that sits on top of the CLR's ThreadPool and Overlapped infrastructure + // + + /// <summary> + /// Represents an I/O handle that is bound to the system thread pool and enables low-level + /// components to receive notifications for asynchronous I/O operations. + /// </summary> + public sealed partial class ThreadPoolBoundHandle : IDisposable + { + private readonly SafeHandle _handle; + private bool _isDisposed; + + private ThreadPoolBoundHandle(SafeHandle handle) + { + _handle = handle; + } + + /// <summary> + /// Gets the bound operating system handle. + /// </summary> + /// <value> + /// A <see cref="SafeHandle"/> object that holds the bound operating system handle. + /// </value> + public SafeHandle Handle + { + get { return _handle; } + } + + /// <summary> + /// Returns a <see cref="ThreadPoolBoundHandle"/> for the specific handle, + /// which is bound to the system thread pool. + /// </summary> + /// <param name="handle"> + /// A <see cref="SafeHandle"/> object that holds the operating system handle. The + /// handle must have been opened for overlapped I/O on the unmanaged side. + /// </param> + /// <returns> + /// <see cref="ThreadPoolBoundHandle"/> for <paramref name="handle"/>, which + /// is bound to the system thread pool. + /// </returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="handle"/> is <see langword="null"/>. + /// </exception> + /// <exception cref="ArgumentException"> + /// <paramref name="handle"/> has been disposed. + /// <para> + /// -or- + /// </para> + /// <paramref name="handle"/> does not refer to a valid I/O handle. + /// <para> + /// -or- + /// </para> + /// <paramref name="handle"/> refers to a handle that has not been opened + /// for overlapped I/O. + /// <para> + /// -or- + /// </para> + /// <paramref name="handle"/> refers to a handle that has already been bound. + /// </exception> + /// <remarks> + /// This method should be called once per handle. + /// <para> + /// -or- + /// </para> + /// <see cref="ThreadPoolBoundHandle"/> does not take ownership of <paramref name="handle"/>, + /// it remains the responsibility of the caller to call <see cref="SafeHandle.Dispose"/>. + /// </remarks> + public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) + { + if (handle == null) + throw new ArgumentNullException(nameof(handle)); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + try + { + // ThreadPool.BindHandle will always return true, otherwise, it throws. See the underlying FCall + // implementation in ThreadPoolNative::CorBindIoCompletionCallback to see the implementation. + bool succeeded = ThreadPool.BindHandle(handle); + Debug.Assert(succeeded); + } + catch (Exception ex) + { // BindHandle throws ApplicationException on full CLR and Exception on CoreCLR. + // We do not let either of these leak and convert them to ArgumentException to + // indicate that the specified handles are invalid. + + if (ex.HResult == System.HResults.E_HANDLE) // Bad handle + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + if (ex.HResult == System.HResults.E_INVALIDARG) // Handle already bound or sync handle + throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); + + throw; + } + + return new ThreadPoolBoundHandle(handle); + } + + /// <summary> + /// Returns an unmanaged pointer to a <see cref="NativeOverlapped"/> structure, specifying + /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// </summary> + /// <param name="callback"> + /// An <see cref="IOCompletionCallback"/> delegate that represents the callback method + /// invoked when the asynchronous I/O operation completes. + /// </param> + /// <param name="state"> + /// A user-provided object that distinguishes this <see cref="NativeOverlapped"/> from other + /// <see cref="NativeOverlapped"/> instances. Can be <see langword="null"/>. + /// </param> + /// <param name="pinData"> + /// An object or array of objects representing the input or output buffer for the operation. Each + /// object represents a buffer, for example an array of bytes. Can be <see langword="null"/>. + /// </param> + /// <returns> + /// An unmanaged pointer to a <see cref="NativeOverlapped"/> structure. + /// </returns> + /// <remarks> + /// <para> + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The <see cref="NativeOverlapped"/> structure is fixed in + /// physical memory until <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> is called. + /// </para> + /// <para> + /// The buffer or buffers specified in <paramref name="pinData"/> must be the same as those passed + /// to the unmanaged operating system function that performs the asynchronous I/O. + /// </para> + /// <note> + /// The buffers specified in <paramref name="pinData"/> are pinned for the duration of + /// the I/O operation. + /// </note> + /// </remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="callback"/> is <see langword="null"/>. + /// </exception> + /// <exception cref="ObjectDisposedException"> + /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed. + /// </exception> + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData) + { + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + EnsureNotDisposed(); + + ThreadPoolBoundHandleOverlapped overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, preAllocated: null); + overlapped._boundHandle = this; + return overlapped._nativeOverlapped; + } + + /// <summary> + /// Returns an unmanaged pointer to a <see cref="NativeOverlapped"/> structure, using the callback, + /// state, and buffers associated with the specified <see cref="PreAllocatedOverlapped"/> object. + /// </summary> + /// <param name="preAllocated"> + /// A <see cref="PreAllocatedOverlapped"/> object from which to create the NativeOverlapped pointer. + /// </param> + /// <returns> + /// An unmanaged pointer to a <see cref="NativeOverlapped"/> structure. + /// </returns> + /// <remarks> + /// <para> + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The <see cref="NativeOverlapped"/> structure is fixed in + /// physical memory until <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> is called. + /// </para> + /// </remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="preAllocated"/> is <see langword="null"/>. + /// </exception> + /// <exception cref="ArgumentException"> + /// <paramref name="preAllocated"/> is currently in use for another I/O operation. + /// </exception> + /// <exception cref="ObjectDisposedException"> + /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed, or + /// this method was called after <paramref name="preAllocated"/> was disposed. + /// </exception> + /// <seealso cref="PreAllocatedOverlapped"/> + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) + { + if (preAllocated == null) + throw new ArgumentNullException(nameof(preAllocated)); + + EnsureNotDisposed(); + + preAllocated.AddRef(); + try + { + ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped; + + if (overlapped._boundHandle != null) + throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); + + overlapped._boundHandle = this; + + return overlapped._nativeOverlapped; + } + catch + { + preAllocated.Release(); + throw; + } + } + + /// <summary> + /// Frees the unmanaged memory associated with a <see cref="NativeOverlapped"/> structure + /// allocated by the <see cref="AllocateNativeOverlapped"/> method. + /// </summary> + /// <param name="overlapped"> + /// An unmanaged pointer to the <see cref="NativeOverlapped"/> structure to be freed. + /// </param> + /// <remarks> + /// <note type="caution"> + /// You must call the <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> method exactly once + /// on every <see cref="NativeOverlapped"/> unmanaged pointer allocated using the + /// <see cref="AllocateNativeOverlapped"/> method. + /// If you do not call the <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> method, you will + /// leak memory. If you call the <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> method more + /// than once on the same <see cref="NativeOverlapped"/> unmanaged pointer, memory will be corrupted. + /// </note> + /// </remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="overlapped"/> is <see langword="null"/>. + /// </exception> + /// <exception cref="ObjectDisposedException"> + /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed. + /// </exception> + [CLSCompliant(false)] + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) + { + if (overlapped == null) + throw new ArgumentNullException(nameof(overlapped)); + + // Note: we explicitly allow FreeNativeOverlapped calls after the ThreadPoolBoundHandle has been Disposed. + + ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped, this); + + if (wrapper._boundHandle != this) + throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); + + if (wrapper._preAllocated != null) + wrapper._preAllocated.Release(); + else + Overlapped.Free(overlapped); + } + + /// <summary> + /// Returns the user-provided object specified when the <see cref="NativeOverlapped"/> instance was + /// allocated using the <see cref="AllocateNativeOverlapped(IOCompletionCallback, object, byte[])"/>. + /// </summary> + /// <param name="overlapped"> + /// An unmanaged pointer to the <see cref="NativeOverlapped"/> structure from which to return the + /// asscociated user-provided object. + /// </param> + /// <returns> + /// A user-provided object that distinguishes this <see cref="NativeOverlapped"/> + /// from other <see cref="NativeOverlapped"/> instances, otherwise, <see langword="null"/> if one was + /// not specified when the instance was allocated using <see cref="AllocateNativeOverlapped"/>. + /// </returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="overlapped"/> is <see langword="null"/>. + /// </exception> + [CLSCompliant(false)] + public unsafe static object GetNativeOverlappedState(NativeOverlapped* overlapped) + { + if (overlapped == null) + throw new ArgumentNullException(nameof(overlapped)); + + ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped, null); + Debug.Assert(wrapper._boundHandle != null); + return wrapper._userState; + } + + private static unsafe ThreadPoolBoundHandleOverlapped GetOverlappedWrapper(NativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle) + { + ThreadPoolBoundHandleOverlapped wrapper; + try + { + wrapper = (ThreadPoolBoundHandleOverlapped)Overlapped.Unpack(overlapped); + } + catch (NullReferenceException ex) + { + throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped), ex); + } + + return wrapper; + } + + public void Dispose() + { + // .NET Native's version of ThreadPoolBoundHandle that wraps the Win32 ThreadPool holds onto + // native resources so it needs to be disposable. To match the contract, we are also disposable. + // We also implement a disposable state to mimic behavior between this implementation and + // .NET Native's version (code written against us, will also work against .NET Native's version). + _isDisposed = true; + } + + + private void EnsureNotDisposed() + { + if (_isDisposed) + throw new ObjectDisposedException(GetType().ToString()); + } + } +} diff --git a/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs new file mode 100644 index 0000000000..1aea2a294b --- /dev/null +++ b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Threading +{ + /// <summary> + /// Overlapped subclass adding data needed by ThreadPoolBoundHandle. + /// </summary> + internal sealed class ThreadPoolBoundHandleOverlapped : Overlapped + { + private static readonly unsafe IOCompletionCallback s_completionCallback = CompletionCallback; + + private readonly IOCompletionCallback _userCallback; + internal readonly object _userState; + internal PreAllocatedOverlapped _preAllocated; + internal unsafe NativeOverlapped* _nativeOverlapped; + internal ThreadPoolBoundHandle _boundHandle; + internal bool _completed; + + public unsafe ThreadPoolBoundHandleOverlapped(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated) + { + _userCallback = callback; + _userState = state; + _preAllocated = preAllocated; + + _nativeOverlapped = Pack(s_completionCallback, pinData); + _nativeOverlapped->OffsetLow = 0; // CLR reuses NativeOverlapped instances and does not reset these + _nativeOverlapped->OffsetHigh = 0; + } + + private unsafe static void CompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) + { + ThreadPoolBoundHandleOverlapped overlapped = (ThreadPoolBoundHandleOverlapped)Overlapped.Unpack(nativeOverlapped); + + // + // The Win32 thread pool implementation of ThreadPoolBoundHandle does not permit reuse of NativeOverlapped + // pointers without freeing them and allocating new a new one. We need to ensure that code using the CLR + // ThreadPool implementation follows those rules. + // + if (overlapped._completed) + throw new InvalidOperationException(SR.InvalidOperation_NativeOverlappedReused); + + overlapped._completed = true; + + if (overlapped._boundHandle == null) + throw new InvalidOperationException(SR.Argument_NativeOverlappedAlreadyFree); + + overlapped._userCallback(errorCode, numBytes, nativeOverlapped); + } + } +} diff --git a/src/mscorlib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs b/src/mscorlib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs new file mode 100644 index 0000000000..a42e0c7983 --- /dev/null +++ b/src/mscorlib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Threading +{ + /// <summary> + /// Represents pre-allocated state for native overlapped I/O operations. + /// </summary> + /// <seealso cref="ThreadPoolBoundHandle.AllocateNativeOverlapped(PreAllocatedOverlapped)"/> + public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + internal readonly ThreadPoolBoundHandleOverlapped _overlapped; + private DeferredDisposableLifetime<PreAllocatedOverlapped> _lifetime; + + /// <summary> + /// Initializes a new instance of the <see cref="PreAllocatedOverlapped"/> class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// </summary> + /// <param name="callback"> + /// An <see cref="IOCompletionCallback"/> delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// </param> + /// <param name="state"> + /// A user-provided object that distinguishes <see cref="NativeOverlapped"/> instance produced from this + /// object from other <see cref="NativeOverlapped"/> instances. Can be <see langword="null"/>. + /// </param> + /// <param name="pinData"> + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be <see langword="null"/>. + /// </param> + /// <remarks> + /// The new <see cref="PreAllocatedOverlapped"/> instance can be passed to + /// <see cref="ThreadPoolBoundHandle.AllocateNativeOverlapped(PreAllocatedOverlapped)"/>, to produce + /// a <see cref="NativeOverlapped"/> instance that can be passed to the operating system in overlapped + /// I/O operations. A single <see cref="PreAllocatedOverlapped"/> instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the <see cref="PreAllocatedOverlapped"/> + /// instance can be reused for subsequent native operations. + /// <note> + /// The buffers specified in <paramref name="pinData"/> are pinned until <see cref="Dispose"/> is called. + /// </note> + /// </remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="callback"/> is <see langword="null"/>. + /// </exception> + /// <exception cref="ObjectDisposedException"> + /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed. + /// </exception> + [CLSCompliant(false)] + public unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object state, object pinData) + { + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + _overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this); + } + + internal bool AddRef() + { + return _lifetime.AddRef(this); + } + + internal void Release() + { + _lifetime.Release(this); + } + + /// <summary> + /// Frees the resources associated with this <see cref="PreAllocatedOverlapped"/> instance. + /// </summary> + public unsafe void Dispose() + { + _lifetime.Dispose(this); + GC.SuppressFinalize(this); + } + + ~PreAllocatedOverlapped() + { + // + // During shutdown, don't automatically clean up, because this instance may still be + // reachable/usable by other code. + // + if (!Environment.HasShutdownStarted) + Dispose(); + } + + unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + { + if (_overlapped != null) + { + if (disposed) + { + Overlapped.Free(_overlapped._nativeOverlapped); + } + else + { + _overlapped._boundHandle = null; + _overlapped._completed = false; + *_overlapped._nativeOverlapped = default(NativeOverlapped); + } + } + } + } +} diff --git a/src/mscorlib/src/System/Threading/CountdownEvent.cs b/src/mscorlib/src/System/Threading/CountdownEvent.cs deleted file mode 100644 index af055e347e..0000000000 --- a/src/mscorlib/src/System/Threading/CountdownEvent.cs +++ /dev/null @@ -1,589 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// A simple coordination data structure that we use for fork/join style parallelism. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Runtime.InteropServices; -using System.Threading; -using System.Diagnostics; -using System.Diagnostics.Contracts; - -namespace System.Threading -{ - - /// <summary> - /// Represents a synchronization primitive that is signaled when its count reaches zero. - /// </summary> - /// <remarks> - /// All public and protected members of <see cref="CountdownEvent"/> are thread-safe and may be used - /// concurrently from multiple threads, with the exception of Dispose, which - /// must only be used when all other operations on the <see cref="CountdownEvent"/> have - /// completed, and Reset, which should only be used when no other threads are - /// accessing the event. - /// </remarks> - [DebuggerDisplay("Initial Count={InitialCount}, Current Count={CurrentCount}")] - public class CountdownEvent : IDisposable - { - // CountdownEvent is a simple synchronization primitive used for fork/join parallelism. We create a - // latch with a count of N; threads then signal the latch, which decrements N by 1; other threads can - // wait on the latch at any point; when the latch count reaches 0, all threads are woken and - // subsequent waiters return without waiting. The implementation internally lazily creates a true - // Win32 event as needed. We also use some amount of spinning on MP machines before falling back to a - // wait. - - private int m_initialCount; // The original # of signals the latch was instantiated with. - private volatile int m_currentCount; // The # of outstanding signals before the latch transitions to a signaled state. - private ManualResetEventSlim m_event; // An event used to manage blocking and signaling. - private volatile bool m_disposed; // Whether the latch has been disposed. - - /// <summary> - /// Initializes a new instance of <see cref="T:System.Threading.CountdownEvent"/> class with the - /// specified count. - /// </summary> - /// <param name="initialCount">The number of signals required to set the <see - /// cref="T:System.Threading.CountdownEvent"/>.</param> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="initialCount"/> is less - /// than 0.</exception> - public CountdownEvent(int initialCount) - { - if (initialCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(initialCount)); - } - - m_initialCount = initialCount; - m_currentCount = initialCount; - - // Allocate a thin event, which internally defers creation of an actual Win32 event. - m_event = new ManualResetEventSlim(); - - // If the latch was created with a count of 0, then it's already in the signaled state. - if (initialCount == 0) - { - m_event.Set(); - } - } - - /// <summary> - /// Gets the number of remaining signals required to set the event. - /// </summary> - /// <value> - /// The number of remaining signals required to set the event. - /// </value> - public int CurrentCount - { - get - { - int observedCount = m_currentCount; - return observedCount < 0 ? 0 : observedCount; - } - } - - /// <summary> - /// Gets the numbers of signals initially required to set the event. - /// </summary> - /// <value> - /// The number of signals initially required to set the event. - /// </value> - public int InitialCount - { - get { return m_initialCount; } - } - - /// <summary> - /// Determines whether the event is set. - /// </summary> - /// <value>true if the event is set; otherwise, false.</value> - public bool IsSet - { - get - { - // The latch is "completed" if its current count has reached 0. Note that this is NOT - // the same thing is checking the event's IsCompleted property. There is a tiny window - // of time, after the final decrement of the current count to 0 and before setting the - // event, where the two values are out of sync. - return (m_currentCount <= 0); - } - } - - /// <summary> - /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for the event to be set. - /// </summary> - /// <value>A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for the event to be set.</value> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception> - /// <remarks> - /// <see cref="WaitHandle"/> should only be used if it's needed for integration with code bases - /// that rely on having a WaitHandle. If all that's needed is to wait for the <see cref="CountdownEvent"/> - /// to be set, the <see cref="Wait()"/> method should be preferred. - /// </remarks> - public WaitHandle WaitHandle - { - get - { - ThrowIfDisposed(); - return m_event.WaitHandle; - } - } - - /// <summary> - /// Releases all resources used by the current instance of <see cref="T:System.Threading.CountdownEvent"/>. - /// </summary> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, <see cref="Dispose()"/> is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - public void Dispose() - { - // Gets rid of this latch's associated resources. This can consist of a Win32 event - // which is (lazily) allocated by the underlying thin event. This method is not safe to - // call concurrently -- i.e. a caller must coordinate to ensure only one thread is using - // the latch at the time of the call to Dispose. - - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// When overridden in a derived class, releases the unmanaged resources used by the - /// <see cref="T:System.Threading.CountdownEvent"/>, and optionally releases the managed resources. - /// </summary> - /// <param name="disposing">true to release both managed and unmanaged resources; false to release - /// only unmanaged resources.</param> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, <see cref="Dispose()"/> is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - m_event.Dispose(); - m_disposed = true; - } - } - - /// <summary> - /// Registers a signal with the <see cref="T:System.Threading.CountdownEvent"/>, decrementing its - /// count. - /// </summary> - /// <returns>true if the signal caused the count to reach zero and the event was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.InvalidOperationException">The current instance is already set. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Signal() - { - ThrowIfDisposed(); - Debug.Assert(m_event != null); - - if (m_currentCount <= 0) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); - } -#pragma warning disable 0420 - int newCount = Interlocked.Decrement(ref m_currentCount); -#pragma warning restore 0420 - if (newCount == 0) - { - m_event.Set(); - return true; - } - else if (newCount < 0) - { - //if the count is decremented below zero, then throw, it's OK to keep the count negative, and we shouldn't set the event here - //because there was a thread already which decremented it to zero and set the event - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); - } - - return false; - } - - /// <summary> - /// Registers multiple signals with the <see cref="T:System.Threading.CountdownEvent"/>, - /// decrementing its count by the specified amount. - /// </summary> - /// <param name="signalCount">The number of signals to register.</param> - /// <returns>true if the signals caused the count to reach zero and the event was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.InvalidOperationException"> - /// The current instance is already set. -or- Or <paramref name="signalCount"/> is greater than <see - /// cref="CurrentCount"/>. - /// </exception> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less - /// than 1.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Signal(int signalCount) - { - if (signalCount <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalCount)); - } - - ThrowIfDisposed(); - Debug.Assert(m_event != null); - - int observedCount; - SpinWait spin = new SpinWait(); - while (true) - { - observedCount = m_currentCount; - - // If the latch is already signaled, we will fail. - if (observedCount < signalCount) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); - } - - // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning - // for this statement. This warning is clearly senseless for Interlocked operations. -#pragma warning disable 0420 - if (Interlocked.CompareExchange(ref m_currentCount, observedCount - signalCount, observedCount) == observedCount) -#pragma warning restore 0420 - { - break; - } - - // The CAS failed. Spin briefly and try again. - spin.SpinOnce(); - } - - // If we were the last to signal, set the event. - if (observedCount == signalCount) - { - m_event.Set(); - return true; - } - - Debug.Assert(m_currentCount >= 0, "latch was decremented below zero"); - return false; - } - - /// <summary> - /// Increments the <see cref="T:System.Threading.CountdownEvent"/>'s current count by one. - /// </summary> - /// <exception cref="T:System.InvalidOperationException">The current instance is already - /// set.</exception> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException"> - /// The current instance has already been disposed. - /// </exception> - public void AddCount() - { - AddCount(1); - } - - /// <summary> - /// Attempts to increment the <see cref="T:System.Threading.CountdownEvent"/>'s current count by one. - /// </summary> - /// <returns>true if the increment succeeded; otherwise, false. If <see cref="CurrentCount"/> is - /// already at zero. this will return false.</returns> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool TryAddCount() - { - return TryAddCount(1); - } - - /// <summary> - /// Increments the <see cref="T:System.Threading.CountdownEvent"/>'s current count by a specified - /// value. - /// </summary> - /// <param name="signalCount">The value by which to increase <see cref="CurrentCount"/>.</param> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less than - /// 0.</exception> - /// <exception cref="T:System.InvalidOperationException">The current instance is already - /// set.</exception> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void AddCount(int signalCount) - { - if (!TryAddCount(signalCount)) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyZero")); - } - } - - /// <summary> - /// Attempts to increment the <see cref="T:System.Threading.CountdownEvent"/>'s current count by a - /// specified value. - /// </summary> - /// <param name="signalCount">The value by which to increase <see cref="CurrentCount"/>.</param> - /// <returns>true if the increment succeeded; otherwise, false. If <see cref="CurrentCount"/> is - /// already at zero this will return false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less - /// than 0.</exception> - /// <exception cref="T:System.InvalidOperationException">The current instance is already - /// set.</exception> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool TryAddCount(int signalCount) - { - if (signalCount <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalCount)); - } - - ThrowIfDisposed(); - - // Loop around until we successfully increment the count. - int observedCount; - SpinWait spin = new SpinWait(); - while (true) - { - observedCount = m_currentCount; - - if (observedCount <= 0) - { - return false; - } - else if (observedCount > (Int32.MaxValue - signalCount)) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyMax")); - } - - // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning - // for this statement. This warning is clearly senseless for Interlocked operations. -#pragma warning disable 0420 - if (Interlocked.CompareExchange(ref m_currentCount, observedCount + signalCount, observedCount) == observedCount) -#pragma warning restore 0420 - { - break; - } - - // The CAS failed. Spin briefly and try again. - spin.SpinOnce(); - } - - return true; - } - - /// <summary> - /// Resets the <see cref="CurrentCount"/> to the value of <see cref="InitialCount"/>. - /// </summary> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, Reset is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed..</exception> - public void Reset() - { - Reset(m_initialCount); - } - - /// <summary> - /// Resets the <see cref="CurrentCount"/> to a specified value. - /// </summary> - /// <param name="count">The number of signals required to set the <see - /// cref="T:System.Threading.CountdownEvent"/>.</param> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, Reset is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="count"/> is - /// less than 0.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has alread been disposed.</exception> - public void Reset(int count) - { - ThrowIfDisposed(); - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - m_currentCount = count; - m_initialCount = count; - - if (count == 0) - { - m_event.Set(); - } - else - { - m_event.Reset(); - } - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set. - /// </summary> - /// <remarks> - /// The caller of this method blocks indefinitely until the current instance is set. The caller will - /// return immediately if the event is currently in a set state. - /// </remarks> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void Wait() - { - Wait(Timeout.Infinite, new CancellationToken()); - } - - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, while - /// observing a <see cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <remarks> - /// The caller of this method blocks indefinitely until the current instance is set. The caller will - /// return immediately if the event is currently in a set state. If the - /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> being observed - /// is canceled during the wait operation, an <see cref="T:System.OperationCanceledException"/> - /// will be thrown. - /// </remarks> - /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has been - /// canceled.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void Wait(CancellationToken cancellationToken) - { - Wait(Timeout.Infinite, cancellationToken); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a - /// <see cref="T:System.TimeSpan"/> to measure the time interval. - /// </summary> - /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of - /// milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to - /// wait indefinitely.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative - /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than <see cref="System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Wait(TimeSpan timeout) - { - long totalMilliseconds = (long)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } - - return Wait((int)totalMilliseconds, new CancellationToken()); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using - /// a <see cref="T:System.TimeSpan"/> to measure the time interval, while observing a - /// <see cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of - /// milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to - /// wait indefinitely.</param> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative - /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than <see cref="System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has - /// been canceled.</exception> - public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) - { - long totalMilliseconds = (long)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } - - return Wait((int)totalMilliseconds, cancellationToken); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a - /// 32-bit signed integer to measure the time interval. - /// </summary> - /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see - /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a - /// negative number other than -1, which represents an infinite time-out.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Wait(int millisecondsTimeout) - { - return Wait(millisecondsTimeout, new CancellationToken()); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a - /// 32-bit signed integer to measure the time interval, while observing a - /// <see cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see - /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a - /// negative number other than -1, which represents an infinite time-out.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has - /// been canceled.</exception> - public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) - { - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); - } - - ThrowIfDisposed(); - cancellationToken.ThrowIfCancellationRequested(); - - bool returnValue = IsSet; - - // If not completed yet, wait on the event. - if (!returnValue) - { - // ** the actual wait - returnValue = m_event.Wait(millisecondsTimeout, cancellationToken); - //the Wait will throw OCE itself if the token is canceled. - } - - return returnValue; - } - - // -------------------------------------- - // Private methods - - - /// <summary> - /// Throws an exception if the latch has been disposed. - /// </summary> - private void ThrowIfDisposed() - { - if (m_disposed) - { - throw new ObjectDisposedException("CountdownEvent"); - } - } - } -} diff --git a/src/mscorlib/src/System/Threading/EventResetMode.cs b/src/mscorlib/src/System/Threading/EventResetMode.cs deleted file mode 100644 index edafab9bb5..0000000000 --- a/src/mscorlib/src/System/Threading/EventResetMode.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** Enum: EventResetMode -** -** -** Purpose: Enum to determine the Event type to create -** -** -=============================================================================*/ - - -namespace System.Threading -{ - using System.Runtime.InteropServices; - [ComVisibleAttribute(false)] - public enum EventResetMode - { - AutoReset = 0, - ManualReset = 1 - } -} diff --git a/src/mscorlib/src/System/Threading/EventWaitHandle.cs b/src/mscorlib/src/System/Threading/EventWaitHandle.cs index 0268948a5c..611d9de7e7 100644 --- a/src/mscorlib/src/System/Threading/EventWaitHandle.cs +++ b/src/mscorlib/src/System/Threading/EventWaitHandle.cs @@ -39,25 +39,25 @@ namespace System.Threading [ComVisibleAttribute(true)] public class EventWaitHandle : WaitHandle { - public EventWaitHandle(bool initialState, EventResetMode mode) : this(initialState,mode,null) { } + public EventWaitHandle(bool initialState, EventResetMode mode) : this(initialState, mode, null) { } public EventWaitHandle(bool initialState, EventResetMode mode, string name) { - if(name != null) + if (name != null) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives")); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); #else if (System.IO.Path.MaxPath < name.Length) { - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); } #endif } Contract.EndContractBlock(); - + SafeWaitHandle _handle = null; - switch(mode) + switch (mode) { case EventResetMode.ManualReset: _handle = Win32Native.CreateEvent(null, true, initialState, name); @@ -67,16 +67,16 @@ namespace System.Threading break; default: - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag",name)); + throw new ArgumentException(SR.Format(SR.Argument_InvalidFlag, name)); }; - + if (_handle.IsInvalid) { int errorCode = Marshal.GetLastWin32Error(); - + _handle.SetHandleAsInvalid(); - if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) - throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name)); + if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); __Error.WinIOError(errorCode, name); } @@ -90,14 +90,14 @@ namespace System.Threading internal unsafe EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity) { - if(name != null) + if (name != null) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives")); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); #else if (System.IO.Path.MaxPath < name.Length) { - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); } #endif } @@ -106,7 +106,7 @@ namespace System.Threading SafeWaitHandle _handle = null; Boolean isManualReset; - switch(mode) + switch (mode) { case EventResetMode.ManualReset: isManualReset = true; @@ -116,7 +116,7 @@ namespace System.Threading break; default: - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag",name)); + throw new ArgumentException(SR.Format(SR.Argument_InvalidFlag, name)); }; _handle = Win32Native.CreateEvent(secAttrs, isManualReset, initialState, name); @@ -124,10 +124,9 @@ namespace System.Threading if (_handle.IsInvalid) { - _handle.SetHandleAsInvalid(); - if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) - throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name)); + if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); __Error.WinIOError(errorCode, name); } @@ -154,7 +153,7 @@ namespace System.Threading throw new WaitHandleCannotBeOpenedException(); case OpenExistingResult.NameInvalid: - throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name)); + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); case OpenExistingResult.PathNotFound: __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, ""); @@ -173,23 +172,23 @@ namespace System.Threading private static OpenExistingResult OpenExistingWorker(string name, EventWaitHandleRights rights, out EventWaitHandle result) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives")); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); #else if (name == null) { - throw new ArgumentNullException(nameof(name), Environment.GetResourceString("ArgumentNull_WithParamName")); + throw new ArgumentNullException(nameof(name), SR.ArgumentNull_WithParamName); } - if(name.Length == 0) + if (name.Length == 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), nameof(name)); + throw new ArgumentException(SR.Argument_EmptyName, nameof(name)); } - if(null != name && System.IO.Path.MaxPath < name.Length) + if (null != name && System.IO.Path.MaxPath < name.Length) { - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); } - + Contract.EndContractBlock(); result = null; @@ -200,14 +199,14 @@ namespace System.Threading { int errorCode = Marshal.GetLastWin32Error(); - if(Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode) + if (Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode) return OpenExistingResult.NameNotFound; if (Win32Native.ERROR_PATH_NOT_FOUND == errorCode) return OpenExistingResult.PathNotFound; - if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) + if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) return OpenExistingResult.NameInvalid; //this is for passed through Win32Native Errors - __Error.WinIOError(errorCode,""); + __Error.WinIOError(errorCode, ""); } result = new EventWaitHandle(myHandle); return OpenExistingResult.Success; diff --git a/src/mscorlib/src/System/Threading/ExecutionContext.cs b/src/mscorlib/src/System/Threading/ExecutionContext.cs deleted file mode 100644 index 47a55a3bb9..0000000000 --- a/src/mscorlib/src/System/Threading/ExecutionContext.cs +++ /dev/null @@ -1,380 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** Purpose: Capture execution context for a thread -** -** -===========================================================*/ -namespace System.Threading -{ - using System; - using System.Security; - using System.Runtime.Remoting; - using System.Collections; - using System.Collections.Generic; - using System.Reflection; - using System.Runtime.ExceptionServices; - using System.Runtime.Serialization; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Runtime.ConstrainedExecution; - using System.Diagnostics; - using System.Diagnostics.Contracts; - using System.Diagnostics.CodeAnalysis; - - public delegate void ContextCallback(Object state); - - internal struct ExecutionContextSwitcher - { - internal ExecutionContext m_ec; - internal SynchronizationContext m_sc; - - internal void Undo(Thread currentThread) - { - Debug.Assert(currentThread == Thread.CurrentThread); - - // The common case is that these have not changed, so avoid the cost of a write if not needed. - if (currentThread.SynchronizationContext != m_sc) - { - currentThread.SynchronizationContext = m_sc; - } - - if (currentThread.ExecutionContext != m_ec) - { - ExecutionContext.Restore(currentThread, m_ec); - } - } - } - - [Serializable] - public sealed class ExecutionContext : IDisposable, ISerializable - { - internal static readonly ExecutionContext Default = new ExecutionContext(); - - private readonly IAsyncLocalValueMap m_localValues; - private readonly IAsyncLocal[] m_localChangeNotifications; - private readonly bool m_isFlowSuppressed; - - private ExecutionContext() - { - m_localValues = AsyncLocalValueMap.Empty; - m_localChangeNotifications = Array.Empty<IAsyncLocal>(); - } - - private ExecutionContext( - IAsyncLocalValueMap localValues, - IAsyncLocal[] localChangeNotifications, - bool isFlowSuppressed) - { - m_localValues = localValues; - m_localChangeNotifications = localChangeNotifications; - m_isFlowSuppressed = isFlowSuppressed; - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - Contract.EndContractBlock(); - } - - private ExecutionContext(SerializationInfo info, StreamingContext context) - { - } - - public static ExecutionContext Capture() - { - ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext; - return - executionContext == null ? Default : - executionContext.m_isFlowSuppressed ? null : - executionContext; - } - - private ExecutionContext ShallowClone(bool isFlowSuppressed) - { - Debug.Assert(isFlowSuppressed != m_isFlowSuppressed); - - if (!isFlowSuppressed && - m_localValues == Default.m_localValues && - m_localChangeNotifications == Default.m_localChangeNotifications) - { - return null; // implies the default context - } - return new ExecutionContext(m_localValues, m_localChangeNotifications, isFlowSuppressed); - } - - public static AsyncFlowControl SuppressFlow() - { - Thread currentThread = Thread.CurrentThread; - ExecutionContext executionContext = currentThread.ExecutionContext ?? Default; - if (executionContext.m_isFlowSuppressed) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes")); - } - Contract.EndContractBlock(); - - executionContext = executionContext.ShallowClone(isFlowSuppressed: true); - var asyncFlowControl = new AsyncFlowControl(); - currentThread.ExecutionContext = executionContext; - asyncFlowControl.Initialize(currentThread); - return asyncFlowControl; - } - - public static void RestoreFlow() - { - Thread currentThread = Thread.CurrentThread; - ExecutionContext executionContext = currentThread.ExecutionContext; - if (executionContext == null || !executionContext.m_isFlowSuppressed) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRestoreUnsupressedFlow")); - } - Contract.EndContractBlock(); - - currentThread.ExecutionContext = executionContext.ShallowClone(isFlowSuppressed: false); - } - - public static bool IsFlowSuppressed() - { - ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext; - return executionContext != null && executionContext.m_isFlowSuppressed; - } - - [HandleProcessCorruptedStateExceptions] - public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state) - { - if (executionContext == null) - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NullContext")); - - Thread currentThread = Thread.CurrentThread; - ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher); - try - { - EstablishCopyOnWriteScope(currentThread, ref ecsw); - ExecutionContext.Restore(currentThread, executionContext); - callback(state); - } - catch - { - // Note: we have a "catch" rather than a "finally" because we want - // to stop the first pass of EH here. That way we can restore the previous - // context before any of our callers' EH filters run. That means we need to - // end the scope separately in the non-exceptional case below. - ecsw.Undo(currentThread); - throw; - } - ecsw.Undo(currentThread); - } - - internal static void Restore(Thread currentThread, ExecutionContext executionContext) - { - Debug.Assert(currentThread == Thread.CurrentThread); - - ExecutionContext previous = currentThread.ExecutionContext ?? Default; - currentThread.ExecutionContext = executionContext; - - // New EC could be null if that's what ECS.Undo saved off. - // For the purposes of dealing with context change, treat this as the default EC - executionContext = executionContext ?? Default; - - if (previous != executionContext) - { - OnContextChanged(previous, executionContext); - } - } - - static internal void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw) - { - Debug.Assert(currentThread == Thread.CurrentThread); - - ecsw.m_ec = currentThread.ExecutionContext; - ecsw.m_sc = currentThread.SynchronizationContext; - } - - [HandleProcessCorruptedStateExceptions] - private static void OnContextChanged(ExecutionContext previous, ExecutionContext current) - { - Debug.Assert(previous != null); - Debug.Assert(current != null); - Debug.Assert(previous != current); - - foreach (IAsyncLocal local in previous.m_localChangeNotifications) - { - object previousValue; - object currentValue; - previous.m_localValues.TryGetValue(local, out previousValue); - current.m_localValues.TryGetValue(local, out currentValue); - - if (previousValue != currentValue) - local.OnValueChanged(previousValue, currentValue, true); - } - - if (current.m_localChangeNotifications != previous.m_localChangeNotifications) - { - try - { - foreach (IAsyncLocal local in current.m_localChangeNotifications) - { - // If the local has a value in the previous context, we already fired the event for that local - // in the code above. - object previousValue; - if (!previous.m_localValues.TryGetValue(local, out previousValue)) - { - object currentValue; - current.m_localValues.TryGetValue(local, out currentValue); - - if (previousValue != currentValue) - local.OnValueChanged(previousValue, currentValue, true); - } - } - } - catch (Exception ex) - { - Environment.FailFast( - Environment.GetResourceString("ExecutionContext_ExceptionInAsyncLocalNotification"), - ex); - } - } - } - - internal static object GetLocalValue(IAsyncLocal local) - { - ExecutionContext current = Thread.CurrentThread.ExecutionContext; - if (current == null) - return null; - - object value; - current.m_localValues.TryGetValue(local, out value); - return value; - } - - internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications) - { - ExecutionContext current = Thread.CurrentThread.ExecutionContext ?? ExecutionContext.Default; - - object previousValue; - bool hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue); - - if (previousValue == newValue) - return; - - IAsyncLocalValueMap newValues = current.m_localValues.Set(local, newValue); - - // - // Either copy the change notification array, or create a new one, depending on whether we need to add a new item. - // - IAsyncLocal[] newChangeNotifications = current.m_localChangeNotifications; - if (needChangeNotifications) - { - if (hadPreviousValue) - { - Debug.Assert(Array.IndexOf(newChangeNotifications, local) >= 0); - } - else - { - int newNotificationIndex = newChangeNotifications.Length; - Array.Resize(ref newChangeNotifications, newNotificationIndex + 1); - newChangeNotifications[newNotificationIndex] = local; - } - } - - Thread.CurrentThread.ExecutionContext = - new ExecutionContext(newValues, newChangeNotifications, current.m_isFlowSuppressed); - - if (needChangeNotifications) - { - local.OnValueChanged(previousValue, newValue, false); - } - } - - public ExecutionContext CreateCopy() - { - return this; // since CoreCLR's ExecutionContext is immutable, we don't need to create copies. - } - - public void Dispose() - { - // For CLR compat only - } - } - - public struct AsyncFlowControl : IDisposable - { - private Thread _thread; - - internal void Initialize(Thread currentThread) - { - Debug.Assert(currentThread == Thread.CurrentThread); - _thread = currentThread; - } - - public void Undo() - { - if (_thread == null) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCMultiple")); - } - if (Thread.CurrentThread != _thread) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCOtherThread")); - } - - // An async flow control cannot be undone when a different execution context is applied. The desktop framework - // mutates the execution context when its state changes, and only changes the instance when an execution context - // is applied (for instance, through ExecutionContext.Run). The framework prevents a suppressed-flow execution - // context from being applied by returning null from ExecutionContext.Capture, so the only type of execution - // context that can be applied is one whose flow is not suppressed. After suppressing flow and changing an async - // local's value, the desktop framework verifies that a different execution context has not been applied by - // checking the execution context instance against the one saved from when flow was suppressed. In .NET Core, - // since the execution context instance will change after changing the async local's value, it verifies that a - // different execution context has not been applied, by instead ensuring that the current execution context's - // flow is suppressed. - if (!ExecutionContext.IsFlowSuppressed()) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch")); - } - Contract.EndContractBlock(); - - _thread = null; - ExecutionContext.RestoreFlow(); - } - - public void Dispose() - { - Undo(); - } - - public override bool Equals(object obj) - { - return obj is AsyncFlowControl && Equals((AsyncFlowControl)obj); - } - - public bool Equals(AsyncFlowControl obj) - { - return _thread == obj._thread; - } - - public override int GetHashCode() - { - return _thread?.GetHashCode() ?? 0; - } - - public static bool operator ==(AsyncFlowControl a, AsyncFlowControl b) - { - return a.Equals(b); - } - - public static bool operator !=(AsyncFlowControl a, AsyncFlowControl b) - { - return !(a == b); - } - } -} - - diff --git a/src/mscorlib/src/System/Threading/Interlocked.cs b/src/mscorlib/src/System/Threading/Interlocked.cs index 131d51a65b..7e2c2aeeab 100644 --- a/src/mscorlib/src/System/Threading/Interlocked.cs +++ b/src/mscorlib/src/System/Threading/Interlocked.cs @@ -3,21 +3,24 @@ // See the LICENSE file in the project root for more information. // + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Security; + namespace System.Threading { - using System; - using System.Runtime.CompilerServices; - using System.Runtime.ConstrainedExecution; - using System.Runtime.Versioning; - using System.Runtime; - // After much discussion, we decided the Interlocked class doesn't need // any HPA's for synchronization or external threading. They hurt C#'s // codegen for the yield keyword, and arguably they didn't protect much. // Instead, they penalized people (and compilers) for writing threadsafe // code. public static class Interlocked - { + { /****************************** * Increment * Implemented: int @@ -102,16 +105,16 @@ namespace System.Threading *****************************/ [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern int CompareExchange(ref int location1, int value, int comparand); + public static extern int CompareExchange(ref int location1, int value, int comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern long CompareExchange(ref long location1, long value, long comparand); + public static extern long CompareExchange(ref long location1, long value, long comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float CompareExchange(ref float location1, float value, float comparand); + public static extern float CompareExchange(ref float location1, float value, float comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double CompareExchange(ref double location1, double value, double comparand); + public static extern double CompareExchange(ref double location1, double value, double comparand); [MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern Object CompareExchange(ref Object location1, Object value, Object comparand); @@ -142,7 +145,7 @@ namespace System.Threading * See getILIntrinsicImplementationForInterlocked() in VM\JitInterface.cpp * for details. *****************************************************************/ - + public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class { // _CompareExchange() passes back the value read from location1 via local named 'value' @@ -169,28 +172,34 @@ namespace System.Threading [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern long ExchangeAdd(ref long location1, long value); - public static int Add(ref int location1, int value) + public static int Add(ref int location1, int value) { return ExchangeAdd(ref location1, value) + value; } - public static long Add(ref long location1, long value) + public static long Add(ref long location1, long value) { return ExchangeAdd(ref location1, value) + value; } - + /****************************** * Read *****************************/ public static long Read(ref long location) { - return Interlocked.CompareExchange(ref location,0,0); + return Interlocked.CompareExchange(ref location, 0, 0); } + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void MemoryBarrier(); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern void _MemoryBarrierProcessWide(); - public static void MemoryBarrier() + public static void MemoryBarrierProcessWide() { - Thread.MemoryBarrier(); + _MemoryBarrierProcessWide(); } } } diff --git a/src/mscorlib/src/System/Threading/LazyInitializer.cs b/src/mscorlib/src/System/Threading/LazyInitializer.cs index af32673d03..d585ba6c35 100644 --- a/src/mscorlib/src/System/Threading/LazyInitializer.cs +++ b/src/mscorlib/src/System/Threading/LazyInitializer.cs @@ -13,40 +13,9 @@ using System.Diagnostics; using System.Diagnostics.Contracts; + namespace System.Threading { - - /// <summary> - /// Specifies how a <see cref="T:System.Threading.Lazy{T}"/> instance should synchronize access among multiple threads. - /// </summary> - public enum LazyThreadSafetyMode - { - /// <summary> - /// This mode makes no guarantees around the thread-safety of the <see cref="T:System.Threading.Lazy{T}"/> instance. If used from multiple threads, the behavior of the <see cref="T:System.Threading.Lazy{T}"/> is undefined. - /// This mode should be used when a <see cref="T:System.Threading.Lazy{T}"/> is guaranteed to never be initialized from more than one thread simultaneously and high performance is crucial. - /// If valueFactory throws an exception when the <see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on subsequent accesses to Value. Also, if valueFactory recursively - /// accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown. - /// </summary> - None, - - /// <summary> - /// When multiple threads attempt to simultaneously initialize a <see cref="T:System.Threading.Lazy{T}"/> instance, this mode allows each thread to execute the - /// valueFactory but only the first thread to complete initialization will be allowed to set the final value of the <see cref="T:System.Threading.Lazy{T}"/>. - /// Once initialized successfully, any future calls to Value will return the cached result. If valueFactory throws an exception on any thread, that exception will be - /// propagated out of Value. If any thread executes valueFactory without throwing an exception and, therefore, successfully sets the value, that value will be returned on - /// subsequent accesses to Value from any thread. If no thread succeeds in setting the value, IsValueCreated will remain false and subsequent accesses to Value will result in - /// the valueFactory delegate re-executing. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, an exception will NOT be thrown. - /// </summary> - PublicationOnly, - - /// <summary> - /// This mode uses locks to ensure that only a single thread can initialize a <see cref="T:System.Threading.Lazy{T}"/> instance in a thread-safe manner. In general, - /// taken if this mode is used in conjunction with a <see cref="T:System.Threading.Lazy{T}"/> valueFactory delegate that uses locks internally, a deadlock can occur if not - /// handled carefully. If valueFactory throws an exception when the<see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on - /// subsequent accesses to Value. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown. - /// </summary> - ExecutionAndPublication - } /// <summary> /// Provides lazy initialization routines. /// </summary> @@ -82,16 +51,8 @@ namespace System.Threading /// if an object was not used and to then dispose of the object appropriately. /// </para> /// </remarks> - public static T EnsureInitialized<T>(ref T target) where T : class - { - // Fast path. - if (Volatile.Read<T>(ref target) != null) - { - return target; - } - - return EnsureInitializedCore<T>(ref target, LazyHelpers<T>.s_activatorFactorySelector); - } + public static T EnsureInitialized<T>(ref T target) where T : class => + Volatile.Read<T>(ref target) ?? EnsureInitializedCore<T>(ref target, LazyHelpers<T>.s_activatorFactorySelector); /// <summary> /// Initializes a target reference type using the specified function if it has not already been @@ -121,16 +82,8 @@ namespace System.Threading /// if an object was not used and to then dispose of the object appropriately. /// </para> /// </remarks> - public static T EnsureInitialized<T>(ref T target, Func<T> valueFactory) where T : class - { - // Fast path. - if (Volatile.Read<T>(ref target) != null) - { - return target; - } - - return EnsureInitializedCore<T>(ref target, valueFactory); - } + public static T EnsureInitialized<T>(ref T target, Func<T> valueFactory) where T : class => + Volatile.Read<T>(ref target) ?? EnsureInitializedCore<T>(ref target, valueFactory); /// <summary> /// Initialize the target using the given delegate (slow path). @@ -144,7 +97,7 @@ namespace System.Threading T value = valueFactory(); if (value == null) { - throw new InvalidOperationException(Environment.GetResourceString("Lazy_StaticInit_InvalidOperation")); + throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); } Interlocked.CompareExchange(ref target, value, null); @@ -152,7 +105,6 @@ namespace System.Threading return target; } - /// <summary> /// Initializes a target reference or value type with its default constructor if it has not already /// been initialized. @@ -198,11 +150,33 @@ namespace System.Threading return target; } - return EnsureInitializedCore<T>(ref target, ref initialized, ref syncLock, valueFactory); } /// <summary> + /// Ensure the lock object is intialized. + /// </summary> + /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null, + /// a new object will be instantiated.</param> + /// <returns>Initialized lock object.</returns> + private static object EnsureLockInitialized(ref object syncLock) => + syncLock ?? + Interlocked.CompareExchange(ref syncLock, new object(), null) ?? + syncLock; + + /// <summary> + /// Initializes a target reference type with a specified function if it has not already been initialized. + /// </summary> + /// <typeparam name="T">The type of the reference to be initialized. Has to be reference type.</typeparam> + /// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not already been initialized.</param> + /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing + /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param> + /// <param name="valueFactory">The <see cref="T:System.Func{T}"/> invoked to initialize the reference.</param> + /// <returns>The initialized value of type <typeparamref name="T"/>.</returns> + public static T EnsureInitialized<T>(ref T target, ref object syncLock, Func<T> valueFactory) where T : class => + Volatile.Read(ref target) ?? EnsureInitializedCore<T>(ref target, ref syncLock, valueFactory); + + /// <summary> /// Ensure the target is initialized and return the value (slow path). This overload permits nulls /// and also works for value type targets. Uses the supplied function to create the value. /// </summary> @@ -217,35 +191,52 @@ namespace System.Threading /// <returns>The initialized object.</returns> private static T EnsureInitializedCore<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory) { - // Lazily initialize the lock if necessary. - object slock = syncLock; - if (slock == null) + // Lazily initialize the lock if necessary and, then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) { - object newLock = new object(); - slock = Interlocked.CompareExchange(ref syncLock, newLock, null); - if (slock == null) + if (!Volatile.Read(ref initialized)) { - slock = newLock; + target = valueFactory(); + Volatile.Write(ref initialized, true); } } - // Now double check that initialization is still required. - lock (slock) + return target; + } + + /// <summary> + /// Ensure the target is initialized and return the value (slow path). This overload works only for reference type targets. + /// Uses the supplied function to create the value. + /// </summary> + /// <typeparam name="T">The type of target. Has to be reference type.</typeparam> + /// <param name="target">A reference to the target to be initialized.</param> + /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null, + /// a new object will be instantiated.</param> + /// <param name="valueFactory"> + /// The <see cref="T:System.Func{T}"/> to invoke in order to produce the lazily-initialized value. + /// </param> + /// <returns>The initialized object.</returns> + private static T EnsureInitializedCore<T>(ref T target, ref object syncLock, Func<T> valueFactory) where T : class + { + // Lazily initialize the lock if necessary and, then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) { - if (!Volatile.Read(ref initialized)) + if (Volatile.Read(ref target) == null) { - target = valueFactory(); - Volatile.Write(ref initialized, true); + Volatile.Write(ref target, valueFactory()); + if (target == null) + { + throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); + } } } return target; } - } // Caches the activation selector function to avoid delegate allocations. - static class LazyHelpers<T> + internal static class LazyHelpers<T> { internal static Func<T> s_activatorFactorySelector = new Func<T>(ActivatorFactorySelector); @@ -257,7 +248,7 @@ namespace System.Threading } catch (MissingMethodException) { - throw new MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT")); + throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); } } } diff --git a/src/mscorlib/src/System/Threading/LockRecursionException.cs b/src/mscorlib/src/System/Threading/LockRecursionException.cs deleted file mode 100644 index 40f04b00b4..0000000000 --- a/src/mscorlib/src/System/Threading/LockRecursionException.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -// -// -// -// Purpose: -// This exception represents a failed attempt to recursively -// acquire a lock, because the particular lock kind doesn't -// support it in its current state. -============================================================*/ - -namespace System.Threading -{ - using System; - using System.Runtime.Serialization; - using System.Runtime.CompilerServices; - - [Serializable] - public class LockRecursionException : System.Exception - { - public LockRecursionException() { } - public LockRecursionException(string message) : base(message) { } - protected LockRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) { } - public LockRecursionException(string message, Exception innerException) : base(message, innerException) { } - } - -} diff --git a/src/mscorlib/src/System/Threading/ManualResetEvent.cs b/src/mscorlib/src/System/Threading/ManualResetEvent.cs deleted file mode 100644 index a8e012fb43..0000000000 --- a/src/mscorlib/src/System/Threading/ManualResetEvent.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: An example of a WaitHandle class -** -** -=============================================================================*/ -namespace System.Threading { - - using System; - using System.Runtime.InteropServices; - - public sealed class ManualResetEvent : EventWaitHandle - { - public ManualResetEvent(bool initialState) : base(initialState,EventResetMode.ManualReset){} - } -} - diff --git a/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs index 2d57b4102b..402a76cdc7 100644 --- a/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs +++ b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs @@ -20,7 +20,6 @@ using System.Diagnostics.Contracts; namespace System.Threading { - // ManualResetEventSlim wraps a manual-reset event internally with a little bit of // spinning. When an event will be set imminently, it is often advantageous to avoid // a 4k+ cycle context switch in favor of briefly spinning. Therefore we layer on to @@ -98,7 +97,6 @@ namespace System.Threading /// </remarks> public WaitHandle WaitHandle { - get { ThrowIfDisposed(); @@ -165,11 +163,10 @@ namespace System.Threading // it is possible for the max number of waiters to be exceeded via user-code, hence we use a real exception here. if (value >= NumWaitersState_MaxValue) - throw new InvalidOperationException(String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_TooManyWaiters"), NumWaitersState_MaxValue)); + throw new InvalidOperationException(String.Format(SR.ManualResetEventSlim_ctor_TooManyWaiters, NumWaitersState_MaxValue)); UpdateStateAtomically(value << NumWaitersState_ShiftCount, NumWaitersState_BitMask); } - } //----------------------------------------------------------------------------------- @@ -184,7 +181,6 @@ namespace System.Threading public ManualResetEventSlim() : this(false) { - } /// <summary> @@ -222,7 +218,7 @@ namespace System.Threading { throw new ArgumentOutOfRangeException( nameof(spinCount), - String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_SpinCountOutOfRange"), SpinCountState_MaxValue)); + String.Format(SR.ManualResetEventSlim_ctor_SpinCountOutOfRange, SpinCountState_MaxValue)); } // We will suppress default spin because the user specified a count. @@ -236,14 +232,13 @@ namespace System.Threading /// <param name="spinCount">The spin count that decides when the event will block.</param> private void Initialize(bool initialState, int spinCount) { - this.m_combinedState = initialState ? (1 << SignalledState_ShiftCount) : 0; + m_combinedState = initialState ? (1 << SignalledState_ShiftCount) : 0; //the spinCount argument has been validated by the ctors. //but we now sanity check our predefined constants. Debug.Assert(DEFAULT_SPIN_SP >= 0, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); Debug.Assert(DEFAULT_SPIN_SP <= SpinCountState_MaxValue, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); SpinCount = PlatformHelper.IsSingleProcessor ? DEFAULT_SPIN_SP : spinCount; - } /// <summary> @@ -283,7 +278,6 @@ namespace System.Threading } else { - // Now that the event is published, verify that the state hasn't changed since // we snapped the preInitializeState. Another thread could have done that // between our initial observation above and here. The barrier incurred from @@ -337,7 +331,6 @@ namespace System.Threading Debug.Assert(m_lock != null); //if waiters>0, then m_lock has already been created. lock (m_lock) { - Monitor.PulseAll(m_lock); } } @@ -665,7 +658,6 @@ namespace System.Threading // Now just loop back around, and the right thing will happen. Either: // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) // or 2. the wait was successful. (the loop will break) - } } } @@ -725,7 +717,7 @@ namespace System.Threading private void ThrowIfDisposed() { if ((m_combinedState & Dispose_BitMask) != 0) - throw new ObjectDisposedException(Environment.GetResourceString("ManualResetEventSlim_Disposed")); + throw new ObjectDisposedException(SR.ManualResetEventSlim_Disposed); } /// <summary> diff --git a/src/mscorlib/src/System/Threading/Monitor.cs b/src/mscorlib/src/System/Threading/Monitor.cs index fdbc384243..3ace3335aa 100644 --- a/src/mscorlib/src/System/Threading/Monitor.cs +++ b/src/mscorlib/src/System/Threading/Monitor.cs @@ -14,19 +14,20 @@ =============================================================================*/ -namespace System.Threading { - - using System; - using System.Runtime; - using System.Runtime.Remoting; - using System.Threading; - using System.Runtime.CompilerServices; - using System.Runtime.ConstrainedExecution; - using System.Runtime.Versioning; - using System.Diagnostics; - using System.Diagnostics.Contracts; - - public static class Monitor + +using System; +using System.Runtime; +using System.Runtime.Remoting; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System.Threading +{ + public static class Monitor { /*========================================================================= ** Obtain the monitor lock of obj. Will block if another thread holds the lock @@ -56,7 +57,7 @@ namespace System.Threading { private static void ThrowLockTakenException() { - throw new ArgumentException(Environment.GetResourceString("Argument_MustBeFalse"), "lockTaken"); + throw new ArgumentException(SR.Argument_MustBeFalse, "lockTaken"); } [MethodImplAttribute(MethodImplOptions.InternalCall)] @@ -75,7 +76,7 @@ namespace System.Threading { =========================================================================*/ [MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern void Exit(Object obj); - + /*========================================================================= ** Similar to Enter, but will never block. That is, if the current thread can ** acquire the monitor lock without blocking, it will do so and TRUE will @@ -99,7 +100,7 @@ namespace System.Threading { ReliableEnterTimeout(obj, 0, ref lockTaken); } - + /*========================================================================= ** Version of TryEnter that will block, but only up to a timeout period ** expressed in milliseconds. If timeout == Timeout.Infinite the method @@ -121,7 +122,7 @@ namespace System.Threading { { long tm = (long)timeout.TotalMilliseconds; if (tm < -1 || tm > (long)Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); return (int)tm; } diff --git a/src/mscorlib/src/System/Threading/Mutex.cs b/src/mscorlib/src/System/Threading/Mutex.cs index dcb821307a..454a323f9a 100644 --- a/src/mscorlib/src/System/Threading/Mutex.cs +++ b/src/mscorlib/src/System/Threading/Mutex.cs @@ -11,8 +11,9 @@ ** ** =============================================================================*/ -namespace System.Threading -{ + +namespace System.Threading +{ using System; using System.Threading; using System.Runtime.CompilerServices; @@ -28,7 +29,7 @@ namespace System.Threading public sealed class Mutex : WaitHandle { - static bool dummyBool; + private static bool dummyBool; internal class MutexSecurity { @@ -46,12 +47,12 @@ namespace System.Threading // Empty name is treated as an unnamed mutex. Set to null, and we will check for null from now on. name = null; } -#if !PLATFORM_UNIX +#if PLATFORM_WINDOWS if (name != null && System.IO.Path.MaxPath < name.Length) { - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); } -#endif +#endif // PLATFORM_WINDOWS Contract.EndContractBlock(); Win32Native.SECURITY_ATTRIBUTES secAttrs = null; @@ -67,20 +68,20 @@ namespace System.Threading RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup( tryCode, cleanupCode, - cleanupInfo); + cleanupInfo); createdNew = tryCodeHelper.m_newMutex; } - internal class MutexTryCodeHelper + internal class MutexTryCodeHelper { - bool m_initiallyOwned; - MutexCleanupInfo m_cleanupInfo; + private bool m_initiallyOwned; + private MutexCleanupInfo m_cleanupInfo; internal bool m_newMutex; - String m_name; - Win32Native.SECURITY_ATTRIBUTES m_secAttrs; - Mutex m_mutex; + private String m_name; + private Win32Native.SECURITY_ATTRIBUTES m_secAttrs; + private Mutex m_mutex; - internal MutexTryCodeHelper(bool initiallyOwned,MutexCleanupInfo cleanupInfo, String name, Win32Native.SECURITY_ATTRIBUTES secAttrs, Mutex mutex) + internal MutexTryCodeHelper(bool initiallyOwned, MutexCleanupInfo cleanupInfo, String name, Win32Native.SECURITY_ATTRIBUTES secAttrs, Mutex mutex) { Debug.Assert(name == null || name.Length != 0); @@ -92,16 +93,16 @@ namespace System.Threading } internal void MutexTryCode(object userData) - { + { SafeWaitHandle mutexHandle = null; // try block RuntimeHelpers.PrepareConstrainedRegions(); - try + try { } - finally + finally { - if (m_initiallyOwned) + if (m_initiallyOwned) { m_cleanupInfo.inCriticalRegion = true; } @@ -109,15 +110,15 @@ namespace System.Threading int errorCode = 0; RuntimeHelpers.PrepareConstrainedRegions(); - try + try { } - finally + finally { errorCode = CreateMutexHandle(m_initiallyOwned, m_name, m_secAttrs, out mutexHandle); - } + } - if (mutexHandle.IsInvalid) + if (mutexHandle.IsInvalid) { mutexHandle.SetHandleAsInvalid(); if (m_name != null) @@ -127,11 +128,11 @@ namespace System.Threading #if PLATFORM_UNIX case Win32Native.ERROR_FILENAME_EXCED_RANGE: // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Interop.Sys.MaxName), "name"); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Sys.MaxName), "name"); #endif case Win32Native.ERROR_INVALID_HANDLE: - throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", m_name)); + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, m_name)); } } __Error.WinIOError(errorCode, m_name); @@ -140,19 +141,21 @@ namespace System.Threading m_mutex.SetHandleInternal(mutexHandle); m_mutex.hasThreadAffinity = true; - } } private void MutexCleanupCode(Object userData, bool exceptionThrown) { - MutexCleanupInfo cleanupInfo = (MutexCleanupInfo) userData; - + MutexCleanupInfo cleanupInfo = (MutexCleanupInfo)userData; + // If hasThreadAffinity isn't true, we've thrown an exception in the above try, and we must free the mutex // on this OS thread before ending our thread affninity. - if(!hasThreadAffinity) { - if (cleanupInfo.mutexHandle != null && !cleanupInfo.mutexHandle.IsInvalid) { - if( cleanupInfo.inCriticalRegion) { + if (!hasThreadAffinity) + { + if (cleanupInfo.mutexHandle != null && !cleanupInfo.mutexHandle.IsInvalid) + { + if (cleanupInfo.inCriticalRegion) + { Win32Native.ReleaseMutex(cleanupInfo.mutexHandle); } cleanupInfo.mutexHandle.Dispose(); @@ -171,7 +174,8 @@ namespace System.Threading } } - public Mutex(bool initiallyOwned, String name) : this(initiallyOwned, name, out dummyBool) { + public Mutex(bool initiallyOwned, String name) : this(initiallyOwned, name, out dummyBool) + { } public Mutex(bool initiallyOwned) : this(initiallyOwned, null, out dummyBool) @@ -181,7 +185,7 @@ namespace System.Threading public Mutex() : this(false, null, out dummyBool) { } - + private Mutex(SafeWaitHandle handle) { SetHandleInternal(handle); @@ -190,7 +194,7 @@ namespace System.Threading public static Mutex OpenExisting(string name) { - return OpenExisting(name, (MutexRights) 0); + return OpenExisting(name, (MutexRights)0); } internal enum MutexRights @@ -206,7 +210,7 @@ namespace System.Threading throw new WaitHandleCannotBeOpenedException(); case OpenExistingResult.NameInvalid: - throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name)); + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); case OpenExistingResult.PathNotFound: __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, name); @@ -226,17 +230,17 @@ namespace System.Threading { if (name == null) { - throw new ArgumentNullException(nameof(name), Environment.GetResourceString("ArgumentNull_WithParamName")); + throw new ArgumentNullException(nameof(name), SR.ArgumentNull_WithParamName); } - if(name.Length == 0) + if (name.Length == 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), nameof(name)); + throw new ArgumentException(SR.Argument_EmptyName, nameof(name)); } #if !PLATFORM_UNIX - if(System.IO.Path.MaxPath < name.Length) + if (System.IO.Path.MaxPath < name.Length) { - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); } #endif Contract.EndContractBlock(); @@ -258,11 +262,11 @@ namespace System.Threading if (name != null && errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE) { // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Interop.Sys.MaxName), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Sys.MaxName), nameof(name)); } #endif - if(Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode) + if (Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode) return OpenExistingResult.NameNotFound; if (Win32Native.ERROR_PATH_NOT_FOUND == errorCode) return OpenExistingResult.PathNotFound; @@ -270,7 +274,7 @@ namespace System.Threading return OpenExistingResult.NameInvalid; // this is for passed through Win32Native Errors - __Error.WinIOError(errorCode,name); + __Error.WinIOError(errorCode, name); } result = new Mutex(myHandle); @@ -287,11 +291,11 @@ namespace System.Threading } else { - throw new ApplicationException(Environment.GetResourceString("Arg_SynchronizationLockException")); + throw new ApplicationException(SR.Arg_SynchronizationLockException); } } - static int CreateMutexHandle(bool initiallyOwned, String name, Win32Native.SECURITY_ATTRIBUTES securityAttribute, out SafeWaitHandle mutexHandle) + private static int CreateMutexHandle(bool initiallyOwned, String name, Win32Native.SECURITY_ATTRIBUTES securityAttribute, out SafeWaitHandle mutexHandle) { int errorCode; diff --git a/src/mscorlib/src/System/Threading/Overlapped.cs b/src/mscorlib/src/System/Threading/Overlapped.cs index d3caff5e74..0830ee6b6c 100644 --- a/src/mscorlib/src/System/Threading/Overlapped.cs +++ b/src/mscorlib/src/System/Threading/Overlapped.cs @@ -24,18 +24,18 @@ =============================================================================*/ -namespace System.Threading -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Runtime.Versioning; - using System.Security; - using System.Runtime.ConstrainedExecution; - using System.Diagnostics; - using System.Diagnostics.Contracts; - using System.Collections.Concurrent; - +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using System.Security; +using System.Runtime.ConstrainedExecution; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Collections.Concurrent; + +namespace System.Threading +{ #region struct NativeOverlapped // Valuetype that represents the (unmanaged) Win32 OVERLAPPED structure @@ -45,11 +45,11 @@ namespace System.Threading [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] public struct NativeOverlapped { - public IntPtr InternalLow; - public IntPtr InternalHigh; - public int OffsetLow; - public int OffsetHigh; - public IntPtr EventHandle; + public IntPtr InternalLow; + public IntPtr InternalHigh; + public int OffsetLow; + public int OffsetHigh; + public IntPtr EventHandle; } #endregion struct NativeOverlapped @@ -59,15 +59,11 @@ namespace System.Threading unsafe internal class _IOCompletionCallback { - IOCompletionCallback _ioCompletionCallback; - ExecutionContext _executionContext; - uint _errorCode; // Error code - uint _numBytes; // No. of bytes transferred - NativeOverlapped* _pOVERLAP; - - static _IOCompletionCallback() - { - } + private IOCompletionCallback _ioCompletionCallback; + private ExecutionContext _executionContext; + private uint _errorCode; // Error code + private uint _numBytes; // No. of bytes transferred + private NativeOverlapped* _pOVERLAP; internal _IOCompletionCallback(IOCompletionCallback ioCompletionCallback) { @@ -79,12 +75,12 @@ namespace System.Threading static internal ContextCallback _ccb = new ContextCallback(IOCompletionCallback_Context); static internal void IOCompletionCallback_Context(Object state) { - _IOCompletionCallback helper = (_IOCompletionCallback)state; - Debug.Assert(helper != null,"_IOCompletionCallback cannot be null"); + _IOCompletionCallback helper = (_IOCompletionCallback)state; + Debug.Assert(helper != null, "_IOCompletionCallback cannot be null"); helper._ioCompletionCallback(helper._errorCode, helper._numBytes, helper._pOVERLAP); } - + // call back helper static unsafe internal void PerformIOCompletionCallback(uint errorCode, // Error code uint numBytes, // No. of bytes transferred @@ -97,13 +93,13 @@ namespace System.Threading do { overlapped = OverlappedData.GetOverlappedFromNative(pOVERLAP).m_overlapped; - helper = overlapped.iocbHelper; + helper = overlapped.iocbHelper; if (helper == null || helper._executionContext == null || helper._executionContext == ExecutionContext.Default) { // We got here because of UnsafePack (or) Pack with EC flow supressed IOCompletionCallback callback = overlapped.UserCallback; - callback( errorCode, numBytes, pOVERLAP); + callback(errorCode, numBytes, pOVERLAP); } else { @@ -112,12 +108,11 @@ namespace System.Threading helper._numBytes = numBytes; helper._pOVERLAP = pOVERLAP; ExecutionContext.Run(helper._executionContext, _ccb, helper); - } + } //Quickly check the VM again, to see if a packet has arrived. OverlappedData.CheckVMForIOPacket(out pOVERLAP, out errorCode, out numBytes); } while (pOVERLAP != null); - } } @@ -147,7 +142,7 @@ namespace System.Threading internal NativeOverlapped m_nativeOverlapped; // Adding an empty default ctor for annotation purposes - internal OverlappedData(){} + internal OverlappedData() { } internal void ReInitialize() { @@ -169,8 +164,9 @@ namespace System.Threading unsafe internal NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData) { - if (!m_pinSelf.IsNull()) { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_Overlapped_Pack")); + if (!m_pinSelf.IsNull()) + { + throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } if (iocb != null) @@ -199,9 +195,10 @@ namespace System.Threading } unsafe internal NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData) - { - if (!m_pinSelf.IsNull()) { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_Overlapped_Pack")); + { + if (!m_pinSelf.IsNull()) + { + throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } m_userObject = userData; if (m_userObject != null) @@ -225,7 +222,7 @@ namespace System.Threading get { return m_nativeOverlapped.EventHandle; } set { m_nativeOverlapped.EventHandle = value; } } - + [MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe private extern NativeOverlapped* AllocateNativeOverlapped(); @@ -233,7 +230,7 @@ namespace System.Threading unsafe internal static extern void FreeNativeOverlapped(NativeOverlapped* nativeOverlappedPtr); [MethodImplAttribute(MethodImplOptions.InternalCall)] - unsafe internal static extern OverlappedData GetOverlappedFromNative(NativeOverlapped* nativeOverlappedPtr); + unsafe internal static extern OverlappedData GetOverlappedFromNative(NativeOverlapped* nativeOverlappedPtr); [MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe internal static extern void CheckVMForIOPacket(out NativeOverlapped* pOVERLAP, out uint errorCode, out uint numBytes); @@ -244,21 +241,20 @@ namespace System.Threading #region class Overlapped - /// <internalonly/> public class Overlapped { private OverlappedData m_overlappedData; - private static PinnableBufferCache s_overlappedDataCache = new PinnableBufferCache("System.Threading.OverlappedData", ()=> new OverlappedData()); - - public Overlapped() + private static PinnableBufferCache s_overlappedDataCache = new PinnableBufferCache("System.Threading.OverlappedData", () => new OverlappedData()); + + public Overlapped() { - m_overlappedData = (OverlappedData) s_overlappedDataCache.Allocate(); + m_overlappedData = (OverlappedData)s_overlappedDataCache.Allocate(); m_overlappedData.m_overlapped = this; } public Overlapped(int offsetLo, int offsetHi, IntPtr hEvent, IAsyncResult ar) { - m_overlappedData = (OverlappedData) s_overlappedDataCache.Allocate(); + m_overlappedData = (OverlappedData)s_overlappedDataCache.Allocate(); m_overlappedData.m_overlapped = this; m_overlappedData.m_nativeOverlapped.OffsetLow = offsetLo; m_overlappedData.m_nativeOverlapped.OffsetHigh = offsetHi; @@ -321,7 +317,7 @@ namespace System.Threading [CLSCompliant(false)] unsafe public NativeOverlapped* Pack(IOCompletionCallback iocb) { - return Pack (iocb, null); + return Pack(iocb, null); } [CLSCompliant(false)] @@ -334,12 +330,12 @@ namespace System.Threading [CLSCompliant(false)] unsafe public NativeOverlapped* UnsafePack(IOCompletionCallback iocb) { - return UnsafePack (iocb, null); + return UnsafePack(iocb, null); } [CLSCompliant(false)] unsafe public NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData) - { + { return m_overlappedData.UnsafePack(iocb, userData); } @@ -355,7 +351,7 @@ namespace System.Threading Contract.EndContractBlock(); Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped; - + return overlapped; } @@ -373,9 +369,7 @@ namespace System.Threading overlappedData.ReInitialize(); s_overlappedDataCache.Free(overlappedData); } - } #endregion class Overlapped - } // namespace diff --git a/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs b/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs deleted file mode 100644 index 32b63153c4..0000000000 --- a/src/mscorlib/src/System/Threading/ParameterizedThreadStart.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: This class is a Delegate which defines the start method -** for starting a thread. That method must match this delegate. -** -** -=============================================================================*/ - - -namespace System.Threading { - using System.Threading; - using System.Runtime.InteropServices; - - [ComVisibleAttribute(false)] - public delegate void ParameterizedThreadStart(object obj); -} diff --git a/src/mscorlib/src/System/Threading/ReaderWriterLockSlim.cs b/src/mscorlib/src/System/Threading/ReaderWriterLockSlim.cs new file mode 100644 index 0000000000..98517ad85f --- /dev/null +++ b/src/mscorlib/src/System/Threading/ReaderWriterLockSlim.cs @@ -0,0 +1,1311 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Runtime.Augments; +using System.Diagnostics; // for TraceInformation +using System.Threading; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + public enum LockRecursionPolicy + { + NoRecursion = 0, + SupportsRecursion = 1, + } + + // + // ReaderWriterCount tracks how many of each kind of lock is held by each thread. + // We keep a linked list for each thread, attached to a ThreadStatic field. + // These are reused wherever possible, so that a given thread will only + // allocate N of these, where N is the maximum number of locks held simultaneously + // by that thread. + // + internal class ReaderWriterCount + { + // Which lock does this object belong to? This is a numeric ID for two reasons: + // 1) We don't want this field to keep the lock object alive, and a WeakReference would + // be too expensive. + // 2) Setting the value of a long is faster than setting the value of a reference. + // The "hot" paths in ReaderWriterLockSlim are short enough that this actually + // matters. + public long lockID; + + // How many reader locks does this thread hold on this ReaderWriterLockSlim instance? + public int readercount; + + // Ditto for writer/upgrader counts. These are only used if the lock allows recursion. + // But we have to have the fields on every ReaderWriterCount instance, because + // we reuse it for different locks. + public int writercount; + public int upgradecount; + + // Next RWC in this thread's list. + public ReaderWriterCount next; + } + + /// <summary> + /// A reader-writer lock implementation that is intended to be simple, yet very + /// efficient. In particular only 1 interlocked operation is taken for any lock + /// operation (we use spin locks to achieve this). The spin lock is never held + /// for more than a few instructions (in particular, we never call event APIs + /// or in fact any non-trivial API while holding the spin lock). + /// </summary> + public class ReaderWriterLockSlim : IDisposable + { + //Specifying if locked can be reacquired recursively. + private bool _fIsReentrant; + + // Lock specification for myLock: This lock protects exactly the local fields associated with this + // instance of ReaderWriterLockSlim. It does NOT protect the memory associated with + // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). + private int _myLock; + + //The variables controlling spinning behavior of Mylock(which is a spin-lock) + + private const int LockSpinCycles = 20; + private const int LockSpinCount = 10; + private const int LockSleep0Count = 5; + + // These variables allow use to avoid Setting events (which is expensive) if we don't have to. + private uint _numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent + private uint _numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent + private uint _numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). + private uint _numUpgradeWaiters; + + //Variable used for quick check when there are no waiters. + private bool _fNoWaiters; + + private int _upgradeLockOwnerId; + private int _writeLockOwnerId; + + // conditions we wait on. + private EventWaitHandle _writeEvent; // threads waiting to acquire a write lock go here. + private EventWaitHandle _readEvent; // threads waiting to acquire a read lock go here (will be released in bulk) + private EventWaitHandle _upgradeEvent; // thread waiting to acquire the upgrade lock + private EventWaitHandle _waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one) + + // Every lock instance has a unique ID, which is used by ReaderWriterCount to associate itself with the lock + // without holding a reference to it. + private static long s_nextLockID; + private long _lockID; + + // See comments on ReaderWriterCount. + [ThreadStatic] + private static ReaderWriterCount t_rwc; + + private bool _fUpgradeThreadHoldingRead; + + private const int MaxSpinCount = 20; + + //The uint, that contains info like if the writer lock is held, num of + //readers etc. + private uint _owners; + + //Various R/W masks + //Note: + //The Uint is divided as follows: + // + //Writer-Owned Waiting-Writers Waiting Upgraders Num-Readers + // 31 30 29 28.......0 + // + //Dividing the uint, allows to vastly simplify logic for checking if a + //reader should go in etc. Setting the writer bit will automatically + //make the value of the uint much larger than the max num of readers + //allowed, thus causing the check for max_readers to fail. + + private const uint WRITER_HELD = 0x80000000; + private const uint WAITING_WRITERS = 0x40000000; + private const uint WAITING_UPGRADER = 0x20000000; + + //The max readers is actually one less then its theoretical max. + //This is done in order to prevent reader count overflows. If the reader + //count reaches max, other readers will wait. + private const uint MAX_READER = 0x10000000 - 2; + + private const uint READER_MASK = 0x10000000 - 1; + + private bool _fDisposed; + + private void InitializeThreadCounts() + { + _upgradeLockOwnerId = -1; + _writeLockOwnerId = -1; + } + + public ReaderWriterLockSlim() + : this(LockRecursionPolicy.NoRecursion) + { + } + + public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy) + { + if (recursionPolicy == LockRecursionPolicy.SupportsRecursion) + { + _fIsReentrant = true; + } + InitializeThreadCounts(); + _fNoWaiters = true; + _lockID = Interlocked.Increment(ref s_nextLockID); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsRWEntryEmpty(ReaderWriterCount rwc) + { + if (rwc.lockID == 0) + return true; + else if (rwc.readercount == 0 && rwc.writercount == 0 && rwc.upgradecount == 0) + return true; + else + return false; + } + + private bool IsRwHashEntryChanged(ReaderWriterCount lrwc) + { + return lrwc.lockID != _lockID; + } + + /// <summary> + /// This routine retrieves/sets the per-thread counts needed to enforce the + /// various rules related to acquiring the lock. + /// + /// DontAllocate is set to true if the caller just wants to get an existing + /// entry for this thread, but doesn't want to add one if an existing one + /// could not be found. + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReaderWriterCount GetThreadRWCount(bool dontAllocate) + { + ReaderWriterCount rwc = t_rwc; + ReaderWriterCount empty = null; + while (rwc != null) + { + if (rwc.lockID == _lockID) + return rwc; + + if (!dontAllocate && empty == null && IsRWEntryEmpty(rwc)) + empty = rwc; + + rwc = rwc.next; + } + + if (dontAllocate) + return null; + + if (empty == null) + { + empty = new ReaderWriterCount(); + empty.next = t_rwc; + t_rwc = empty; + } + + empty.lockID = _lockID; + return empty; + } + + public void EnterReadLock() + { + TryEnterReadLock(-1); + } + + // + // Common timeout support + // + private struct TimeoutTracker + { + private int _total; + private int _start; + + public TimeoutTracker(TimeSpan timeout) + { + long ltm = (long)timeout.TotalMilliseconds; + if (ltm < -1 || ltm > (long)Int32.MaxValue) + throw new ArgumentOutOfRangeException(nameof(timeout)); + _total = (int)ltm; + if (_total != -1 && _total != 0) + _start = Environment.TickCount; + else + _start = 0; + } + + public TimeoutTracker(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); + _total = millisecondsTimeout; + if (_total != -1 && _total != 0) + _start = Environment.TickCount; + else + _start = 0; + } + + public int RemainingMilliseconds + { + get + { + if (_total == -1 || _total == 0) + return _total; + + int elapsed = Environment.TickCount - _start; + // elapsed may be negative if TickCount has overflowed by 2^31 milliseconds. + if (elapsed < 0 || elapsed >= _total) + return 0; + + return _total - elapsed; + } + } + + public bool IsExpired + { + get + { + return RemainingMilliseconds == 0; + } + } + } + + public bool TryEnterReadLock(TimeSpan timeout) + { + return TryEnterReadLock(new TimeoutTracker(timeout)); + } + + public bool TryEnterReadLock(int millisecondsTimeout) + { + return TryEnterReadLock(new TimeoutTracker(millisecondsTimeout)); + } + + private bool TryEnterReadLock(TimeoutTracker timeout) + { + return TryEnterReadLockCore(timeout); + } + + private bool TryEnterReadLockCore(TimeoutTracker timeout) + { + if (_fDisposed) + throw new ObjectDisposedException(null); + + ReaderWriterCount lrwc = null; + int id = Environment.CurrentManagedThreadId; + + if (!_fIsReentrant) + { + if (id == _writeLockOwnerId) + { + //Check for AW->AR + throw new LockRecursionException(SR.LockRecursionException_ReadAfterWriteNotAllowed); + } + + EnterMyLock(); + + lrwc = GetThreadRWCount(false); + + //Check if the reader lock is already acquired. Note, we could + //check the presence of a reader by not allocating rwc (But that + //would lead to two lookups in the common case. It's better to keep + //a count in the structure). + if (lrwc.readercount > 0) + { + ExitMyLock(); + throw new LockRecursionException(SR.LockRecursionException_RecursiveReadNotAllowed); + } + else if (id == _upgradeLockOwnerId) + { + //The upgrade lock is already held. + //Update the global read counts and exit. + + lrwc.readercount++; + _owners++; + ExitMyLock(); + return true; + } + } + else + { + EnterMyLock(); + lrwc = GetThreadRWCount(false); + if (lrwc.readercount > 0) + { + lrwc.readercount++; + ExitMyLock(); + return true; + } + else if (id == _upgradeLockOwnerId) + { + //The upgrade lock is already held. + //Update the global read counts and exit. + lrwc.readercount++; + _owners++; + ExitMyLock(); + _fUpgradeThreadHoldingRead = true; + return true; + } + else if (id == _writeLockOwnerId) + { + //The write lock is already held. + //Update global read counts here, + lrwc.readercount++; + _owners++; + ExitMyLock(); + return true; + } + } + + bool retVal = true; + + int spincount = 0; + + for (; ;) + { + // We can enter a read lock if there are only read-locks have been given out + // and a writer is not trying to get in. + + if (_owners < MAX_READER) + { + // Good case, there is no contention, we are basically done + _owners++; // Indicate we have another reader + lrwc.readercount++; + break; + } + + if (spincount < MaxSpinCount) + { + ExitMyLock(); + if (timeout.IsExpired) + return false; + spincount++; + SpinWait(spincount); + EnterMyLock(); + //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + continue; + } + + // Drat, we need to wait. Mark that we have waiters and wait. + if (_readEvent == null) // Create the needed event + { + LazyCreateEvent(ref _readEvent, false); + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + continue; // since we left the lock, start over. + } + + retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout, isWriteWaiter: false); + if (!retVal) + { + return false; + } + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + } + + ExitMyLock(); + return retVal; + } + + public void EnterWriteLock() + { + TryEnterWriteLock(-1); + } + + public bool TryEnterWriteLock(TimeSpan timeout) + { + return TryEnterWriteLock(new TimeoutTracker(timeout)); + } + + public bool TryEnterWriteLock(int millisecondsTimeout) + { + return TryEnterWriteLock(new TimeoutTracker(millisecondsTimeout)); + } + + private bool TryEnterWriteLock(TimeoutTracker timeout) + { + return TryEnterWriteLockCore(timeout); + } + + private bool TryEnterWriteLockCore(TimeoutTracker timeout) + { + if (_fDisposed) + throw new ObjectDisposedException(null); + + int id = Environment.CurrentManagedThreadId; + ReaderWriterCount lrwc; + bool upgradingToWrite = false; + + if (!_fIsReentrant) + { + if (id == _writeLockOwnerId) + { + //Check for AW->AW + throw new LockRecursionException(SR.LockRecursionException_RecursiveWriteNotAllowed); + } + else if (id == _upgradeLockOwnerId) + { + //AU->AW case is allowed once. + upgradingToWrite = true; + } + + EnterMyLock(); + lrwc = GetThreadRWCount(true); + + //Can't acquire write lock with reader lock held. + if (lrwc != null && lrwc.readercount > 0) + { + ExitMyLock(); + throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); + } + } + else + { + EnterMyLock(); + lrwc = GetThreadRWCount(false); + + if (id == _writeLockOwnerId) + { + lrwc.writercount++; + ExitMyLock(); + return true; + } + else if (id == _upgradeLockOwnerId) + { + upgradingToWrite = true; + } + else if (lrwc.readercount > 0) + { + //Write locks may not be acquired if only read locks have been + //acquired. + ExitMyLock(); + throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); + } + } + + int spincount = 0; + bool retVal = true; + + for (; ;) + { + if (IsWriterAcquired()) + { + // Good case, there is no contention, we are basically done + SetWriterAcquired(); + break; + } + + //Check if there is just one upgrader, and no readers. + //Assumption: Only one thread can have the upgrade lock, so the + //following check will fail for all other threads that may sneak in + //when the upgrading thread is waiting. + + if (upgradingToWrite) + { + uint readercount = GetNumReaders(); + + if (readercount == 1) + { + //Good case again, there is just one upgrader, and no readers. + SetWriterAcquired(); // indicate we have a writer. + break; + } + else if (readercount == 2) + { + if (lrwc != null) + { + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + + if (lrwc.readercount > 0) + { + //This check is needed for EU->ER->EW case, as the owner count will be two. + Debug.Assert(_fIsReentrant); + Debug.Assert(_fUpgradeThreadHoldingRead); + + //Good case again, there is just one upgrader, and no readers. + SetWriterAcquired(); // indicate we have a writer. + break; + } + } + } + } + + if (spincount < MaxSpinCount) + { + ExitMyLock(); + if (timeout.IsExpired) + return false; + spincount++; + SpinWait(spincount); + EnterMyLock(); + continue; + } + + if (upgradingToWrite) + { + if (_waitUpgradeEvent == null) // Create the needed event + { + LazyCreateEvent(ref _waitUpgradeEvent, true); + continue; // since we left the lock, start over. + } + + Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); + + retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout, isWriteWaiter: true); + + //The lock is not held in case of failure. + if (!retVal) + return false; + } + else + { + // Drat, we need to wait. Mark that we have waiters and wait. + if (_writeEvent == null) // create the needed event. + { + LazyCreateEvent(ref _writeEvent, true); + continue; // since we left the lock, start over. + } + + retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout, isWriteWaiter: true); + //The lock is not held in case of failure. + if (!retVal) + return false; + } + } + + Debug.Assert((_owners & WRITER_HELD) > 0); + + if (_fIsReentrant) + { + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + lrwc.writercount++; + } + + ExitMyLock(); + + _writeLockOwnerId = id; + + return true; + } + + public void EnterUpgradeableReadLock() + { + TryEnterUpgradeableReadLock(-1); + } + + public bool TryEnterUpgradeableReadLock(TimeSpan timeout) + { + return TryEnterUpgradeableReadLock(new TimeoutTracker(timeout)); + } + + public bool TryEnterUpgradeableReadLock(int millisecondsTimeout) + { + return TryEnterUpgradeableReadLock(new TimeoutTracker(millisecondsTimeout)); + } + + private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) + { + return TryEnterUpgradeableReadLockCore(timeout); + } + + private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout) + { + if (_fDisposed) + throw new ObjectDisposedException(null); + + int id = Environment.CurrentManagedThreadId; + ReaderWriterCount lrwc; + + if (!_fIsReentrant) + { + if (id == _upgradeLockOwnerId) + { + //Check for AU->AU + throw new LockRecursionException(SR.LockRecursionException_RecursiveUpgradeNotAllowed); + } + else if (id == _writeLockOwnerId) + { + //Check for AU->AW + throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterWriteNotAllowed); + } + + EnterMyLock(); + lrwc = GetThreadRWCount(true); + //Can't acquire upgrade lock with reader lock held. + if (lrwc != null && lrwc.readercount > 0) + { + ExitMyLock(); + throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); + } + } + else + { + EnterMyLock(); + lrwc = GetThreadRWCount(false); + + if (id == _upgradeLockOwnerId) + { + lrwc.upgradecount++; + ExitMyLock(); + return true; + } + else if (id == _writeLockOwnerId) + { + //Write lock is already held, Just update the global state + //to show presence of upgrader. + Debug.Assert((_owners & WRITER_HELD) > 0); + _owners++; + _upgradeLockOwnerId = id; + lrwc.upgradecount++; + if (lrwc.readercount > 0) + _fUpgradeThreadHoldingRead = true; + ExitMyLock(); + return true; + } + else if (lrwc.readercount > 0) + { + //Upgrade locks may not be acquired if only read locks have been + //acquired. + ExitMyLock(); + throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); + } + } + + bool retVal = true; + + int spincount = 0; + + for (; ;) + { + //Once an upgrade lock is taken, it's like having a reader lock held + //until upgrade or downgrade operations are performed. + + if ((_upgradeLockOwnerId == -1) && (_owners < MAX_READER)) + { + _owners++; + _upgradeLockOwnerId = id; + break; + } + + if (spincount < MaxSpinCount) + { + ExitMyLock(); + if (timeout.IsExpired) + return false; + spincount++; + SpinWait(spincount); + EnterMyLock(); + continue; + } + + // Drat, we need to wait. Mark that we have waiters and wait. + if (_upgradeEvent == null) // Create the needed event + { + LazyCreateEvent(ref _upgradeEvent, true); + continue; // since we left the lock, start over. + } + + //Only one thread with the upgrade lock held can proceed. + retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout, isWriteWaiter: false); + if (!retVal) + return false; + } + + if (_fIsReentrant) + { + //The lock may have been dropped getting here, so make a quick check to see whether some other + //thread did not grab the entry. + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + lrwc.upgradecount++; + } + + ExitMyLock(); + + return true; + } + + public void ExitReadLock() + { + ReaderWriterCount lrwc = null; + + EnterMyLock(); + + lrwc = GetThreadRWCount(true); + + if (lrwc == null || lrwc.readercount < 1) + { + //You have to be holding the read lock to make this call. + ExitMyLock(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedRead); + } + + if (_fIsReentrant) + { + if (lrwc.readercount > 1) + { + lrwc.readercount--; + ExitMyLock(); + return; + } + + if (Environment.CurrentManagedThreadId == _upgradeLockOwnerId) + { + _fUpgradeThreadHoldingRead = false; + } + } + + Debug.Assert(_owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken"); + + --_owners; + + Debug.Assert(lrwc.readercount == 1); + lrwc.readercount--; + + ExitAndWakeUpAppropriateWaiters(); + } + + public void ExitWriteLock() + { + ReaderWriterCount lrwc; + if (!_fIsReentrant) + { + if (Environment.CurrentManagedThreadId != _writeLockOwnerId) + { + //You have to be holding the write lock to make this call. + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); + } + EnterMyLock(); + } + else + { + EnterMyLock(); + lrwc = GetThreadRWCount(false); + + if (lrwc == null) + { + ExitMyLock(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); + } + + if (lrwc.writercount < 1) + { + ExitMyLock(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); + } + + lrwc.writercount--; + + if (lrwc.writercount > 0) + { + ExitMyLock(); + return; + } + } + + Debug.Assert((_owners & WRITER_HELD) > 0, "Calling ReleaseWriterLock when no write lock is held"); + + ClearWriterAcquired(); + + _writeLockOwnerId = -1; + + ExitAndWakeUpAppropriateWaiters(); + } + + public void ExitUpgradeableReadLock() + { + ReaderWriterCount lrwc; + if (!_fIsReentrant) + { + if (Environment.CurrentManagedThreadId != _upgradeLockOwnerId) + { + //You have to be holding the upgrade lock to make this call. + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); + } + EnterMyLock(); + } + else + { + EnterMyLock(); + lrwc = GetThreadRWCount(true); + + if (lrwc == null) + { + ExitMyLock(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); + } + + if (lrwc.upgradecount < 1) + { + ExitMyLock(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); + } + + lrwc.upgradecount--; + + if (lrwc.upgradecount > 0) + { + ExitMyLock(); + return; + } + + _fUpgradeThreadHoldingRead = false; + } + + _owners--; + _upgradeLockOwnerId = -1; + + ExitAndWakeUpAppropriateWaiters(); + } + + /// <summary> + /// A routine for lazily creating a event outside the lock (so if errors + /// happen they are outside the lock and that we don't do much work + /// while holding a spin lock). If all goes well, reenter the lock and + /// set 'waitEvent' + /// </summary> + private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) + { +#if DEBUG + Debug.Assert(MyLockHeld); + Debug.Assert(waitEvent == null); +#endif + ExitMyLock(); + EventWaitHandle newEvent; + if (makeAutoResetEvent) + newEvent = new AutoResetEvent(false); + else + newEvent = new ManualResetEvent(false); + EnterMyLock(); + if (waitEvent == null) // maybe someone snuck in. + waitEvent = newEvent; + else + newEvent.Dispose(); + } + + /// <summary> + /// Waits on 'waitEvent' with a timeout + /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. + /// </summary> + private bool WaitOnEvent( + EventWaitHandle waitEvent, + ref uint numWaiters, + TimeoutTracker timeout, + bool isWriteWaiter) + { +#if DEBUG + Debug.Assert(MyLockHeld); +#endif + waitEvent.Reset(); + numWaiters++; + _fNoWaiters = false; + + //Setting these bits will prevent new readers from getting in. + if (_numWriteWaiters == 1) + SetWritersWaiting(); + if (_numWriteUpgradeWaiters == 1) + SetUpgraderWaiting(); + + bool waitSuccessful = false; + ExitMyLock(); // Do the wait outside of any lock + + try + { + waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); + } + finally + { + EnterMyLock(); + --numWaiters; + + if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) + _fNoWaiters = true; + + if (_numWriteWaiters == 0) + ClearWritersWaiting(); + if (_numWriteUpgradeWaiters == 0) + ClearUpgraderWaiting(); + + if (!waitSuccessful) // We may also be about to throw for some reason. Exit myLock. + { + if (isWriteWaiter) + { + // Write waiters block read waiters from acquiring the lock. Since this was the last write waiter, try + // to wake up the appropriate read waiters. + ExitAndWakeUpAppropriateReadWaiters(); + } + else + ExitMyLock(); + } + } + return waitSuccessful; + } + + /// <summary> + /// Determines the appropriate events to set, leaves the locks, and sets the events. + /// </summary> + private void ExitAndWakeUpAppropriateWaiters() + { +#if DEBUG + Debug.Assert(MyLockHeld); +#endif + if (_fNoWaiters) + { + ExitMyLock(); + return; + } + + ExitAndWakeUpAppropriateWaitersPreferringWriters(); + } + + private void ExitAndWakeUpAppropriateWaitersPreferringWriters() + { + uint readercount = GetNumReaders(); + + //We need this case for EU->ER->EW case, as the read count will be 2 in + //that scenario. + if (_fIsReentrant) + { + if (_numWriteUpgradeWaiters > 0 && _fUpgradeThreadHoldingRead && readercount == 2) + { + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). + return; + } + } + + if (readercount == 1 && _numWriteUpgradeWaiters > 0) + { + //We have to be careful now, as we are dropping the lock. + //No new writes should be allowed to sneak in if an upgrade + //was pending. + + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). + } + else if (readercount == 0 && _numWriteWaiters > 0) + { + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + _writeEvent.Set(); // release one writer. + } + else + { + ExitAndWakeUpAppropriateReadWaiters(); + } + } + + private void ExitAndWakeUpAppropriateReadWaiters() + { +#if DEBUG + Debug.Assert(MyLockHeld); +#endif + + if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || _fNoWaiters) + { + ExitMyLock(); + return; + } + + Debug.Assert(_numReadWaiters != 0 || _numUpgradeWaiters != 0); + + bool setReadEvent = _numReadWaiters != 0; + bool setUpgradeEvent = _numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1; + + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + + if (setReadEvent) + _readEvent.Set(); // release all readers. + + if (setUpgradeEvent) + _upgradeEvent.Set(); //release one upgrader. + } + + private bool IsWriterAcquired() + { + return (_owners & ~WAITING_WRITERS) == 0; + } + + private void SetWriterAcquired() + { + _owners |= WRITER_HELD; // indicate we have a writer. + } + + private void ClearWriterAcquired() + { + _owners &= ~WRITER_HELD; + } + + private void SetWritersWaiting() + { + _owners |= WAITING_WRITERS; + } + + private void ClearWritersWaiting() + { + _owners &= ~WAITING_WRITERS; + } + + private void SetUpgraderWaiting() + { + _owners |= WAITING_UPGRADER; + } + + private void ClearUpgraderWaiting() + { + _owners &= ~WAITING_UPGRADER; + } + + private uint GetNumReaders() + { + return _owners & READER_MASK; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnterMyLock() + { + if (Interlocked.CompareExchange(ref _myLock, 1, 0) != 0) + EnterMyLockSpin(); + } + + private void EnterMyLockSpin() + { + int pc = Environment.ProcessorCount; + for (int i = 0; ; i++) + { + if (i < LockSpinCount && pc > 1) + { + RuntimeThread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock. + } + else if (i < (LockSpinCount + LockSleep0Count)) + { + RuntimeThread.Sleep(0); // Give up my quantum. + } + else + { + RuntimeThread.Sleep(1); // Give up my quantum. + } + + if (_myLock == 0 && Interlocked.CompareExchange(ref _myLock, 1, 0) == 0) + return; + } + } + + private void ExitMyLock() + { + Debug.Assert(_myLock != 0, "Exiting spin lock that is not held"); + Volatile.Write(ref _myLock, 0); + } + +#if DEBUG + private bool MyLockHeld { get { return _myLock != 0; } } +#endif + + private static void SpinWait(int SpinCount) + { + //Exponential back-off + if ((SpinCount < 5) && (Environment.ProcessorCount > 1)) + { + RuntimeThread.SpinWait(LockSpinCycles * SpinCount); + } + else + { + RuntimeThread.Sleep(0); + } + + // Don't want to Sleep(1) in this spin wait: + // - Don't want to spin for that long, since a proper wait will follow when the spin wait fails. The artifical + // delay introduced by Sleep(1) will in some cases be much longer than desired. + // - Sleep(1) would put the thread into a wait state, and a proper wait will follow when the spin wait fails + // anyway, so it's preferable to put the thread into the proper wait state + } + + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing && !_fDisposed) + { + if (WaitingReadCount > 0 || WaitingUpgradeCount > 0 || WaitingWriteCount > 0) + throw new SynchronizationLockException(SR.SynchronizationLockException_IncorrectDispose); + + if (IsReadLockHeld || IsUpgradeableReadLockHeld || IsWriteLockHeld) + throw new SynchronizationLockException(SR.SynchronizationLockException_IncorrectDispose); + + if (_writeEvent != null) + { + _writeEvent.Dispose(); + _writeEvent = null; + } + + if (_readEvent != null) + { + _readEvent.Dispose(); + _readEvent = null; + } + + if (_upgradeEvent != null) + { + _upgradeEvent.Dispose(); + _upgradeEvent = null; + } + + if (_waitUpgradeEvent != null) + { + _waitUpgradeEvent.Dispose(); + _waitUpgradeEvent = null; + } + + _fDisposed = true; + } + } + + public bool IsReadLockHeld + { + get + { + if (RecursiveReadCount > 0) + return true; + else + return false; + } + } + + public bool IsUpgradeableReadLockHeld + { + get + { + if (RecursiveUpgradeCount > 0) + return true; + else + return false; + } + } + + public bool IsWriteLockHeld + { + get + { + if (RecursiveWriteCount > 0) + return true; + else + return false; + } + } + + public LockRecursionPolicy RecursionPolicy + { + get + { + if (_fIsReentrant) + { + return LockRecursionPolicy.SupportsRecursion; + } + else + { + return LockRecursionPolicy.NoRecursion; + } + } + } + + public int CurrentReadCount + { + get + { + int numreaders = (int)GetNumReaders(); + + if (_upgradeLockOwnerId != -1) + return numreaders - 1; + else + return numreaders; + } + } + + + public int RecursiveReadCount + { + get + { + int count = 0; + ReaderWriterCount lrwc = GetThreadRWCount(true); + if (lrwc != null) + count = lrwc.readercount; + + return count; + } + } + + public int RecursiveUpgradeCount + { + get + { + if (_fIsReentrant) + { + int count = 0; + + ReaderWriterCount lrwc = GetThreadRWCount(true); + if (lrwc != null) + count = lrwc.upgradecount; + + return count; + } + else + { + if (Environment.CurrentManagedThreadId == _upgradeLockOwnerId) + return 1; + else + return 0; + } + } + } + + public int RecursiveWriteCount + { + get + { + if (_fIsReentrant) + { + int count = 0; + + ReaderWriterCount lrwc = GetThreadRWCount(true); + if (lrwc != null) + count = lrwc.writercount; + + return count; + } + else + { + if (Environment.CurrentManagedThreadId == _writeLockOwnerId) + return 1; + else + return 0; + } + } + } + + public int WaitingReadCount + { + get + { + return (int)_numReadWaiters; + } + } + + public int WaitingUpgradeCount + { + get + { + return (int)_numUpgradeWaiters; + } + } + + public int WaitingWriteCount + { + get + { + return (int)_numWriteWaiters; + } + } + } +} diff --git a/src/mscorlib/src/System/Threading/Semaphore.cs b/src/mscorlib/src/System/Threading/Semaphore.cs index 1eac4aaaeb..ae353cc3e3 100644 --- a/src/mscorlib/src/System/Threading/Semaphore.cs +++ b/src/mscorlib/src/System/Threading/Semaphore.cs @@ -20,17 +20,17 @@ namespace System.Threading { if (initialCount < 0) { - throw new ArgumentOutOfRangeException(nameof(initialCount), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(initialCount), SR.ArgumentOutOfRange_NeedNonNegNum); } if (maximumCount < 1) { - throw new ArgumentOutOfRangeException(nameof(maximumCount), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + throw new ArgumentOutOfRangeException(nameof(maximumCount), SR.ArgumentOutOfRange_NeedPosNum); } if (initialCount > maximumCount) { - throw new ArgumentException(Environment.GetResourceString("Argument_SemaphoreInitialMaximum")); + throw new ArgumentException(SR.Argument_SemaphoreInitialMaximum); } SafeWaitHandle myHandle = CreateSemaphone(initialCount, maximumCount, name); @@ -41,7 +41,7 @@ namespace System.Threading if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) throw new WaitHandleCannotBeOpenedException( - Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name)); + SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); __Error.WinIOError(); } @@ -52,17 +52,17 @@ namespace System.Threading { if (initialCount < 0) { - throw new ArgumentOutOfRangeException(nameof(initialCount), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(initialCount), SR.ArgumentOutOfRange_NeedNonNegNum); } if (maximumCount < 1) { - throw new ArgumentOutOfRangeException(nameof(maximumCount), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(maximumCount), SR.ArgumentOutOfRange_NeedNonNegNum); } if (initialCount > maximumCount) { - throw new ArgumentException(Environment.GetResourceString("Argument_SemaphoreInitialMaximum")); + throw new ArgumentException(SR.Argument_SemaphoreInitialMaximum); } SafeWaitHandle myHandle = CreateSemaphone(initialCount, maximumCount, name); @@ -72,7 +72,7 @@ namespace System.Threading { if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) throw new WaitHandleCannotBeOpenedException( - Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name)); + SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); __Error.WinIOError(); } createdNew = errorCode != Win32Native.ERROR_ALREADY_EXISTS; @@ -89,10 +89,10 @@ namespace System.Threading if (name != null) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives")); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); #else if (name.Length > Path.MaxPath) - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); #endif } @@ -111,7 +111,7 @@ namespace System.Threading case OpenExistingResult.NameNotFound: throw new WaitHandleCannotBeOpenedException(); case OpenExistingResult.NameInvalid: - throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name)); + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); case OpenExistingResult.PathNotFound: throw new IOException(Win32Native.GetMessage(Win32Native.ERROR_PATH_NOT_FOUND)); default: @@ -127,14 +127,14 @@ namespace System.Threading private static OpenExistingResult OpenExistingWorker(string name, out Semaphore result) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_NamedSynchronizationPrimitives")); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); #else if (name == null) - throw new ArgumentNullException(nameof(name), Environment.GetResourceString("ArgumentNull_WithParamName")); + throw new ArgumentNullException(nameof(name), SR.ArgumentNull_WithParamName); if (name.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), nameof(name)); + throw new ArgumentException(SR.Argument_EmptyName, nameof(name)); if (name.Length > Path.MaxPath) - throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Path.MaxPath), nameof(name)); + throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Path.MaxPath), nameof(name)); const int SYNCHRONIZE = 0x00100000; const int SEMAPHORE_MODIFY_STATE = 0x00000002; @@ -173,7 +173,7 @@ namespace System.Threading { if (releaseCount < 1) { - throw new ArgumentOutOfRangeException(nameof(releaseCount), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(releaseCount), SR.ArgumentOutOfRange_NeedNonNegNum); } //If ReleaseSempahore returns false when the specified value would cause diff --git a/src/mscorlib/src/System/Threading/SemaphoreFullException.cs b/src/mscorlib/src/System/Threading/SemaphoreFullException.cs deleted file mode 100644 index 01c5040b7e..0000000000 --- a/src/mscorlib/src/System/Threading/SemaphoreFullException.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Threading { - using System; - using System.Runtime.Serialization; - using System.Runtime.InteropServices; - - [Serializable] - [ComVisibleAttribute(false)] - public class SemaphoreFullException : SystemException { - - public SemaphoreFullException() : base(Environment.GetResourceString("Threading_SemaphoreFullException")){ - } - - public SemaphoreFullException(String message) : base(message) { - } - - public SemaphoreFullException(String message, Exception innerException) : base(message, innerException) { - } - - protected SemaphoreFullException(SerializationInfo info, StreamingContext context) : base (info, context) { - } - } -} - diff --git a/src/mscorlib/src/System/Threading/SemaphoreSlim.cs b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs index c3b43d9585..97bbae18cc 100644 --- a/src/mscorlib/src/System/Threading/SemaphoreSlim.cs +++ b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -21,6 +21,7 @@ using System.Diagnostics.Contracts; using System.Threading.Tasks; // The class will be part of the current System.Threading namespace + namespace System.Threading { /// <summary> @@ -84,7 +85,7 @@ namespace System.Threading private sealed class TaskNode : Task<bool>, IThreadPoolWorkItem { internal TaskNode Prev, Next; - internal TaskNode() : base() {} + internal TaskNode() : base() { } void IThreadPoolWorkItem.ExecuteWorkItem() { @@ -177,13 +178,13 @@ namespace System.Threading if (initialCount < 0 || initialCount > maxCount) { throw new ArgumentOutOfRangeException( - nameof(initialCount), initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong")); + nameof(initialCount), initialCount, SR.SemaphoreSlim_ctor_InitialCountWrong); } //validate input if (maxCount <= 0) { - throw new ArgumentOutOfRangeException(nameof(maxCount), maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong")); + throw new ArgumentOutOfRangeException(nameof(maxCount), maxCount, SR.SemaphoreSlim_ctor_MaxCountWrong); } m_maxCount = maxCount; @@ -240,7 +241,7 @@ namespace System.Threading if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( - nameof(timeout), timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } // Call wait with the timeout milliseconds @@ -270,7 +271,7 @@ namespace System.Threading if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( - nameof(timeout), timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } // Call wait with the timeout milliseconds @@ -313,7 +314,7 @@ namespace System.Threading if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } cancellationToken.ThrowIfCancellationRequested(); @@ -370,7 +371,7 @@ namespace System.Threading Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't"); asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken); } - // There are no async waiters, so we can proceed with normal synchronous waiting. + // There are no async waiters, so we can proceed with normal synchronous waiting. else { // If the count > 0 we are good to move on. @@ -399,7 +400,7 @@ namespace System.Threading // defer to synchronous waiters in priority, which means that if it's possible an asynchronous // waiter didn't get released because a synchronous waiter was present, we need to ensure // that synchronous waiter succeeds so that they have a chance to release. - Debug.Assert(!waitSuccessful || m_currentCount > 0, + Debug.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available."); if (m_currentCount > 0) { @@ -574,7 +575,7 @@ namespace System.Threading if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( - nameof(timeout), timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } // Call wait with the timeout milliseconds @@ -607,7 +608,7 @@ namespace System.Threading if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); + nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } // Bail early for cancellation @@ -628,9 +629,9 @@ namespace System.Threading // No counts, if timeout is zero fail fast return s_falseTask; } - // If there aren't, create and return a task to the caller. - // The task will be completed either when they've successfully acquired - // the semaphore or when the timeout expired or cancellation was requested. + // If there aren't, create and return a task to the caller. + // The task will be completed either when they've successfully acquired + // the semaphore or when the timeout expired or cancellation was requested. else { Debug.Assert(m_currentCount == 0, "m_currentCount should never be negative"); @@ -771,7 +772,7 @@ namespace System.Threading if (releaseCount < 1) { throw new ArgumentOutOfRangeException( - nameof(releaseCount), releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong")); + nameof(releaseCount), releaseCount, SR.SemaphoreSlim_Release_CountWrong); } int returnCount; @@ -882,7 +883,7 @@ namespace System.Threading } - + /// <summary> /// Private helper method to wake up waiters when a cancellationToken gets canceled. /// </summary> @@ -905,7 +906,7 @@ namespace System.Threading { if (m_lockObj == null) { - throw new ObjectDisposedException(null, GetResourceString("SemaphoreSlim_Disposed")); + throw new ObjectDisposedException(null, SR.SemaphoreSlim_Disposed); } } @@ -915,7 +916,7 @@ namespace System.Threading /// <param name="str">The key string</param> private static string GetResourceString(string str) { - return Environment.GetResourceString(str); + return SR.GetResourceString(str); } #endregion } diff --git a/src/mscorlib/src/System/Threading/SendOrPostCallback.cs b/src/mscorlib/src/System/Threading/SendOrPostCallback.cs deleted file mode 100644 index b81d2bff64..0000000000 --- a/src/mscorlib/src/System/Threading/SendOrPostCallback.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** Purpose: Represents a method to be called when a message is to be dispatched to a synchronization context. -** -** -===========================================================*/ - -namespace System.Threading -{ - public delegate void SendOrPostCallback(Object state); -} diff --git a/src/mscorlib/src/System/Threading/SpinWait.cs b/src/mscorlib/src/System/Threading/SpinWait.cs index 8431f6564f..30d7aa679c 100644 --- a/src/mscorlib/src/System/Threading/SpinWait.cs +++ b/src/mscorlib/src/System/Threading/SpinWait.cs @@ -51,7 +51,7 @@ namespace System.Threading /// <remarks> /// <para> /// <see cref="SpinWait"/> encapsulates common spinning logic. On single-processor machines, yields are - /// always used instead of busy waits, and on computers with Intel™ processors employing Hyper-Threading™ + /// always used instead of busy waits, and on computers with Intel processors employing Hyper-Threading /// technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of /// spinning and true yielding. /// </para> @@ -71,7 +71,6 @@ namespace System.Threading /// </remarks> public struct SpinWait { - // These constants determine the frequency of yields versus spinning. The // numbers may seem fairly arbitrary, but were derived with at least some // thought in the design document. I fully expect they will need to change @@ -188,7 +187,7 @@ namespace System.Threading public static void SpinUntil(Func<bool> condition) { #if DEBUG - bool result = + bool result = #endif SpinUntil(condition, Timeout.Infinite); #if DEBUG @@ -215,7 +214,7 @@ namespace System.Threading if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( - nameof(timeout), timeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); + nameof(timeout), timeout, SR.SpinWait_SpinUntil_TimeoutWrong); } // Call wait with the timeout milliseconds @@ -237,11 +236,11 @@ namespace System.Threading if (millisecondsTimeout < Timeout.Infinite) { throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); + nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinWait_SpinUntil_TimeoutWrong); } if (condition == null) { - throw new ArgumentNullException(nameof(condition), Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull")); + throw new ArgumentNullException(nameof(condition), SR.SpinWait_SpinUntil_ArgumentNull); } uint startTime = 0; if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite) @@ -267,7 +266,6 @@ namespace System.Threading } } return true; - } #endregion @@ -314,51 +312,4 @@ namespace System.Threading get { return ProcessorCount == 1; } } } - - /// <summary> - /// A helper class to capture a start time using Environment.TickCout as a time in milliseconds, also updates a given timeout bu subtracting the current time from - /// the start time - /// </summary> - internal static class TimeoutHelper - { - /// <summary> - /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days - /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days - /// </summary> - /// <returns></returns> - public static uint GetTime() - { - return (uint)Environment.TickCount; - } - - /// <summary> - /// Helper function to measure and update the elapsed time - /// </summary> - /// <param name="startTime"> The first time (in milliseconds) observed when the wait started</param> - /// <param name="originalWaitMillisecondsTimeout">The orginal wait timeoutout in milliseconds</param> - /// <returns>The new wait time in milliseconds, -1 if the time expired</returns> - public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout) - { - // The function must be called in case the time out is not infinite - Debug.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite); - - uint elapsedMilliseconds = (GetTime() - startTime); - - // Check the elapsed milliseconds is greater than max int because this property is uint - if (elapsedMilliseconds > int.MaxValue) - { - return 0; - } - - // Subtract the elapsed time from the current wait time - int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ; - if (currentWaitTimeout <= 0) - { - return 0; - } - - return currentWaitTimeout; - } - } - } diff --git a/src/mscorlib/src/System/Threading/SynchronizationContext.cs b/src/mscorlib/src/System/Threading/SynchronizationContext.cs index f4b3c79409..676a198ee7 100644 --- a/src/mscorlib/src/System/Threading/SynchronizationContext.cs +++ b/src/mscorlib/src/System/Threading/SynchronizationContext.cs @@ -11,7 +11,7 @@ ===========================================================*/ namespace System.Threading -{ +{ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -27,7 +27,7 @@ namespace System.Threading [Flags] - enum SynchronizationContextProperties + internal enum SynchronizationContextProperties { None = 0, RequireWaitNotification = 0x1 @@ -41,27 +41,27 @@ namespace System.Threading [FriendAccessAllowed] internal class WinRTSynchronizationContextFactoryBase { - public virtual SynchronizationContext Create(object coreDispatcher) {return null;} + public virtual SynchronizationContext Create(object coreDispatcher) { return null; } } #endif //FEATURE_COMINTEROP public class SynchronizationContext { - SynchronizationContextProperties _props = SynchronizationContextProperties.None; - + private SynchronizationContextProperties _props = SynchronizationContextProperties.None; + public SynchronizationContext() { } - + // helper delegate to statically bind to Wait method private delegate int WaitDelegate(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout); - static Type s_cachedPreparedType1; - static Type s_cachedPreparedType2; - static Type s_cachedPreparedType3; - static Type s_cachedPreparedType4; - static Type s_cachedPreparedType5; + private static Type s_cachedPreparedType1; + private static Type s_cachedPreparedType2; + private static Type s_cachedPreparedType3; + private static Type s_cachedPreparedType4; + private static Type s_cachedPreparedType5; // protected so that only the derived sync context class can enable these flags [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "We never dereference s_cachedPreparedType*, so ordering is unimportant")] @@ -87,11 +87,11 @@ namespace System.Threading { RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait)); - if (s_cachedPreparedType1 == null) s_cachedPreparedType1 = type; - else if (s_cachedPreparedType2 == null) s_cachedPreparedType2 = type; - else if (s_cachedPreparedType3 == null) s_cachedPreparedType3 = type; - else if (s_cachedPreparedType4 == null) s_cachedPreparedType4 = type; - else if (s_cachedPreparedType5 == null) s_cachedPreparedType5 = type; + if (s_cachedPreparedType1 == null) s_cachedPreparedType1 = type; + else if (s_cachedPreparedType2 == null) s_cachedPreparedType2 = type; + else if (s_cachedPreparedType3 == null) s_cachedPreparedType3 = type; + else if (s_cachedPreparedType4 == null) s_cachedPreparedType4 = type; + else if (s_cachedPreparedType5 == null) s_cachedPreparedType5 = type; } _props |= SynchronizationContextProperties.RequireWaitNotification; @@ -99,10 +99,10 @@ namespace System.Threading public bool IsWaitNotificationRequired() { - return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0); + return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0); } - + public virtual void Send(SendOrPostCallback d, Object state) { d(state); @@ -113,7 +113,7 @@ namespace System.Threading ThreadPool.QueueUserWorkItem(new WaitCallback(d), state); } - + /// <summary> /// Optional override for subclasses, for responding to notification that operation is starting. /// </summary> @@ -159,9 +159,9 @@ namespace System.Threading Thread.CurrentThread.SynchronizationContext = syncContext; } - public static SynchronizationContext Current + public static SynchronizationContext Current { - get + get { SynchronizationContext context = Thread.CurrentThread.SynchronizationContext; @@ -189,7 +189,7 @@ namespace System.Threading { Debug.Assert(Environment.IsWinRTSupported); Debug.Assert(AppDomain.IsAppXModel()); - + // // We call into the VM to get the dispatcher. This is because: // @@ -207,7 +207,7 @@ namespace System.Threading return null; } - static WinRTSynchronizationContextFactoryBase s_winRTContextFactory; + private static WinRTSynchronizationContextFactoryBase s_winRTContextFactory; private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory() { diff --git a/src/mscorlib/src/System/Threading/SynchronizationLockException.cs b/src/mscorlib/src/System/Threading/SynchronizationLockException.cs deleted file mode 100644 index de42c1f232..0000000000 --- a/src/mscorlib/src/System/Threading/SynchronizationLockException.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: Wait(), Notify() or NotifyAll() was called from an unsynchronized -** block of code. -** -** -=============================================================================*/ - -namespace System.Threading { - - using System; - using System.Runtime.Serialization; - [Serializable] - public class SynchronizationLockException : SystemException { - public SynchronizationLockException() - : base(Environment.GetResourceString("Arg_SynchronizationLockException")) { - SetErrorCode(__HResults.COR_E_SYNCHRONIZATIONLOCK); - } - - public SynchronizationLockException(String message) - : base(message) { - SetErrorCode(__HResults.COR_E_SYNCHRONIZATIONLOCK); - } - - public SynchronizationLockException(String message, Exception innerException) - : base(message, innerException) { - SetErrorCode(__HResults.COR_E_SYNCHRONIZATIONLOCK); - } - - protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base (info, context) { - } - } - -} - - diff --git a/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs b/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs index ec7c5aaeea..ec154f9efb 100644 --- a/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs +++ b/src/mscorlib/src/System/Threading/Tasks/AsyncCausalityTracer.cs @@ -21,7 +21,6 @@ using WFD = Windows.Foundation.Diagnostics; namespace System.Threading.Tasks { - [FriendAccessAllowed] internal enum CausalityTraceLevel { @@ -85,13 +84,13 @@ namespace System.Threading.Tasks [FriendAccessAllowed] internal static class AsyncCausalityTracer { - static internal void EnableToETW(bool enabled) + static internal void EnableToETW(bool enabled) { #if FEATURE_COMINTEROP if (enabled) - f_LoggingOn |= Loggers.ETW; - else - f_LoggingOn &= ~Loggers.ETW; + f_LoggingOn |= Loggers.ETW; + else + f_LoggingOn &= ~Loggers.ETW; #endif } @@ -121,7 +120,8 @@ namespace System.Threading.Tasks // The loggers that this Tracer knows about. [Flags] - private enum Loggers : byte { + private enum Loggers : byte + { CausalityTracer = 1, ETW = 2 } @@ -148,7 +148,7 @@ namespace System.Threading.Tasks int hresult = Microsoft.Win32.UnsafeNativeMethods.RoGetActivationFactory(ClassId, ref guid, out factory); if (hresult < 0 || factory == null) return; //This prevents having an exception thrown in case IAsyncCausalityTracerStatics isn't registered. - + s_TracerFactory = (WFD.IAsyncCausalityTracerStatics)factory; EventRegistrationToken token = s_TracerFactory.add_TracingStatusChanged(new EventHandler<WFD.TracingStatusChangedEventArgs>(TracingStatusChangedHandler)); @@ -161,15 +161,14 @@ namespace System.Threading.Tasks // doing here depends on internal state. LogAndDisable(ex); } - } private static void TracingStatusChangedHandler(Object sender, WFD.TracingStatusChangedEventArgs args) { if (args.Enabled) - f_LoggingOn |= Loggers.CausalityTracer; - else - f_LoggingOn &= ~Loggers.CausalityTracer; + f_LoggingOn |= Loggers.CausalityTracer; + else + f_LoggingOn &= ~Loggers.CausalityTracer; } #endif @@ -185,11 +184,11 @@ namespace System.Threading.Tasks try { if ((f_LoggingOn & Loggers.ETW) != 0) - TplEtwProvider.Log.TraceOperationBegin(taskId, operationName, (long) relatedContext); + TplEtwProvider.Log.TraceOperationBegin(taskId, operationName, (long)relatedContext); if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceOperationCreation((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), operationName, relatedContext); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -209,7 +208,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceOperationCompletion((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.AsyncCausalityStatus)status); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -228,7 +227,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceOperationRelation((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.CausalityRelation)relation); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -247,7 +246,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceSynchronousWorkStart((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, s_PlatformId, GetOperationId((uint)taskId), (WFD.CausalitySynchronousWork)work); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -266,7 +265,7 @@ namespace System.Threading.Tasks if ((f_LoggingOn & Loggers.CausalityTracer) != 0) s_TracerFactory.TraceSynchronousWorkCompletion((WFD.CausalityTraceLevel)traceLevel, s_CausalitySource, (WFD.CausalitySynchronousWork)work); } - catch(Exception ex) + catch (Exception ex) { //view function comment LogAndDisable(ex); @@ -288,6 +287,5 @@ namespace System.Threading.Tasks { return (((ulong)AppDomain.CurrentDomain.Id) << 32) + taskId; } - } } diff --git a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs index a87406a493..07a673bf4e 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs @@ -72,22 +72,25 @@ namespace System.Threading.Tasks /// Initializes the ConcurrentExclusiveSchedulerPair. /// </summary> public ConcurrentExclusiveSchedulerPair() : - this(TaskScheduler.Default, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { } + this(TaskScheduler.Default, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) + { } /// <summary> /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler. /// </summary> /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param> public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler) : - this(taskScheduler, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { } + this(taskScheduler, DefaultMaxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) + { } /// <summary> /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler with a maximum concurrency level. /// </summary> /// <param name="taskScheduler">The target scheduler on which this pair should execute.</param> /// <param name="maxConcurrencyLevel">The maximum number of tasks to run concurrently.</param> - public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler, int maxConcurrencyLevel) : - this(taskScheduler, maxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) { } + public ConcurrentExclusiveSchedulerPair(TaskScheduler taskScheduler, int maxConcurrencyLevel) : + this(taskScheduler, maxConcurrencyLevel, DEFAULT_MAXITEMSPERTASK) + { } /// <summary> /// Initializes the ConcurrentExclusiveSchedulerPair to target the specified scheduler with a maximum @@ -141,7 +144,7 @@ namespace System.Threading.Tasks } /// <summary>Gets a <see cref="System.Threading.Tasks.Task"/> that will complete when the scheduler has completed processing.</summary> - public Task Completion + public Task Completion { // ValueLock not needed, but it's ok if it's held get { return EnsureCompletionStateInitialized().Task; } @@ -162,7 +165,7 @@ namespace System.Threading.Tasks } /// <summary>Sets that completion has been requested.</summary> - private void RequestCompletion() + private void RequestCompletion() { ContractAssertMonitorStatus(ValueLock, held: true); EnsureCompletionStateInitialized().m_completionRequested = true; @@ -190,9 +193,9 @@ namespace System.Threading.Tasks // Now, only allow shutdown if an exception occurred or if there are no more tasks to process. var cs = EnsureCompletionStateInitialized(); - return + return (cs.m_exceptions != null && cs.m_exceptions.Count > 0) || - (m_concurrentTaskScheduler.m_tasks.IsEmpty && m_exclusiveTaskScheduler.m_tasks.IsEmpty); + (m_concurrentTaskScheduler.m_tasks.IsEmpty && m_exclusiveTaskScheduler.m_tasks.IsEmpty); } } @@ -330,7 +333,7 @@ namespace System.Threading.Tasks } } } - + // Check to see if all tasks have completed and if completion has been requested. CleanupStateIfCompletingAndQuiesced(); } @@ -370,7 +373,7 @@ namespace System.Threading.Tasks // We're no longer processing exclusive tasks on the current thread ProcessingMode currentMode; m_threadProcessingMapping.TryRemove(Thread.CurrentThread.ManagedThreadId, out currentMode); - Debug.Assert(currentMode == ProcessingMode.ProcessingExclusiveTask, + Debug.Assert(currentMode == ProcessingMode.ProcessingExclusiveTask, "Somehow we ended up escaping exclusive mode."); lock (ValueLock) @@ -720,7 +723,7 @@ namespace System.Threading.Tasks return mode; } } - + /// <summary>Asserts that a given synchronization object is either held or not held.</summary> /// <param name="syncObj">The monitor to check.</param> /// <param name="held">Whether we want to assert that it's currently held or not held.</param> @@ -748,7 +751,7 @@ namespace System.Threading.Tasks Debug.Assert(Monitor.IsEntered(syncObj) == held, "The locking scheme was not correctly followed."); #endif } - + /// <summary>Gets the options to use for tasks.</summary> /// <param name="isReplacementReplica">If this task is being created to replace another.</param> /// <remarks> @@ -758,7 +761,7 @@ namespace System.Threading.Tasks /// <returns>The options to use.</returns> internal static TaskCreationOptions GetCreationOptionsForTask(bool isReplacementReplica = false) { - TaskCreationOptions options = + TaskCreationOptions options = #if PRENET45 TaskCreationOptions.None; #else @@ -784,5 +787,4 @@ namespace System.Threading.Tasks Completed = 0x8 } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs index 137afa11f5..60a7c81dcf 100644 --- a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs +++ b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs @@ -568,8 +568,6 @@ namespace System.Threading.Tasks promise.DangerousSetResult(result); } } - - } } @@ -691,7 +689,7 @@ namespace System.Threading.Tasks // RespectParentCancellation. Task t = new Task(new Action<object>(delegate { - FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization:true); + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: true); }), (object)null, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null); @@ -706,7 +704,7 @@ namespace System.Threading.Tasks if (asyncResult.IsCompleted) { - try { t.InternalRunSynchronously(scheduler, waitForCompletion:false); } + try { t.InternalRunSynchronously(scheduler, waitForCompletion: false); } catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions } else @@ -792,7 +790,7 @@ namespace System.Threading.Tasks ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); - + TaskFactory.CheckFromAsyncOptions(creationOptions, true); Task<TResult> promise = new Task<TResult>(state, creationOptions); @@ -943,7 +941,6 @@ namespace System.Threading.Tasks } catch { - if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error); @@ -1241,7 +1238,7 @@ namespace System.Threading.Tasks internal static Task<TResult> FromAsyncTrim<TInstance, TArgs>( TInstance thisRef, TArgs args, Func<TInstance, TArgs, AsyncCallback, object, IAsyncResult> beginMethod, - Func<TInstance, IAsyncResult, TResult> endMethod) + Func<TInstance, IAsyncResult, TResult> endMethod) where TInstance : class { // Validate arguments, but only with asserts, as this is an internal only implementation. @@ -1319,7 +1316,7 @@ namespace System.Threading.Tasks // we'll instead complete the promise at the call site. if (!asyncResult.CompletedSynchronously) { - promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization:true); + promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization: true); } } @@ -1661,7 +1658,7 @@ namespace System.Threading.Tasks return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } - + // Core implementation of ContinueWhenAll -- the generic version // Note: if you make any changes to this method, please do the same to the non-generic version too. internal static Task<TResult> ContinueWhenAllImpl<TAntecedentResult>(Task<TAntecedentResult>[] tasks, @@ -1744,10 +1741,10 @@ namespace System.Threading.Tasks //the following delegate avoids closure capture as much as possible //completedTasks.Result == tasksCopy; //state == continuationFunction - (completedTasks, state) => + (completedTasks, state) => { completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); - return ((Func<Task[], TResult>)state)(completedTasks.Result); + return ((Func<Task[], TResult>)state)(completedTasks.Result); }, continuationFunction, scheduler, cancellationToken, continuationOptions); } @@ -1755,13 +1752,13 @@ namespace System.Threading.Tasks { Debug.Assert(continuationAction != null); return starter.ContinueWith<TResult>( - //the following delegate avoids closure capture as much as possible - //completedTasks.Result == tasksCopy; - //state == continuationAction - (completedTasks, state) => + //the following delegate avoids closure capture as much as possible + //completedTasks.Result == tasksCopy; + //state == continuationAction + (completedTasks, state) => { completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); - ((Action<Task[]>)state)(completedTasks.Result); return default(TResult); + ((Action<Task[]>)state)(completedTasks.Result); return default(TResult); }, continuationAction, scheduler, cancellationToken, continuationOptions); } @@ -2054,7 +2051,7 @@ namespace System.Threading.Tasks // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); - if(tasks.Length == 0) ThrowHelper.ThrowArgumentException( ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); + if (tasks.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); //ArgumentNullException of continuationFunction or continuationAction is checked by the caller Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null"); @@ -2076,8 +2073,8 @@ namespace System.Threading.Tasks if (continuationFunction != null) { return starter.ContinueWith( - //the following delegate avoids closure capture as much as possible - //completedTask.Result is the winning task; state == continuationAction + //the following delegate avoids closure capture as much as possible + //completedTask.Result is the winning task; state == continuationAction (completedTask, state) => { return ((Func<Task, TResult>)state)(completedTask.Result); }, continuationFunction, scheduler, cancellationToken, continuationOptions); } @@ -2132,7 +2129,7 @@ namespace System.Threading.Tasks Debug.Assert(continuationAction != null); return starter.ContinueWith<TResult>( // Use a cached delegate - GenericDelegateCache<TAntecedentResult,TResult>.CWAnyActionDelegate, + GenericDelegateCache<TAntecedentResult, TResult>.CWAnyActionDelegate, continuationAction, scheduler, cancellationToken, continuationOptions); } } @@ -2180,7 +2177,5 @@ namespace System.Threading.Tasks action(wrappedAntecedents.Result); return default(TResult); }; - } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs index 32efd771a0..17dd1f8bde 100644 --- a/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs +++ b/src/mscorlib/src/System/Threading/Tasks/IAsyncCausalityTracerStatics.cs @@ -13,6 +13,7 @@ using System.Runtime.InteropServices.WindowsRuntime; // Windows.Foundation.Diagnostics cannot be referenced from managed code because // they're hidden by the metadata adapter. We redeclare the interfaces manually // to be able to talk to native WinRT objects. + namespace Windows.Foundation.Diagnostics { [ComImport] @@ -47,16 +48,16 @@ namespace Windows.Foundation.Diagnostics [WindowsRuntimeImport] internal sealed class TracingStatusChangedEventArgs : ITracingStatusChangedEventArgs { - public extern bool Enabled + public extern bool Enabled { [MethodImpl(MethodImplOptions.InternalCall)] get; } - - public extern CausalityTraceLevel TraceLevel + + public extern CausalityTraceLevel TraceLevel { [MethodImpl(MethodImplOptions.InternalCall)] - get; + get; } } @@ -97,5 +98,4 @@ namespace Windows.Foundation.Diagnostics Error = 3, Started = 0 } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs index 545bf9a5e5..f9d5f89398 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs @@ -182,7 +182,8 @@ namespace System.Threading.Tasks newSegment.m_state.m_last = 1; newSegment.m_state.m_lastCopy = 1; - try { } finally + try { } + finally { // Finally block to protect against corruption due to a thread abort // between setting m_next and setting m_tail. @@ -271,8 +272,8 @@ namespace System.Threading.Tasks { for (Segment segment = m_head; segment != null; segment = segment.m_next) { - for (int pt = segment.m_state.m_first; - pt != segment.m_state.m_last; + for (int pt = segment.m_state.m_first; + pt != segment.m_state.m_last; pt = (pt + 1) & (segment.m_array.Length - 1)) { yield return segment.m_array[pt]; @@ -307,7 +308,7 @@ namespace System.Threading.Tasks } /// <summary>A segment in the queue containing one or more items.</summary> - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential)] private sealed class Segment { /// <summary>The next segment in the linked list of segments.</summary> @@ -367,7 +368,7 @@ namespace System.Threading.Tasks } /// <summary>A placeholder class for common padding constants and eventually routines.</summary> - static class PaddingHelpers + internal static class PaddingHelpers { /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary> internal const int CACHE_LINE_SIZE = 128; @@ -375,8 +376,7 @@ namespace System.Threading.Tasks /// <summary>Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}.</summary> [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(Int32))] // Based on common case of 64-byte cache lines - struct PaddingFor32 + internal struct PaddingFor32 { } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs index 12cc1daa63..33bf792370 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs @@ -16,21 +16,21 @@ using System.Text; using System.Security; using System.Runtime.CompilerServices; +using System.Diagnostics.Tracing; + namespace System.Threading.Tasks { - using System.Diagnostics.Tracing; - /// <summary>Provides an event source for tracing TPL information.</summary> [EventSource( Name = "System.Threading.Tasks.TplEventSource", - Guid = "2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5", - LocalizationResources = System.CoreLib.Name)] + Guid = "2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5", + LocalizationResources = "FxResources.System.Private.CoreLib.SR")] internal sealed class TplEtwProvider : EventSource { /// Used to determine if tasks should generate Activity IDs for themselves internal bool TasksSetActivityIds; // This keyword is set internal bool Debug; - private bool DebugActivityId; + private bool DebugActivityId; /// <summary> /// Get callbacks when the ETW sends us commands` @@ -42,11 +42,11 @@ namespace System.Threading.Tasks AsyncCausalityTracer.EnableToETW(true); else if (command.Command == EventCommand.Disable) AsyncCausalityTracer.EnableToETW(false); - - if (IsEnabled(EventLevel.Informational, Keywords.TasksFlowActivityIds)) + + if (IsEnabled(EventLevel.Informational, Keywords.TasksFlowActivityIds)) ActivityTracker.Instance.Enable(); - else - TasksSetActivityIds = IsEnabled(EventLevel.Informational, Keywords.TasksSetActivityIds); + else + TasksSetActivityIds = IsEnabled(EventLevel.Informational, Keywords.TasksSetActivityIds); Debug = IsEnabled(EventLevel.Informational, Keywords.Debug); DebugActivityId = IsEnabled(EventLevel.Informational, Keywords.DebugActivityId); @@ -99,49 +99,49 @@ namespace System.Threading.Tasks /// This sets activity IDS and logs when tasks are schedules (or waits begin) /// But are otherwise silent /// </summary> - public const EventKeywords TaskTransfer = (EventKeywords) 1; + public const EventKeywords TaskTransfer = (EventKeywords)1; /// <summary> /// TaskTranser events plus events when tasks start and stop /// </summary> - public const EventKeywords Tasks = (EventKeywords) 2; + public const EventKeywords Tasks = (EventKeywords)2; /// <summary> /// Events associted with the higher level parallel APIs /// </summary> - public const EventKeywords Parallel = (EventKeywords) 4; + public const EventKeywords Parallel = (EventKeywords)4; /// <summary> /// These are relatively verbose events that effectively just redirect /// the windows AsyncCausalityTracer to ETW /// </summary> - public const EventKeywords AsyncCausalityOperation = (EventKeywords) 8; - public const EventKeywords AsyncCausalityRelation = (EventKeywords) 0x10; - public const EventKeywords AsyncCausalitySynchronousWork = (EventKeywords) 0x20; + public const EventKeywords AsyncCausalityOperation = (EventKeywords)8; + public const EventKeywords AsyncCausalityRelation = (EventKeywords)0x10; + public const EventKeywords AsyncCausalitySynchronousWork = (EventKeywords)0x20; /// <summary> /// Emit the stops as well as the schedule/start events /// </summary> - public const EventKeywords TaskStops = (EventKeywords) 0x40; + public const EventKeywords TaskStops = (EventKeywords)0x40; /// <summary> /// TasksFlowActivityIds indicate that activity ID flow from one task /// to any task created by it. /// </summary> - public const EventKeywords TasksFlowActivityIds = (EventKeywords) 0x80; + public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80; /// <summary> /// TasksSetActivityIds will cause the task operations to set Activity Ids /// This option is incompatible with TasksFlowActivityIds flow is ignored /// if that keyword is set. This option is likley to be removed in the future /// </summary> - public const EventKeywords TasksSetActivityIds = (EventKeywords) 0x10000; + public const EventKeywords TasksSetActivityIds = (EventKeywords)0x10000; /// <summary> /// Relatively Verbose logging meant for debugging the Task library itself. Will probably be removed in the future /// </summary> - public const EventKeywords Debug = (EventKeywords) 0x20000; + public const EventKeywords Debug = (EventKeywords)0x20000; /// <summary> /// Relatively Verbose logging meant for debugging the Task library itself. Will probably be removed in the future /// </summary> - public const EventKeywords DebugActivityId = (EventKeywords) 0x40000; + public const EventKeywords DebugActivityId = (EventKeywords)0x40000; } /// <summary>Enabled for all keywords.</summary> @@ -182,18 +182,18 @@ namespace System.Threading.Tasks /// <summary>A continuation of a taskWaitEnd is complete </summary> private const int TASKWAITCONTINUATIONSTARTED_ID = 19; - private const int TRACEOPERATIONSTART_ID = 14; - private const int TRACEOPERATIONSTOP_ID = 15; - private const int TRACEOPERATIONRELATION_ID = 16; + private const int TRACEOPERATIONSTART_ID = 14; + private const int TRACEOPERATIONSTOP_ID = 15; + private const int TRACEOPERATIONRELATION_ID = 16; private const int TRACESYNCHRONOUSWORKSTART_ID = 17; - private const int TRACESYNCHRONOUSWORKSTOP_ID = 18; + private const int TRACESYNCHRONOUSWORKSTOP_ID = 18; + - //----------------------------------------------------------------------------------- // // Task Events // - + // These are all verbose events, so we need to call IsEnabled(EventLevel.Verbose, ALL_KEYWORDS) // call. However since the IsEnabled(l,k) call is more expensive than IsEnabled(), we only want // to incur this cost when instrumentation is enabled. So the Task codepaths that call these @@ -208,36 +208,36 @@ namespace System.Threading.Tasks /// <param name="TaskID">The task ID.</param> /// <param name="CreatingTaskID">The task ID</param> /// <param name="TaskCreationOptions">The options used to create the task.</param> - [Event(TASKSCHEDULED_ID, Task = Tasks.TaskScheduled, Version=1, Opcode = EventOpcode.Send, - Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)] + [Event(TASKSCHEDULED_ID, Task = Tasks.TaskScheduled, Version = 1, Opcode = EventOpcode.Send, + Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer | Keywords.Tasks)] public void TaskScheduled( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID, int CreatingTaskID, int TaskCreationOptions, int appDomain) { // IsEnabled() call is an inlined quick check that makes this very fast when provider is off - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer | Keywords.Tasks)) { unsafe { EventData* eventPayload = stackalloc EventData[6]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID)); + eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID)); eventPayload[1].Size = sizeof(int); - eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID)); + eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID)); eventPayload[2].Size = sizeof(int); - eventPayload[2].DataPointer = ((IntPtr) (&TaskID)); + eventPayload[2].DataPointer = ((IntPtr)(&TaskID)); eventPayload[3].Size = sizeof(int); - eventPayload[3].DataPointer = ((IntPtr) (&CreatingTaskID)); + eventPayload[3].DataPointer = ((IntPtr)(&CreatingTaskID)); eventPayload[4].Size = sizeof(int); - eventPayload[4].DataPointer = ((IntPtr) (&TaskCreationOptions)); + eventPayload[4].DataPointer = ((IntPtr)(&TaskCreationOptions)); eventPayload[5].Size = sizeof(int); - eventPayload[5].DataPointer = ((IntPtr) (&appDomain)); + eventPayload[5].DataPointer = ((IntPtr)(&appDomain)); if (TasksSetActivityIds) { Guid childActivityId = CreateGuidForTaskID(TaskID); WriteEventWithRelatedActivityIdCore(TASKSCHEDULED_ID, &childActivityId, 6, eventPayload); } - else + else WriteEventCore(TASKSCHEDULED_ID, 6, eventPayload); } } @@ -251,13 +251,13 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param> /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The task ID.</param> - [Event(TASKSTARTED_ID, + [Event(TASKSTARTED_ID, Level = EventLevel.Informational, Keywords = Keywords.Tasks)] public void TaskStarted( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID) { - if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) + if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) WriteEvent(TASKSTARTED_ID, OriginatingTaskSchedulerID, OriginatingTaskID, TaskID); } #endregion @@ -270,33 +270,33 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The task ID.</param> /// <param name="IsExceptional">Whether the task completed due to an error.</param> - [Event(TASKCOMPLETED_ID, Version=1, + [Event(TASKCOMPLETED_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.TaskStops)] public void TaskCompleted( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID, bool IsExceptional) { - if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) + if (IsEnabled(EventLevel.Informational, Keywords.Tasks)) { unsafe { EventData* eventPayload = stackalloc EventData[4]; Int32 isExceptionalInt = IsExceptional ? 1 : 0; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID)); + eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID)); eventPayload[1].Size = sizeof(int); - eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID)); + eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID)); eventPayload[2].Size = sizeof(int); - eventPayload[2].DataPointer = ((IntPtr) (&TaskID)); + eventPayload[2].DataPointer = ((IntPtr)(&TaskID)); eventPayload[3].Size = sizeof(int); - eventPayload[3].DataPointer = ((IntPtr) (&isExceptionalInt)); + eventPayload[3].DataPointer = ((IntPtr)(&isExceptionalInt)); WriteEventCore(TASKCOMPLETED_ID, 4, eventPayload); } - } + } } #endregion - #region TaskWaitBegin + #region TaskWaitBegin /// <summary> /// Fired when starting to wait for a taks's completion explicitly or implicitly. /// </summary> @@ -307,13 +307,13 @@ namespace System.Threading.Tasks /// <param name="ContinueWithTaskID">If known, if 'TaskID' has a 'continueWith' task, mention give its ID here. /// 0 means unknown. This allows better visualization of the common sequential chaining case.</param> /// </summary> - [Event(TASKWAITBEGIN_ID, Version=3, Task = TplEtwProvider.Tasks.TaskWait, Opcode = EventOpcode.Send, - Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)] + [Event(TASKWAITBEGIN_ID, Version = 3, Task = TplEtwProvider.Tasks.TaskWait, Opcode = EventOpcode.Send, + Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer | Keywords.Tasks)] public void TaskWaitBegin( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int TaskID, TaskWaitBehavior Behavior, int ContinueWithTaskID) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer | Keywords.Tasks)) { unsafe { @@ -348,7 +348,7 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param> /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The task ID.</param> - [Event(TASKWAITEND_ID, + [Event(TASKWAITEND_ID, Level = EventLevel.Verbose, Keywords = Keywords.Tasks)] public void TaskWaitEnd( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER @@ -395,23 +395,23 @@ namespace System.Threading.Tasks /// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param> /// <param name="OriginatingTaskID">The task ID.</param> /// <param name="TaskID">The activityId for the continuation.</param> - [Event(AWAITTASKCONTINUATIONSCHEDULED_ID, Task = Tasks.AwaitTaskContinuationScheduled, Opcode = EventOpcode.Send, - Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer|Keywords.Tasks)] + [Event(AWAITTASKCONTINUATIONSCHEDULED_ID, Task = Tasks.AwaitTaskContinuationScheduled, Opcode = EventOpcode.Send, + Level = EventLevel.Informational, Keywords = Keywords.TaskTransfer | Keywords.Tasks)] public void AwaitTaskContinuationScheduled( int OriginatingTaskSchedulerID, int OriginatingTaskID, // PFX_COMMON_EVENT_HEADER int ContinuwWithTaskId) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer|Keywords.Tasks)) - { + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.TaskTransfer | Keywords.Tasks)) + { unsafe { EventData* eventPayload = stackalloc EventData[3]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&OriginatingTaskSchedulerID)); + eventPayload[0].DataPointer = ((IntPtr)(&OriginatingTaskSchedulerID)); eventPayload[1].Size = sizeof(int); - eventPayload[1].DataPointer = ((IntPtr) (&OriginatingTaskID)); + eventPayload[1].DataPointer = ((IntPtr)(&OriginatingTaskID)); eventPayload[2].Size = sizeof(int); - eventPayload[2].DataPointer = ((IntPtr) (&ContinuwWithTaskId)); + eventPayload[2].DataPointer = ((IntPtr)(&ContinuwWithTaskId)); if (TasksSetActivityIds) { Guid continuationActivityId = CreateGuidForTaskID(ContinuwWithTaskId); @@ -423,116 +423,116 @@ namespace System.Threading.Tasks } } - [Event(TRACEOPERATIONSTART_ID, Version=1, + [Event(TRACEOPERATIONSTART_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityOperation)] public void TraceOperationBegin(int TaskID, string OperationName, long RelatedContext) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) { unsafe { - fixed(char* operationNamePtr = OperationName) + fixed (char* operationNamePtr = OperationName) { EventData* eventPayload = stackalloc EventData[3]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&TaskID)); + eventPayload[0].DataPointer = ((IntPtr)(&TaskID)); eventPayload[1].Size = ((OperationName.Length + 1) * 2); - eventPayload[1].DataPointer = ((IntPtr) operationNamePtr); + eventPayload[1].DataPointer = ((IntPtr)operationNamePtr); eventPayload[2].Size = sizeof(long); - eventPayload[2].DataPointer = ((IntPtr) (&RelatedContext)); + eventPayload[2].DataPointer = ((IntPtr)(&RelatedContext)); WriteEventCore(TRACEOPERATIONSTART_ID, 3, eventPayload); } } - } + } } - [Event(TRACEOPERATIONRELATION_ID, Version=1, + [Event(TRACEOPERATIONRELATION_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityRelation)] public void TraceOperationRelation(int TaskID, CausalityRelation Relation) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityRelation)) - WriteEvent(TRACEOPERATIONRELATION_ID, TaskID,(int) Relation); // optmized overload for this exists + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityRelation)) + WriteEvent(TRACEOPERATIONRELATION_ID, TaskID, (int)Relation); // optmized overload for this exists } - [Event(TRACEOPERATIONSTOP_ID, Version=1, + [Event(TRACEOPERATIONSTOP_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalityOperation)] public void TraceOperationEnd(int TaskID, AsyncCausalityStatus Status) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) - WriteEvent(TRACEOPERATIONSTOP_ID, TaskID,(int) Status); // optmized overload for this exists + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalityOperation)) + WriteEvent(TRACEOPERATIONSTOP_ID, TaskID, (int)Status); // optmized overload for this exists } - [Event(TRACESYNCHRONOUSWORKSTART_ID, Version=1, + [Event(TRACESYNCHRONOUSWORKSTART_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalitySynchronousWork)] public void TraceSynchronousWorkBegin(int TaskID, CausalitySynchronousWork Work) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) - WriteEvent(TRACESYNCHRONOUSWORKSTART_ID, TaskID,(int) Work); // optmized overload for this exists + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) + WriteEvent(TRACESYNCHRONOUSWORKSTART_ID, TaskID, (int)Work); // optmized overload for this exists } - [Event(TRACESYNCHRONOUSWORKSTOP_ID, Version=1, + [Event(TRACESYNCHRONOUSWORKSTOP_ID, Version = 1, Level = EventLevel.Informational, Keywords = Keywords.AsyncCausalitySynchronousWork)] public void TraceSynchronousWorkEnd(CausalitySynchronousWork Work) { - if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) + if (IsEnabled() && IsEnabled(EventLevel.Informational, Keywords.AsyncCausalitySynchronousWork)) { unsafe { EventData* eventPayload = stackalloc EventData[1]; eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr) (&Work)); + eventPayload[0].DataPointer = ((IntPtr)(&Work)); WriteEventCore(TRACESYNCHRONOUSWORKSTOP_ID, 1, eventPayload); } - } + } } [NonEvent] - unsafe public void RunningContinuation(int TaskID, object Object) { RunningContinuation(TaskID, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); } + unsafe public void RunningContinuation(int TaskID, object Object) { RunningContinuation(TaskID, (long)*((void**)JitHelpers.UnsafeCastToStackPointer(ref Object))); } [Event(20, Keywords = Keywords.Debug)] - private void RunningContinuation(int TaskID, long Object) - { + private void RunningContinuation(int TaskID, long Object) + { if (Debug) - WriteEvent(20, TaskID, Object); + WriteEvent(20, TaskID, Object); } [NonEvent] - unsafe public void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long) *((void**) JitHelpers.UnsafeCastToStackPointer(ref Object))); } + unsafe public void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long)*((void**)JitHelpers.UnsafeCastToStackPointer(ref Object))); } [Event(21, Keywords = Keywords.Debug)] - public void RunningContinuationList(int TaskID, int Index, long Object) - { + public void RunningContinuationList(int TaskID, int Index, long Object) + { if (Debug) - WriteEvent(21, TaskID, Index, Object); - } + WriteEvent(21, TaskID, Index, Object); + } [Event(23, Keywords = Keywords.Debug)] - public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(23, Facility, Message); } + public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(23, Facility, Message); } [Event(24, Keywords = Keywords.Debug)] - public void DebugFacilityMessage1(string Facility, string Message, string Value1) { WriteEvent(24, Facility, Message, Value1); } + public void DebugFacilityMessage1(string Facility, string Message, string Value1) { WriteEvent(24, Facility, Message, Value1); } [Event(25, Keywords = Keywords.DebugActivityId)] public void SetActivityId(Guid NewId) { if (DebugActivityId) - WriteEvent(25, NewId); - } + WriteEvent(25, NewId); + } [Event(26, Keywords = Keywords.Debug)] - public void NewID(int TaskID) - { + public void NewID(int TaskID) + { if (Debug) - WriteEvent(26, TaskID); - } + WriteEvent(26, TaskID); + } /// <summary> /// Activity IDs are GUIDS but task IDS are integers (and are not unique across appdomains /// This routine creates a process wide unique GUID given a task ID /// </summary> - internal static Guid CreateGuidForTaskID(int taskID) + internal static Guid CreateGuidForTaskID(int taskID) { // The thread pool generated a process wide unique GUID from a task GUID by // using the taskGuid, the appdomain ID, and 8 bytes of 'randomization' chosen by @@ -540,9 +540,9 @@ namespace System.Threading.Tasks // These were generated by CreateGuid, and are reasonably random (and thus unlikley to collide uint pid = EventSource.s_currentPid; int appDomainID = System.Threading.Thread.GetDomainID(); - return new Guid(taskID, - (short) appDomainID , (short) (appDomainID >> 16), - (byte)pid, (byte)(pid >> 8), (byte)(pid >> 16), (byte)(pid >> 24), + return new Guid(taskID, + (short)appDomainID, (short)(appDomainID >> 16), + (byte)pid, (byte)(pid >> 8), (byte)(pid >> 16), (byte)(pid >> 24), 0xff, 0xdc, 0xd7, 0xb5); } } diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs index 7013c5c5e0..8e2e6a4cb0 100644 --- a/src/mscorlib/src/System/Threading/Tasks/Task.cs +++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs @@ -29,7 +29,6 @@ using System.Diagnostics.Tracing; namespace System.Threading.Tasks { - /// <summary> /// Utility class for allocating structs as heap variables /// </summary> @@ -41,7 +40,6 @@ namespace System.Threading.Tasks { this.Value = value; } - } /// <summary> @@ -271,7 +269,7 @@ namespace System.Threading.Tasks // but haven't yet been waited on by the parent, lazily initialized. internal volatile List<Task> m_exceptionalChildren; // A task's parent, or null if parent-less. Only set during Task construction. - internal Task m_parent; + internal Task m_parent; /// <summary> /// Sets the internal completion event. @@ -569,8 +567,8 @@ namespace System.Threading.Tasks #if DEBUG // Check the validity of internalOptions - int illegalInternalOptions = - (int) (internalOptions & + int illegalInternalOptions = + (int)(internalOptions & ~(InternalTaskOptions.PromiseTask | InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation | @@ -581,27 +579,26 @@ namespace System.Threading.Tasks // Assign options to m_stateAndOptionsFlag. Debug.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags"); Debug.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits"); - var tmpFlags = (int)creationOptions | (int)internalOptions; - if ((m_action == null) || ((internalOptions & InternalTaskOptions.ContinuationTask) != 0)) - { - // For continuation tasks or TaskCompletionSource.Tasks, begin life in the - // WaitingForActivation state rather than the Created state. - tmpFlags |= TASK_STATE_WAITINGFORACTIVATION; - } - m_stateFlags = tmpFlags; // one write to the volatile m_stateFlags instead of two when setting the above options + int tmpFlags = (int)creationOptions | (int)internalOptions; // one write to the volatile m_stateFlags instead of two when setting the above options + m_stateFlags = m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != 0 ? + tmpFlags | TASK_STATE_WAITINGFORACTIVATION : + tmpFlags; // Now is the time to add the new task to the children list // of the creating task if the options call for it. // We can safely call the creator task's AddNewChild() method to register it, // because at this point we are already on its thread of execution. - Task parent = m_contingentProperties?.m_parent; - if (parent != null - && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0) - && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) - ) + ContingentProperties props = m_contingentProperties; + if (props != null) { - parent.AddNewChild(); + Task parent = props.m_parent; + if (parent != null + && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0) + && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)) + { + parent.AddNewChild(); + } } // if we have a non-null cancellationToken, allocate the contingent properties to save it @@ -716,14 +713,7 @@ namespace System.Threading.Tasks } // Internal property to process TaskCreationOptions access and mutation. - internal TaskCreationOptions Options - { - get - { - int stateFlags = m_stateFlags; // "cast away" volatility to enable inlining of OptionsMethod - return OptionsMethod(stateFlags); - } - } + internal TaskCreationOptions Options => OptionsMethod(m_stateFlags); // Similar to Options property, but allows for the use of a cached flags value rather than // a read of the volatile m_stateFlags field. @@ -737,11 +727,16 @@ namespace System.Threading.Tasks // no illegalBits are set. Returns true on success, false on failure. internal bool AtomicStateUpdate(int newBits, int illegalBits) { - // This could be implemented in terms of: - // internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags); - // but for high-throughput perf, that delegation's cost is noticeable. + int oldFlags = m_stateFlags; + return + (oldFlags & illegalBits) == 0 && + (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags || + AtomicStateUpdateSlow(newBits, illegalBits)); + } - SpinWait sw = new SpinWait(); + private bool AtomicStateUpdateSlow(int newBits, int illegalBits) + { + var sw = new SpinWait(); do { int oldFlags = m_stateFlags; @@ -900,7 +895,6 @@ namespace System.Threading.Tasks return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool FireTaskScheduledIfNeeded(TaskScheduler ts) { var etwLog = TplEtwProvider.Log; @@ -1789,7 +1783,7 @@ namespace System.Threading.Tasks catch (ThreadAbortException tae) { AddException(tae); - FinishThreadAbortedTask(delegateRan:false); + FinishThreadAbortedTask(delegateRan: false); } catch (Exception e) { @@ -2041,32 +2035,45 @@ namespace System.Threading.Tasks /// <summary> /// Signals completion of this particular task. /// - /// The bUserDelegateExecuted parameter indicates whether this Finish() call comes following the + /// The userDelegateExecute parameter indicates whether this Finish() call comes following the /// full execution of the user delegate. /// - /// If bUserDelegateExecuted is false, it mean user delegate wasn't invoked at all (either due to + /// If userDelegateExecute is false, it mean user delegate wasn't invoked at all (either due to /// a cancellation request, or because this task is a promise style Task). In this case, the steps /// involving child tasks (i.e. WaitForChildren) will be skipped. /// /// </summary> - internal void Finish(bool bUserDelegateExecuted) + internal void Finish(bool userDelegateExecute) + { + if (m_contingentProperties == null) + { + FinishStageTwo(); + } + else + { + FinishSlow(userDelegateExecute); + } + } + + private void FinishSlow(bool userDelegateExecute) { - if (!bUserDelegateExecuted) + Debug.Assert(userDelegateExecute || m_contingentProperties != null); + + if (!userDelegateExecute) { // delegate didn't execute => no children. We can safely call the remaining finish stages FinishStageTwo(); } else { - var props = Volatile.Read(ref m_contingentProperties); + ContingentProperties props = m_contingentProperties; - if (props == null || // no contingent properties means no children, so it's safe to complete ourselves - (props.m_completionCountdown == 1) || - // Count of 1 => either all children finished, or there were none. Safe to complete ourselves - // without paying the price of an Interlocked.Decrement. + // Count of 1 => either all children finished, or there were none. Safe to complete ourselves + // without paying the price of an Interlocked.Decrement. + if ((props.m_completionCountdown == 1) || Interlocked.Decrement(ref props.m_completionCountdown) == 0) // Reaching this sub clause means there may be remaining active children, - // and we could be racing with one of them to call FinishStageTwo(). - // So whoever does the final Interlocked.Dec is responsible to finish. + // and we could be racing with one of them to call FinishStageTwo(). + // So whoever does the final Interlocked.Dec is responsible to finish. { FinishStageTwo(); } @@ -2086,8 +2093,7 @@ namespace System.Threading.Tasks // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw. // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren - List<Task> exceptionalChildren = props != null ? props.m_exceptionalChildren : null; - + List<Task> exceptionalChildren = props.m_exceptionalChildren; if (exceptionalChildren != null) { lock (exceptionalChildren) @@ -2106,12 +2112,17 @@ namespace System.Threading.Tasks /// It can happen i) either on the thread that originally executed this task (if no children were spawned, or they all completed by the time this task's delegate quit) /// ii) or on the thread that executed the last child. /// </summary> - internal void FinishStageTwo() + private void FinishStageTwo() { - AddExceptionsFromChildren(); - // At this point, the task is done executing and waiting for its children, // we can transition our task to a completion state. + + ContingentProperties cp = Volatile.Read(ref m_contingentProperties); + if (cp != null) + { + AddExceptionsFromChildren(cp); + } + int completionState; if (ExceptionRecorded) { @@ -2160,7 +2171,7 @@ namespace System.Threading.Tasks // Set the completion event if it's been lazy allocated. // And if we made a cancellation registration, it's now unnecessary. - var cp = Volatile.Read(ref m_contingentProperties); + cp = Volatile.Read(ref m_contingentProperties); // need to re-read after updating state if (cp != null) { cp.SetCompleted(); @@ -2187,6 +2198,17 @@ namespace System.Threading.Tasks m_action = null; // Notify parent if this was an attached task + if (m_contingentProperties != null) + { + NotifyParentIfPotentiallyAttachedTask(); + } + + // Activate continuations (if any). + FinishContinuations(); + } + + internal void NotifyParentIfPotentiallyAttachedTask() + { Task parent = m_contingentProperties?.m_parent; if (parent != null && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) @@ -2194,9 +2216,6 @@ namespace System.Threading.Tasks { parent.ProcessChildCompletion(this); } - - // Activate continuations (if any). - FinishContinuations(); } /// <summary> @@ -2232,7 +2251,6 @@ namespace System.Threading.Tasks tmp.Add(childTask); } } - } if (Interlocked.Decrement(ref props.m_completionCountdown) == 0) @@ -2248,14 +2266,15 @@ namespace System.Threading.Tasks /// This is to be called just before the task does its final state transition. /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list /// </summary> - internal void AddExceptionsFromChildren() + internal void AddExceptionsFromChildren(ContingentProperties props) { + Debug.Assert(props != null); + // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren // being nulled out while it is being processed, which could lead to a NullReferenceException. To // protect ourselves, we'll cache m_exceptionalChildren in a local variable. - var props = Volatile.Read(ref m_contingentProperties); - List<Task> exceptionalChildren = props?.m_exceptionalChildren; + List<Task> exceptionalChildren = props.m_exceptionalChildren; if (exceptionalChildren != null) { @@ -2310,40 +2329,13 @@ namespace System.Threading.Tasks Finish(delegateRan); } - - /// <summary> - /// Executes the task. This method will only be called once, and handles any necessary exception marshaling. - /// </summary> - private void Execute() - { - try - { - InnerInvoke(); - } - catch (ThreadAbortException tae) - { - // Record this exception in the task's exception list - HandleException(tae); - - // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to - // skip the regular Finish codepath. In order not to leave the task unfinished, we now call - // FinishThreadAbortedTask here. - FinishThreadAbortedTask(delegateRan: true); - } - catch (Exception exn) - { - // Record this exception in the task's exception list - HandleException(exn); - } - } - /// <summary> /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it. /// /// </summary> void IThreadPoolWorkItem.ExecuteWorkItem() { - ExecuteEntry(false); + ExecuteEntryUnsafe(); } /// <summary> @@ -2357,46 +2349,58 @@ namespace System.Threading.Tasks if (!IsCompleted) { HandleException(tae); - FinishThreadAbortedTask(delegateRan:false); + FinishThreadAbortedTask(delegateRan: false); } } /// <summary> /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread. - /// Currently this is called by IThreadPoolWorkItem.ExecuteWorkItem(), and TaskManager.TryExecuteInline. - /// /// </summary> - /// <param name="bPreventDoubleExecution"> Performs atomic updates to prevent double execution. Should only be set to true - /// in codepaths servicing user provided TaskSchedulers. The ThreadPool scheduler doesn't need this. </param> - internal bool ExecuteEntry(bool bPreventDoubleExecution) + internal bool ExecuteEntry() { - if (bPreventDoubleExecution) + // Do atomic state transition from queued to invoked. If we observe a task that's already invoked, + // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler. + // However we don't want this exception to be throw if the task was already canceled, because it's a + // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) + int previousState = 0; + if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED, + TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK, + ref previousState) && (previousState & TASK_STATE_CANCELED) == 0) { - int previousState = 0; + // This task has already been invoked. Don't invoke it again. + return false; + } - // Do atomic state transition from queued to invoked. If we observe a task that's already invoked, - // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler. - // However we don't want this exception to be throw if the task was already canceled, because it's a - // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) - if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED, - TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK, - ref previousState) && (previousState & TASK_STATE_CANCELED) == 0) - { - // This task has already been invoked. Don't invoke it again. - return false; - } + if (!IsCancellationRequested & !IsCanceled) + { + ExecuteWithThreadLocal(ref t_currentTask); } else { - // Remember that we started running the task delegate. - m_stateFlags |= TASK_STATE_DELEGATE_INVOKED; + ExecuteEntryCancellationRequestedOrCanceled(); } - if (!IsCancellationRequested && !IsCanceled) + return true; + } + + internal void ExecuteEntryUnsafe() // used instead of ExecuteEntry() when we don't have to worry about double-execution prevent + { + // Remember that we started running the task delegate. + m_stateFlags |= TASK_STATE_DELEGATE_INVOKED; + + if (!IsCancellationRequested & !IsCanceled) { ExecuteWithThreadLocal(ref t_currentTask); } - else if (!IsCanceled) + else + { + ExecuteEntryCancellationRequestedOrCanceled(); + } + } + + internal void ExecuteEntryCancellationRequestedOrCanceled() + { + if (!IsCanceled) { int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED); if ((prevState & TASK_STATE_CANCELED) == 0) @@ -2404,8 +2408,6 @@ namespace System.Threading.Tasks CancellationCleanupLogic(); } } - - return true; } // A trick so we can refer to the TLS slot with a byref. @@ -2429,30 +2431,44 @@ namespace System.Threading.Tasks etwLog.TaskStarted(TaskScheduler.Current.Id, 0, this.Id); } - if (AsyncCausalityTracer.LoggingOn) + bool loggingOn = AsyncCausalityTracer.LoggingOn; + if (loggingOn) AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.Execution); - try { // place the current task into TLS. currentTaskSlot = this; - ExecutionContext ec = CapturedContext; - if (ec == null) + // Execute the task body + try { - // No context, just run the task directly. - Execute(); + ExecutionContext ec = CapturedContext; + if (ec == null) + { + // No context, just run the task directly. + InnerInvoke(); + } + else + { + // Invoke it under the captured ExecutionContext + ExecutionContext.Run(ec, s_ecCallback, this); + } } - else + catch (Exception exn) { - // Run the task. We need a simple shim that converts the - // object back into a Task object, so that we can Execute it. - - ExecutionContext.Run(ec, s_ecCallback, this); + // Record this exception in the task's exception list + HandleException(exn); + if (exn is ThreadAbortException) + { + // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to + // skip the regular Finish codepath. In order not to leave the task unfinished, we now call + // FinishThreadAbortedTask here. + FinishThreadAbortedTask(delegateRan: true); + } } - if (AsyncCausalityTracer.LoggingOn) + if (loggingOn) AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution); Finish(true); @@ -2460,7 +2476,7 @@ namespace System.Threading.Tasks finally { currentTaskSlot = previousTask; - + // ETW event for Task Completed if (etwIsEnabled) { @@ -2476,7 +2492,7 @@ namespace System.Threading.Tasks } } - private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).Execute(); + private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).InnerInvoke(); /// <summary> /// The actual code which invokes the body of the task. This can be overriden in derived types. @@ -2973,7 +2989,6 @@ namespace System.Threading.Tasks { Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i)); } - } return IsCompleted; @@ -3031,12 +3046,10 @@ namespace System.Threading.Tasks // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc) mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED); - // PS: This is slightly different from the regular cancellation codepath // since we record the cancellation request *after* doing the state transition. // However that shouldn't matter too much because the task was never invoked, thus can't have children } - } if (!bCancelNonExecutingOnly || bPopSucceeded || mustCleanup) @@ -3125,7 +3138,7 @@ namespace System.Threading.Tasks oce = edi.SourceException as OperationCanceledException; Debug.Assert(oce != null, "Expected EDI to contain an OCE"); } - Debug.Assert(oce.CancellationToken == tokenToRecord, + Debug.Assert(oce.CancellationToken == tokenToRecord, "Expected OCE's token to match the provided token."); #endif AddException(cancellationException, representsCancellation: true); @@ -3191,7 +3204,18 @@ namespace System.Threading.Tasks { // Atomically store the fact that this task is completing. From this point on, the adding of continuations will // result in the continuations being run/launched directly rather than being added to the continuation list. + // Then if we grabbed any continuations, run them. object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel); + if (continuationObject != null) + { + RunContinuations(continuationObject); + } + } + + private void RunContinuations(object continuationObject) // separated out of FinishContinuations to enable it to be inlined + { + Debug.Assert(continuationObject != null); + TplEtwProvider etw = TplEtwProvider.Log; bool tplEtwProviderLoggingEnabled = etw.IsEnabled(); if (tplEtwProviderLoggingEnabled) @@ -3199,136 +3223,125 @@ namespace System.Threading.Tasks etw.RunningContinuation(Id, continuationObject); } - // If continuationObject == null, then we don't have any continuations to process - if (continuationObject != null) - { - - if (AsyncCausalityTracer.LoggingOn) - AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification); + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification); - // Skip synchronous execution of continuations if this task's thread was aborted - bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) || - (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) || - ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0)); + // Skip synchronous execution of continuations if this task's thread was aborted + bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) || + (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) || + ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0)); - // Handle the single-Action case - Action singleAction = continuationObject as Action; - if (singleAction != null) - { - AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask); - LogFinishCompletionNotification(); - return; - } + // Handle the single-Action case + Action singleAction = continuationObject as Action; + if (singleAction != null) + { + AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask); + LogFinishCompletionNotification(); + return; + } - // Handle the single-ITaskCompletionAction case - ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction; - if (singleTaskCompletionAction != null) + // Handle the single-ITaskCompletionAction case + ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction; + if (singleTaskCompletionAction != null) + { + if (bCanInlineContinuations || !singleTaskCompletionAction.InvokeMayRunArbitraryCode) { - if (bCanInlineContinuations || !singleTaskCompletionAction.InvokeMayRunArbitraryCode) - { - singleTaskCompletionAction.Invoke(this); - } - else - { - ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false); - } - LogFinishCompletionNotification(); - return; + singleTaskCompletionAction.Invoke(this); } - - // Handle the single-TaskContinuation case - TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation; - if (singleTaskContinuation != null) + else { - singleTaskContinuation.Run(this, bCanInlineContinuations); - LogFinishCompletionNotification(); - return; + ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false); } + LogFinishCompletionNotification(); + return; + } - // Not a single; attempt to cast as list - List<object> continuations = continuationObject as List<object>; + // Handle the single-TaskContinuation case + TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation; + if (singleTaskContinuation != null) + { + singleTaskContinuation.Run(this, bCanInlineContinuations); + LogFinishCompletionNotification(); + return; + } - if (continuations == null) - { - LogFinishCompletionNotification(); - return; // Not a single or a list; just return - } + // Not a single; it must be a list. + List<object> continuations = (List<object>)continuationObject; - // - // Begin processing of continuation list - // + // + // Begin processing of continuation list + // - // Wait for any concurrent adds or removes to be retired - lock (continuations) { } - int continuationCount = continuations.Count; + // Wait for any concurrent adds or removes to be retired + lock (continuations) { } + int continuationCount = continuations.Count; - // Fire the asynchronous continuations first ... - for (int i = 0; i < continuationCount; i++) + // Fire the asynchronous continuations first ... + for (int i = 0; i < continuationCount; i++) + { + // Synchronous continuation tasks will have the ExecuteSynchronously option, + // and we're looking for asynchronous tasks... + var tc = continuations[i] as StandardTaskContinuation; + if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0) { - // Synchronous continuation tasks will have the ExecuteSynchronously option, - // and we're looking for asynchronous tasks... - var tc = continuations[i] as StandardTaskContinuation; - if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0) + if (tplEtwProviderLoggingEnabled) { - if (tplEtwProviderLoggingEnabled) - { - etw.RunningContinuationList(Id, i, tc); - } - continuations[i] = null; // so that we can skip this later - tc.Run(this, bCanInlineContinuations); + etw.RunningContinuationList(Id, i, tc); } + continuations[i] = null; // so that we can skip this later + tc.Run(this, bCanInlineContinuations); } + } - // ... and then fire the synchronous continuations (if there are any). - // This includes ITaskCompletionAction, AwaitTaskContinuations, and - // Action delegates, which are all by default implicitly synchronous. - for (int i = 0; i < continuationCount; i++) + // ... and then fire the synchronous continuations (if there are any). + // This includes ITaskCompletionAction, AwaitTaskContinuations, and + // Action delegates, which are all by default implicitly synchronous. + for (int i = 0; i < continuationCount; i++) + { + object currentContinuation = continuations[i]; + if (currentContinuation == null) continue; + continuations[i] = null; // to enable free'ing up memory earlier + if (tplEtwProviderLoggingEnabled) { - object currentContinuation = continuations[i]; - if (currentContinuation == null) continue; - continuations[i] = null; // to enable free'ing up memory earlier - if (tplEtwProviderLoggingEnabled) - { - etw.RunningContinuationList(Id, i, currentContinuation); - } + etw.RunningContinuationList(Id, i, currentContinuation); + } - // If the continuation is an Action delegate, it came from an await continuation, - // and we should use AwaitTaskContinuation to run it. - Action ad = currentContinuation as Action; - if (ad != null) + // If the continuation is an Action delegate, it came from an await continuation, + // and we should use AwaitTaskContinuation to run it. + Action ad = currentContinuation as Action; + if (ad != null) + { + AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask); + } + else + { + // If it's a TaskContinuation object of some kind, invoke it. + TaskContinuation tc = currentContinuation as TaskContinuation; + if (tc != null) { - AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask); + // We know that this is a synchronous continuation because the + // asynchronous ones have been weeded out + tc.Run(this, bCanInlineContinuations); } + // Otherwise, it must be an ITaskCompletionAction, so invoke it. else { - // If it's a TaskContinuation object of some kind, invoke it. - TaskContinuation tc = currentContinuation as TaskContinuation; - if (tc != null) + Debug.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction"); + var action = (ITaskCompletionAction)currentContinuation; + + if (bCanInlineContinuations || !action.InvokeMayRunArbitraryCode) { - // We know that this is a synchronous continuation because the - // asynchronous ones have been weeded out - tc.Run(this, bCanInlineContinuations); + action.Invoke(this); } - // Otherwise, it must be an ITaskCompletionAction, so invoke it. else { - Debug.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction"); - var action = (ITaskCompletionAction)currentContinuation; - - if (bCanInlineContinuations || !action.InvokeMayRunArbitraryCode) - { - action.Invoke(this); - } - else - { - ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false); - } + ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false); } } } - - LogFinishCompletionNotification(); } + + LogFinishCompletionNotification(); } private void LogFinishCompletionNotification() @@ -4140,29 +4153,29 @@ namespace System.Threading.Tasks out InternalTaskOptions internalOptions) { // This is used a couple of times below - TaskContinuationOptions NotOnAnything = + const TaskContinuationOptions NotOnAnything = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.NotOnRanToCompletion; - TaskContinuationOptions creationOptionsMask = + const TaskContinuationOptions CreationOptionsMask = TaskContinuationOptions.PreferFairness | TaskContinuationOptions.LongRunning | TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.HideScheduler | - TaskContinuationOptions.AttachedToParent| + TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.RunContinuationsAsynchronously; // Check that LongRunning and ExecuteSynchronously are not specified together - TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning; - if ((continuationOptions & illegalMask) == illegalMask) + const TaskContinuationOptions IllegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning; + if ((continuationOptions & IllegalMask) == IllegalMask) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions, ExceptionResource.Task_ContinueWith_ESandLR); } // Check that no illegal options were specified if ((continuationOptions & - ~(creationOptionsMask | NotOnAnything | + ~(CreationOptionsMask | NotOnAnything | TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously)) != 0) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions); @@ -4175,14 +4188,12 @@ namespace System.Threading.Tasks } // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions - creationOptions = (TaskCreationOptions)(continuationOptions & creationOptionsMask); - - // internalOptions has at least ContinuationTask ... - internalOptions = InternalTaskOptions.ContinuationTask; + creationOptions = (TaskCreationOptions)(continuationOptions & CreationOptionsMask); - // ... and possibly LazyCancellation - if ((continuationOptions & TaskContinuationOptions.LazyCancellation) != 0) - internalOptions |= InternalTaskOptions.LazyCancellation; + // internalOptions has at least ContinuationTask and possibly LazyCancellation + internalOptions = (continuationOptions & TaskContinuationOptions.LazyCancellation) != 0 ? + InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation : + InternalTaskOptions.ContinuationTask; } @@ -4417,7 +4428,6 @@ namespace System.Threading.Tasks { // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up" continuationsLocalListRef[index] = null; - } } } @@ -4500,7 +4510,6 @@ namespace System.Threading.Tasks } return WaitAll(tasks, (int)totalMilliseconds); - } /// <summary> @@ -5020,6 +5029,10 @@ namespace System.Threading.Tasks signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result); Debug.Assert(signaledTaskIndex >= 0); } + else + { + TaskFactory.CommonCWAnyLogicCleanup(firstCompleted); + } } // We need to prevent the tasks array from being GC'ed until we come out of the wait. @@ -5103,7 +5116,7 @@ namespace System.Threading.Tasks Debug.Assert(succeeded, "This should always succeed on a new task."); return task; } - + /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary> /// <param name="cancellationToken">The token with which to complete the task.</param> /// <returns>The canceled task.</returns> @@ -5112,7 +5125,7 @@ namespace System.Threading.Tasks { return FromCanceled(cancellationToken); } - + /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary> /// <typeparam name="TResult">The type of the result returned by the task.</typeparam> /// <param name="cancellationToken">The token with which to complete the task.</param> @@ -5122,7 +5135,7 @@ namespace System.Threading.Tasks { return FromCanceled<TResult>(cancellationToken); } - + #endregion #region Run methods @@ -6120,8 +6133,8 @@ namespace System.Threading.Tasks internal virtual Delegate[] GetDelegateContinuationsForDebugger() { //Avoid an infinite loop by making sure the continuation object is not a reference to istelf. - if (this.m_continuationObject != this) - return GetDelegatesFromContinuationObject(this.m_continuationObject); + if (m_continuationObject != this) + return GetDelegatesFromContinuationObject(m_continuationObject); else return null; } @@ -6192,11 +6205,8 @@ namespace System.Threading.Tasks private static Task[] GetActiveTasks() { - return new List<Task>(s_currentActiveTasks.Values).ToArray(); } - - } internal sealed class CompletionActionInvoker : IThreadPoolWorkItem @@ -6715,5 +6725,4 @@ namespace System.Threading.Tasks public bool InvokeMayRunArbitraryCode { get { return true; } } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs deleted file mode 100644 index f15e3e783a..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/TaskCanceledException.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// An exception for task cancellations. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; - -namespace System.Threading.Tasks -{ - - /// <summary> - /// Represents an exception used to communicate task cancellation. - /// </summary> - [Serializable] - public class TaskCanceledException : OperationCanceledException - { - - [NonSerialized] - private Task m_canceledTask; // The task which has been canceled. - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class. - /// </summary> - public TaskCanceledException() : base(Environment.GetResourceString("TaskCanceledException_ctor_DefaultMessage")) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> - /// class with a specified error message. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - public TaskCanceledException(string message) : base(message) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> - /// class with a specified error message and a reference to the inner exception that is the cause of - /// this exception. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public TaskCanceledException(string message, Exception innerException) : base(message, innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class - /// with a reference to the <see cref="T:System.Threading.Tasks.Task"/> that has been canceled. - /// </summary> - /// <param name="task">A task that has been canceled.</param> - public TaskCanceledException(Task task) : - base(Environment.GetResourceString("TaskCanceledException_ctor_DefaultMessage"), task!=null ? task.CancellationToken:new CancellationToken()) - { - m_canceledTask = task; - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> - /// class with serialized data. - /// </summary> - /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param> - /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination. </param> - protected TaskCanceledException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - - /// <summary> - /// Gets the task associated with this exception. - /// </summary> - /// <remarks> - /// It is permissible for no Task to be associated with a - /// <see cref="T:System.Threading.Tasks.TaskCanceledException"/>, in which case - /// this property will return null. - /// </remarks> - public Task Task - { - get { return m_canceledTask; } - } - - } - -} diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs index bf9f9cbb2c..0c80afd22c 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskCompletionSource.cs @@ -131,7 +131,7 @@ namespace System.Threading.Tasks { // Spin wait until the completion is finalized by another thread. var sw = new SpinWait(); - while (!m_task.IsCompleted) + while (!m_task.IsCompleted) sw.SpinOnce(); } @@ -185,7 +185,7 @@ namespace System.Threading.Tasks public bool TrySetException(IEnumerable<Exception> exceptions) { if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions); - + List<Exception> defensiveCopy = new List<Exception>(); foreach (Exception e in exceptions) { @@ -276,7 +276,7 @@ namespace System.Threading.Tasks public bool TrySetResult(TResult result) { bool rval = m_task.TrySetResult(result); - if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); + if (!rval) SpinUntilCompleted(); return rval; } @@ -346,7 +346,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> public void SetCanceled() { - if(!TrySetCanceled()) + if (!TrySetCanceled()) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs index 3c6ccd8dd4..848a0ecbc2 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs @@ -32,7 +32,7 @@ namespace System.Threading.Tasks Task antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : base(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null) { - Contract.Requires(action is Action<Task> || action is Action<Task, object>, + Contract.Requires(action is Action<Task> || action is Action<Task, object>, "Invalid delegate type in ContinuationTaskFromTask"); m_antecedent = antecedent; } @@ -45,7 +45,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationTaskFromTask."); m_antecedent = null; @@ -79,7 +79,7 @@ namespace System.Threading.Tasks Task antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : base(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null) { - Contract.Requires(function is Func<Task, TResult> || function is Func<Task, object, TResult>, + Contract.Requires(function is Func<Task, TResult> || function is Func<Task, object, TResult>, "Invalid delegate type in ContinuationResultTaskFromTask"); m_antecedent = antecedent; } @@ -92,7 +92,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationResultTaskFromTask."); m_antecedent = null; @@ -126,7 +126,7 @@ namespace System.Threading.Tasks Task<TAntecedentResult> antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions) : base(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null) { - Contract.Requires(action is Action<Task<TAntecedentResult>> || action is Action<Task<TAntecedentResult>, object>, + Contract.Requires(action is Action<Task<TAntecedentResult>> || action is Action<Task<TAntecedentResult>, object>, "Invalid delegate type in ContinuationTaskFromResultTask"); m_antecedent = antecedent; } @@ -139,7 +139,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationTaskFromResultTask."); m_antecedent = null; @@ -186,7 +186,7 @@ namespace System.Threading.Tasks // Get and null out the antecedent. This is crucial to avoid a memory // leak with long chains of continuations. var antecedent = m_antecedent; - Debug.Assert(antecedent != null, + Debug.Assert(antecedent != null, "No antecedent was set for the ContinuationResultTaskFromResultTask."); m_antecedent = null; @@ -276,7 +276,6 @@ namespace System.Threading.Tasks } internal abstract Delegate[] GetDelegateContinuationsForDebugger(); - } /// <summary>Provides the standard implementation of a task continuation.</summary> @@ -505,7 +504,8 @@ namespace System.Threading.Tasks // Create the continuation task task. If we're allowed to inline, try to do so. // The target scheduler may still deny us from executing on this thread, in which case this'll be queued. - var task = CreateTask(state => { + var task = CreateTask(state => + { try { ((Action)state)(); } catch (Exception exc) { ThrowAsyncIfNecessary(exc); } }, m_action, m_scheduler); @@ -558,10 +558,10 @@ namespace System.Threading.Tasks Contract.Requires(scheduler != null); return new Task( - action, state, null, default(CancellationToken), - TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler) - { - CapturedContext = m_capturedContext + action, state, null, default(CancellationToken), + TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler) + { + CapturedContext = m_capturedContext }; } @@ -590,7 +590,7 @@ namespace System.Threading.Tasks // We couldn't inline, so now we need to schedule it ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false); - } + } } /// <summary> @@ -626,7 +626,7 @@ namespace System.Threading.Tasks } /// <summary>IThreadPoolWorkItem override, which is the entry function for this when the ThreadPool scheduler decides to run it.</summary> - void ExecuteWorkItemHelper() + private void ExecuteWorkItemHelper() { var etwLog = TplEtwProvider.Log; Guid savedActivityId = Guid.Empty; @@ -645,7 +645,7 @@ namespace System.Threading.Tasks { m_action(); } - // If there is an execution context, get the cached delegate and run the action under the context. + // If there is an execution context, get the cached delegate and run the action under the context. else { ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action); @@ -815,5 +815,4 @@ namespace System.Threading.Tasks return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(m_action) }; } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs index ee1112a93f..1385d907e0 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskExceptionHolder.cs @@ -70,9 +70,9 @@ namespace System.Threading.Tasks private static void EnsureADUnloadCallbackRegistered() { - if (s_adUnloadEventHandler == null && - Interlocked.CompareExchange( ref s_adUnloadEventHandler, - AppDomainUnloadCallback, + if (s_adUnloadEventHandler == null && + Interlocked.CompareExchange(ref s_adUnloadEventHandler, + AppDomainUnloadCallback, null) == null) { AppDomain.CurrentDomain.DomainUnload += s_adUnloadEventHandler; @@ -93,7 +93,7 @@ namespace System.Threading.Tasks // We need to do this filtering because all TaskExceptionHolders will be finalized during shutdown or unload // regardles of reachability of the task (i.e. even if the user code was about to observe the task's exception), // which can otherwise lead to spurious crashes during shutdown. - if (m_faultExceptions != null && !m_isHandled && + if (m_faultExceptions != null && !m_isHandled && !Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload() && !s_domainUnloadStarted) { // We don't want to crash the finalizer thread if any ThreadAbortExceptions @@ -124,14 +124,14 @@ namespace System.Threading.Tasks // will have been marked as handled before even getting here. // Give users a chance to keep this exception from crashing the process - + // First, publish the unobserved exception and allow users to observe it AggregateException exceptionToThrow = new AggregateException( - Environment.GetResourceString("TaskExceptionHolder_UnhandledException"), + SR.TaskExceptionHolder_UnhandledException, m_faultExceptions); UnobservedTaskExceptionEventArgs ueea = new UnobservedTaskExceptionEventArgs(exceptionToThrow); TaskScheduler.PublishUnobservedTaskException(m_task, ueea); - + // Now, if we are still unobserved and we're configured to crash on unobserved, throw the exception. // We need to publish the event above even if we're not going to crash, hence // why this check doesn't come at the beginning of the method. @@ -164,7 +164,7 @@ namespace System.Threading.Tasks { Contract.Requires(exceptionObject != null, "TaskExceptionHolder.Add(): Expected a non-null exceptionObject"); Contract.Requires( - exceptionObject is Exception || exceptionObject is IEnumerable<Exception> || + exceptionObject is Exception || exceptionObject is IEnumerable<Exception> || exceptionObject is ExceptionDispatchInfo || exceptionObject is IEnumerable<ExceptionDispatchInfo>, "TaskExceptionHolder.Add(): Expected Exception, IEnumerable<Exception>, ExceptionDispatchInfo, or IEnumerable<ExceptionDispatchInfo>"); @@ -180,16 +180,16 @@ namespace System.Threading.Tasks private void SetCancellationException(object exceptionObject) { Contract.Requires(exceptionObject != null, "Expected exceptionObject to be non-null."); - - Debug.Assert(m_cancellationException == null, + + Debug.Assert(m_cancellationException == null, "Expected SetCancellationException to be called only once."); - // Breaking this assumption will overwrite a previously OCE, - // and implies something may be wrong elsewhere, since there should only ever be one. + // Breaking this assumption will overwrite a previously OCE, + // and implies something may be wrong elsewhere, since there should only ever be one. - Debug.Assert(m_faultExceptions == null, + Debug.Assert(m_faultExceptions == null, "Expected SetCancellationException to be called before any faults were added."); - // Breaking this assumption shouldn't hurt anything here, but it implies something may be wrong elsewhere. - // If this changes, make sure to only conditionally mark as handled below. + // Breaking this assumption shouldn't hurt anything here, but it implies something may be wrong elsewhere. + // If this changes, make sure to only conditionally mark as handled below. // Store the cancellation exception var oce = exceptionObject as OperationCanceledException; @@ -267,21 +267,21 @@ namespace System.Threading.Tasks exceptions.AddRange(ediColl); #if DEBUG Debug.Assert(exceptions.Count > 0, "There should be at least one dispatch info."); - foreach(var tmp in exceptions) + foreach (var tmp in exceptions) { Debug.Assert(tmp != null, "No dispatch infos should be null"); } #endif } - // Anything else is a programming error + // Anything else is a programming error else { - throw new ArgumentException(Environment.GetResourceString("TaskExceptionHolder_UnknownExceptionType"), nameof(exceptionObject)); + throw new ArgumentException(SR.TaskExceptionHolder_UnknownExceptionType, nameof(exceptionObject)); } } } } - + // If all of the exceptions are ThreadAbortExceptions and/or // AppDomainUnloadExceptions, we do not want the finalization diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs index 89ba2988ca..e193d0e4e2 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskFactory.cs @@ -40,30 +40,20 @@ namespace System.Threading.Tasks public class TaskFactory { // member variables - private CancellationToken m_defaultCancellationToken; - private TaskScheduler m_defaultScheduler; - private TaskCreationOptions m_defaultCreationOptions; - private TaskContinuationOptions m_defaultContinuationOptions; + private readonly CancellationToken m_defaultCancellationToken; + private readonly TaskScheduler m_defaultScheduler; + private readonly TaskCreationOptions m_defaultCreationOptions; + private readonly TaskContinuationOptions m_defaultContinuationOptions; - - private TaskScheduler DefaultScheduler - { - get - { - if (m_defaultScheduler == null) return TaskScheduler.Current; - else return m_defaultScheduler; - } - } + private TaskScheduler DefaultScheduler => m_defaultScheduler ?? TaskScheduler.Current; // sister method to above property -- avoids a TLS lookup private TaskScheduler GetDefaultScheduler(Task currTask) { - if (m_defaultScheduler != null) return m_defaultScheduler; - else if ((currTask != null) - && ((currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0) - ) - return currTask.ExecutingTaskScheduler; - else return TaskScheduler.Default; + return + m_defaultScheduler ?? + (currTask != null && (currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0 ? currTask.ExecutingTaskScheduler : + TaskScheduler.Default); } /* Constructors */ @@ -1528,9 +1518,9 @@ namespace System.Threading.Tasks { // Options detected here cause exceptions in FromAsync methods that take beginMethod as a parameter if ((creationOptions & TaskCreationOptions.LongRunning) != 0) - throw new ArgumentOutOfRangeException(nameof(creationOptions), Environment.GetResourceString("Task_FromAsync_LongRunning")); + throw new ArgumentOutOfRangeException(nameof(creationOptions), SR.Task_FromAsync_LongRunning); if ((creationOptions & TaskCreationOptions.PreferFairness) != 0) - throw new ArgumentOutOfRangeException(nameof(creationOptions), Environment.GetResourceString("Task_FromAsync_PreferFairness")); + throw new ArgumentOutOfRangeException(nameof(creationOptions), SR.Task_FromAsync_PreferFairness); } // Check for general validity of options @@ -2325,7 +2315,7 @@ namespace System.Threading.Tasks { Contract.Requires(tasks != null, "Expected non-null collection of tasks"); _tasks = tasks; - + if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "TaskFactory.ContinueWhenAny", 0); @@ -2337,7 +2327,8 @@ namespace System.Threading.Tasks public void Invoke(Task completingTask) { - if (Interlocked.CompareExchange(ref m_firstTaskAlreadyCompleted, 1, 0) == 0) + if (m_firstTaskAlreadyCompleted == 0 && + Interlocked.Exchange(ref m_firstTaskAlreadyCompleted, 1) == 0) { if (AsyncCausalityTracer.LoggingOn) { @@ -2367,7 +2358,6 @@ namespace System.Threading.Tasks !task.IsCompleted) task.RemoveContinuation(this); } _tasks = null; - } } @@ -2382,17 +2372,18 @@ namespace System.Threading.Tasks { Contract.Requires(tasks != null); - // Create a promise task to be returned to the user + // Create a promise task to be returned to the user. + // (If this logic ever changes, also update CommonCWAnyLogicCleanup.) var promise = new CompleteOnInvokePromise(tasks); // At the completion of any of the tasks, complete the promise. bool checkArgsOnly = false; int numTasks = tasks.Count; - for(int i=0; i<numTasks; i++) + for (int i = 0; i < numTasks; i++) { var task = tasks[i]; - if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), nameof(tasks)); + if (task == null) throw new ArgumentException(SR.Task_MultiTaskContinuation_NullTask, nameof(tasks)); if (checkArgsOnly) continue; @@ -2430,6 +2421,17 @@ namespace System.Threading.Tasks return promise; } + /// <summary> + /// Cleans up the operations performed by CommonCWAnyLogic in a case where + /// the created continuation task is being discarded. + /// </summary> + /// <param name="continuation">The task returned from CommonCWAnyLogic.</param> + internal static void CommonCWAnyLogicCleanup(Task<Task> continuation) + { + // Force cleanup of the promise (e.g. removing continuations from each + // constituent task), by completing the promise with any value. + ((CompleteOnInvokePromise)continuation).Invoke(null); + } /// <summary> /// Creates a continuation <see cref="T:System.Threading.Tasks.Task">Task</see> @@ -2663,7 +2665,7 @@ namespace System.Threading.Tasks if (continuationFunction == null) throw new ArgumentNullException(nameof(continuationFunction)); Contract.EndContractBlock(); - return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null,continuationOptions, m_defaultCancellationToken, DefaultScheduler); + return TaskFactory<TResult>.ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// <summary> @@ -2775,7 +2777,7 @@ namespace System.Threading.Tasks if (continuationFunction == null) throw new ArgumentNullException(nameof(continuationFunction)); Contract.EndContractBlock(); - return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); + return TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// <summary> @@ -3018,7 +3020,7 @@ namespace System.Threading.Tasks if (tasks == null) throw new ArgumentNullException(nameof(tasks)); if (tasks.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_EmptyTaskList, nameof(tasks)); Contract.EndContractBlock(); Task[] tasksCopy = new Task[tasks.Length]; @@ -3027,7 +3029,7 @@ namespace System.Threading.Tasks tasksCopy[i] = tasks[i]; if (tasksCopy[i] == null) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_NullTask, nameof(tasks)); } return tasksCopy; @@ -3038,7 +3040,7 @@ namespace System.Threading.Tasks if (tasks == null) throw new ArgumentNullException(nameof(tasks)); if (tasks.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_EmptyTaskList, nameof(tasks)); Contract.EndContractBlock(); Task<TResult>[] tasksCopy = new Task<TResult>[tasks.Length]; @@ -3047,7 +3049,7 @@ namespace System.Threading.Tasks tasksCopy[i] = tasks[i]; if (tasksCopy[i] == null) - throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), nameof(tasks)); + throw new ArgumentException(SR.Task_MultiTaskContinuation_NullTask, nameof(tasks)); } return tasksCopy; @@ -3066,7 +3068,7 @@ namespace System.Threading.Tasks const TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning; if ((continuationOptions & illegalMask) == illegalMask) { - throw new ArgumentOutOfRangeException(nameof(continuationOptions), Environment.GetResourceString("Task_ContinueWith_ESandLR")); + throw new ArgumentOutOfRangeException(nameof(continuationOptions), SR.Task_ContinueWith_ESandLR); } // Check that no nonsensical options are specified. @@ -3085,9 +3087,8 @@ namespace System.Threading.Tasks // Check that no "fire" options are specified. if ((continuationOptions & NotOnAny) != 0) - throw new ArgumentOutOfRangeException(nameof(continuationOptions), Environment.GetResourceString("Task_MultiTaskContinuation_FireOptions")); + throw new ArgumentOutOfRangeException(nameof(continuationOptions), SR.Task_MultiTaskContinuation_FireOptions); Contract.EndContractBlock(); } } - } diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs index d68c3fedc4..45d398f0eb 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs @@ -11,6 +11,7 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // Disable the "reference to volatile field not treated as volatile" error. #pragma warning disable 0420 + using System; using System.Collections.Generic; using System.Globalization; @@ -23,7 +24,6 @@ using System.Runtime.CompilerServices; namespace System.Threading.Tasks { - /// <summary> /// Represents an abstract scheduler for tasks. /// </summary> @@ -46,7 +46,7 @@ namespace System.Threading.Tasks // // User Provided Methods and Properties // - + /// <summary> /// Queues a <see cref="T:System.Threading.Tasks.Task">Task</see> to the scheduler. /// </summary> @@ -168,7 +168,7 @@ namespace System.Threading.Tasks // // Internal overridable methods // - + /// <summary> /// Attempts to execute the target task synchronously. @@ -183,14 +183,14 @@ namespace System.Threading.Tasks // Do not inline TaskCompletionSource-style (a.k.a. "promise") tasks. // No need to attempt inlining if the task body was already run (i.e. either TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bits set) TaskScheduler ets = task.ExecutingTaskScheduler; - + // Delegate cross-scheduler inlining requests to target scheduler - if(ets != this && ets !=null) return ets.TryRunInline(task, taskWasPreviouslyQueued); + if (ets != this && ets != null) return ets.TryRunInline(task, taskWasPreviouslyQueued); StackGuard currentStackGuard; - if( (ets == null) || + if ((ets == null) || (task.m_action == null) || - task.IsDelegateInvoked || + task.IsDelegateInvoked || task.IsCanceled || (currentStackGuard = Task.CurrentStackGuard).TryBeginInliningScope() == false) { @@ -203,7 +203,10 @@ namespace System.Threading.Tasks bool bInlined = false; try { - task.FireTaskScheduledIfNeeded(this); + if (TplEtwProvider.Log.IsEnabled()) + { + task.FireTaskScheduledIfNeeded(this); + } bInlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued); } finally @@ -213,9 +216,9 @@ namespace System.Threading.Tasks // If the custom scheduler returned true, we should either have the TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bit set // Otherwise the scheduler is buggy - if (bInlined && !(task.IsDelegateInvoked || task.IsCanceled)) + if (bInlined && !(task.IsDelegateInvoked || task.IsCanceled)) { - throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_InconsistentStateAfterTryExecuteTaskInline")); + throw new InvalidOperationException(SR.TaskScheduler_InconsistentStateAfterTryExecuteTaskInline); } return bInlined; @@ -237,7 +240,7 @@ namespace System.Threading.Tasks /// Notifies the scheduler that a work item has made progress. /// </summary> internal virtual void NotifyWorkItemProgress() - { + { } /// <summary> @@ -256,7 +259,10 @@ namespace System.Threading.Tasks { Contract.Requires(task != null); - task.FireTaskScheduledIfNeeded(this); + if (TplEtwProvider.Log.IsEnabled()) + { + task.FireTaskScheduledIfNeeded(this); + } this.QueueTask(task); } @@ -269,7 +275,7 @@ namespace System.Threading.Tasks // The global container that keeps track of TaskScheduler instances for debugging purposes. private static ConditionalWeakTable<TaskScheduler, object> s_activeTaskSchedulers; - + // An AppDomain-wide default manager. private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler(); @@ -316,7 +322,7 @@ namespace System.Threading.Tasks /// <summary> /// Gets the default <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> instance. /// </summary> - public static TaskScheduler Default + public static TaskScheduler Default { get { @@ -331,7 +337,7 @@ namespace System.Threading.Tasks /// <remarks> /// When not called from within a task, <see cref="Current"/> will return the <see cref="Default"/> scheduler. /// </remarks> - public static TaskScheduler Current + public static TaskScheduler Current { get { @@ -352,7 +358,7 @@ namespace System.Threading.Tasks get { Task currentTask = Task.InternalCurrent; - return ( (currentTask != null) + return ((currentTask != null) && ((currentTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0) ) ? currentTask.ExecutingTaskScheduler : null; } @@ -398,7 +404,7 @@ namespace System.Threading.Tasks { newId = Interlocked.Increment(ref s_taskSchedulerIdCounter); } while (newId == 0); - + Interlocked.CompareExchange(ref m_taskSchedulerId, newId, 0); } @@ -437,10 +443,10 @@ namespace System.Threading.Tasks { if (task.ExecutingTaskScheduler != this) { - throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_ExecuteTask_WrongTaskScheduler")); + throw new InvalidOperationException(SR.TaskScheduler_ExecuteTask_WrongTaskScheduler); } - return task.ExecuteEntry(true); + return task.ExecuteEntry(); } //////////////////////////////////////////////////////////// @@ -477,12 +483,12 @@ namespace System.Threading.Tasks lock (_unobservedTaskExceptionLockObject) _unobservedTaskException -= value; } } - - + + //////////////////////////////////////////////////////////// // // Internal methods @@ -588,19 +594,18 @@ namespace System.Threading.Tasks m_taskScheduler = scheduler; } - // returns the scheduler’s Id + // returns the scheduler�s Id public Int32 Id - { - get { return m_taskScheduler.Id; } + { + get { return m_taskScheduler.Id; } } - // returns the scheduler’s GetScheduledTasks - public IEnumerable<Task> ScheduledTasks + // returns the scheduler�s GetScheduledTasks + public IEnumerable<Task> ScheduledTasks { get { return m_taskScheduler.GetScheduledTasks(); } } } - } @@ -626,11 +631,10 @@ namespace System.Threading.Tasks // make sure we have a synccontext to work with if (synContext == null) { - throw new InvalidOperationException(Environment.GetResourceString("TaskScheduler_FromCurrentSynchronizationContext_NoCurrent")); + throw new InvalidOperationException(SR.TaskScheduler_FromCurrentSynchronizationContext_NoCurrent); } m_synchronizationContext = synContext; - } /// <summary> @@ -684,16 +688,7 @@ namespace System.Threading.Tasks } // preallocated SendOrPostCallback delegate - private static SendOrPostCallback s_postCallback = new SendOrPostCallback(PostCallback); - - // this is where the actual task invocation occures - private static void PostCallback(object obj) - { - Task task = (Task) obj; - - // calling ExecuteEntry with double execute check enabled because a user implemented SynchronizationContext could be buggy - task.ExecuteEntry(true); - } + private static readonly SendOrPostCallback s_postCallback = s => ((Task)s).ExecuteEntry(); // with double-execute check because SC could be buggy } /// <summary> @@ -728,7 +723,7 @@ namespace System.Threading.Tasks /// Gets whether this exception has been marked as "observed." /// </summary> public bool Observed { get { return m_observed; } } - + /// <summary> /// The Exception that went unobserved. /// </summary> diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs b/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs deleted file mode 100644 index 1d85e49342..0000000000 --- a/src/mscorlib/src/System/Threading/Tasks/TaskSchedulerException.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// An exception for task schedulers. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; - -namespace System.Threading.Tasks -{ - - /// <summary> - /// Represents an exception used to communicate an invalid operation by a - /// <see cref="T:System.Threading.Tasks.TaskScheduler"/>. - /// </summary> - [Serializable] - public class TaskSchedulerException : Exception - { - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> class. - /// </summary> - public TaskSchedulerException() : base(Environment.GetResourceString("TaskSchedulerException_ctor_DefaultMessage")) // - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class with a specified error message. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - public TaskSchedulerException(string message) : base(message) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class using the default error message and a reference to the inner exception that is the cause of - /// this exception. - /// </summary> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public TaskSchedulerException(Exception innerException) - : base(Environment.GetResourceString("TaskSchedulerException_ctor_DefaultMessage"), innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class with a specified error message and a reference to the inner exception that is the cause of - /// this exception. - /// </summary> - /// <param name="message">The error message that explains the reason for the exception.</param> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public TaskSchedulerException(string message, Exception innerException) : base(message, innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> - /// class with serialized data. - /// </summary> - /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds - /// the serialized object data about the exception being thrown.</param> - /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that - /// contains contextual information about the source or destination. </param> - protected TaskSchedulerException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - - } - -} diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs b/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs index 90743aeec5..fdd62c95f5 100644 --- a/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs +++ b/src/mscorlib/src/System/Threading/Tasks/TaskToApm.cs @@ -60,7 +60,7 @@ namespace System.Threading.Tasks { // Asynchronous completion asyncResult = task.AsyncState == state ? (IAsyncResult)task : new TaskWrapperAsyncResult(task, state, completedSynchronously: false); - if (callback != null) + if (callback != null) InvokeCallbackWhenTaskCompletes(task, callback, asyncResult); } return asyncResult; @@ -129,7 +129,7 @@ namespace System.Threading.Tasks // We use OnCompleted rather than ContinueWith in order to avoid running synchronously // if the task has already completed by the time we get here. This is separated out into // its own method currently so that we only pay for the closure if necessary. - antecedent.ConfigureAwait(continueOnCapturedContext:false) + antecedent.ConfigureAwait(continueOnCapturedContext: false) .GetAwaiter() .OnCompleted(() => callback(asyncResult)); @@ -167,7 +167,7 @@ namespace System.Threading.Tasks /// <param name="task">The Task to wrap.</param> /// <param name="state">The new AsyncState value</param> /// <param name="completedSynchronously">The new CompletedSynchronously value.</param> - internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously) + internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously) { Contract.Requires(task != null); Contract.Requires(!completedSynchronously || task.IsCompleted, "If completedSynchronously is true, the task must be completed."); diff --git a/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs index 5c6ca9bb76..e69a89fe66 100644 --- a/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs +++ b/src/mscorlib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs @@ -23,7 +23,7 @@ namespace System.Threading.Tasks /// <summary> /// An implementation of TaskScheduler that uses the ThreadPool scheduler /// </summary> - internal sealed class ThreadPoolTaskScheduler: TaskScheduler + internal sealed class ThreadPoolTaskScheduler : TaskScheduler { /// <summary> /// Constructs a new ThreadPool task scheduler object @@ -34,15 +34,7 @@ namespace System.Threading.Tasks } // static delegate for threads allocated to handle LongRunning tasks. - private static readonly ParameterizedThreadStart s_longRunningThreadWork = new ParameterizedThreadStart(LongRunningThreadWork); - - private static void LongRunningThreadWork(object obj) - { - Contract.Requires(obj != null, "TaskScheduler.LongRunningThreadWork: obj is null"); - Task t = obj as Task; - Debug.Assert(t != null, "TaskScheduler.LongRunningThreadWork: t is null"); - t.ExecuteEntry(false); - } + private static readonly ParameterizedThreadStart s_longRunningThreadWork = s => ((Task)s).ExecuteEntryUnsafe(); /// <summary> /// Schedules a task to the ThreadPool. @@ -64,11 +56,11 @@ namespace System.Threading.Tasks ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue); } } - + /// <summary> /// This internal function will do this: /// (1) If the task had previously been queued, attempt to pop it and return false if that fails. - /// (2) Propagate the return value from Task.ExecuteEntry() back to the caller. + /// (2) Return whether the task is executed /// /// IMPORTANT NOTE: TryExecuteTaskInline will NOT throw task exceptions itself. Any wait code path using this function needs /// to account for exceptions that need to be propagated, and throw themselves accordingly. @@ -79,19 +71,17 @@ namespace System.Threading.Tasks if (taskWasPreviouslyQueued && !ThreadPool.TryPopCustomWorkItem(task)) return false; - // Propagate the return value of Task.ExecuteEntry() - bool rval = false; try { - rval = task.ExecuteEntry(false); // handles switching Task.Current etc. + task.ExecuteEntryUnsafe(); // handles switching Task.Current etc. } finally { // Only call NWIP() if task was previously queued - if(taskWasPreviouslyQueued) NotifyWorkItemProgress(); + if (taskWasPreviouslyQueued) NotifyWorkItemProgress(); } - return rval; + return true; } protected internal override bool TryDequeue(Task task) diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs index 15136f12bf..26c2388e6b 100644 --- a/src/mscorlib/src/System/Threading/Tasks/future.cs +++ b/src/mscorlib/src/System/Threading/Tasks/future.cs @@ -82,20 +82,20 @@ namespace System.Threading.Tasks internal static readonly Func<Task<Task>, Task<TResult>> TaskWhenAnyCast = completed => (Task<TResult>)completed.Result; // Construct a promise-style task without any options. - internal Task() : + internal Task() : base() { } // Construct a promise-style task with state and options. internal Task(object state, TaskCreationOptions options) : - base(state, options, promiseStyle:true) + base(state, options, promiseStyle: true) { } // Construct a pre-completed Task<TResult> - internal Task(TResult result) : + internal Task(TResult result) : base(false, TaskCreationOptions.None, default(CancellationToken)) { m_result = result; @@ -372,9 +372,9 @@ namespace System.Threading.Tasks // Debugger support private string DebuggerDisplayResultDescription { - get + get { - return IsRanToCompletion ? "" + m_result : Environment.GetResourceString("TaskT_DebuggerNoResult"); + return IsRanToCompletion ? "" + m_result : SR.TaskT_DebuggerNoResult; } } @@ -392,7 +392,6 @@ namespace System.Threading.Tasks // internal helper function breaks out logic used by TaskCompletionSource internal bool TrySetResult(TResult result) { - if (IsCompleted) return false; Debug.Assert(m_action == null, "Task<T>.TrySetResult(): non-null m_action"); // "Reserve" the completion for this task, while making sure that: (1) No prior reservation @@ -413,12 +412,13 @@ namespace System.Threading.Tasks // and which can be summarized more concisely with the following snippet from // FinishStageTwo, omitting everything that doesn't pertain to TrySetResult. Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION); - - var cp = m_contingentProperties; - if (cp != null) cp.SetCompleted(); - - FinishStageThree(); - + ContingentProperties props = m_contingentProperties; + if (props != null) + { + NotifyParentIfPotentiallyAttachedTask(); + props.SetCompleted(); + } + FinishContinuations(); return true; } @@ -441,7 +441,7 @@ namespace System.Threading.Tasks bool success = TrySetResult(result); // Nobody else has had a chance to complete this Task yet, so we should succeed. - Debug.Assert(success); + Debug.Assert(success); } else { @@ -477,7 +477,7 @@ namespace System.Threading.Tasks { Debug.Assert(!IsWaitNotificationEnabledOrNotRanToCompletion, "Should only be used when the task completed successfully and there's no wait notification enabled"); - return m_result; + return m_result; } } @@ -539,7 +539,6 @@ namespace System.Threading.Tasks } return returnValue; - } // internal helper function breaks out logic used by TaskCompletionSource and AsyncMethodBuilder @@ -879,7 +878,7 @@ namespace System.Threading.Tasks /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> /// has already been disposed. /// </exception> - public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,CancellationToken cancellationToken) + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, CancellationToken cancellationToken) { return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); } @@ -942,7 +941,7 @@ namespace System.Threading.Tasks /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. /// </exception> - public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,TaskContinuationOptions continuationOptions) + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, TaskContinuationOptions continuationOptions) { return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions); } @@ -1014,7 +1013,7 @@ namespace System.Threading.Tasks out internalOptions); Task continuationTask = new ContinuationTaskFromResultTask<TResult>( - this, continuationAction, state, + this, continuationAction, state, creationOptions, internalOptions ); @@ -1229,7 +1228,7 @@ namespace System.Threading.Tasks out creationOptions, out internalOptions); - Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( + Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult, TNewResult>( this, continuationFunction, null, creationOptions, internalOptions ); @@ -1452,7 +1451,7 @@ namespace System.Threading.Tasks out creationOptions, out internalOptions); - Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( + Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult, TNewResult>( this, continuationFunction, state, creationOptions, internalOptions ); @@ -1467,7 +1466,7 @@ namespace System.Threading.Tasks #endregion #endregion - + /// <summary> /// Subscribes an <see cref="IObserver{TResult}"/> to receive notification of the final state of this <see cref="Task{TResult}"/>. /// </summary> @@ -1553,7 +1552,5 @@ namespace System.Threading.Tasks public int Id { get { return m_task.Id; } } public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } } public TaskStatus Status { get { return m_task.Status; } } - - } } diff --git a/src/mscorlib/src/System/Threading/Thread.cs b/src/mscorlib/src/System/Threading/Thread.cs index d28002729a..70a5d06f7a 100644 --- a/src/mscorlib/src/System/Threading/Thread.cs +++ b/src/mscorlib/src/System/Threading/Thread.cs @@ -14,7 +14,8 @@ using Internal.Runtime.Augments; -namespace System.Threading { +namespace System.Threading +{ using System.Threading; using System.Runtime; using System.Runtime.InteropServices; @@ -33,11 +34,9 @@ namespace System.Threading { internal class ThreadHelper { - static ThreadHelper() {} - - Delegate _start; - Object _startArg = null; - ExecutionContext _executionContext = null; + private Delegate _start; + private Object _startArg = null; + private ExecutionContext _executionContext = null; internal ThreadHelper(Delegate start) { _start = start; @@ -49,7 +48,7 @@ namespace System.Threading { } static internal ContextCallback _ccb = new ContextCallback(ThreadStart_Context); - + static private void ThreadStart_Context(Object state) { ThreadHelper t = (ThreadHelper)state; @@ -65,9 +64,9 @@ namespace System.Threading { // call back helper internal void ThreadStart(object obj) - { + { _startArg = obj; - if (_executionContext != null) + if (_executionContext != null) { ExecutionContext.Run(_executionContext, _ccb, (Object)this); } @@ -80,7 +79,7 @@ namespace System.Threading { // call back helper internal void ThreadStart() { - if (_executionContext != null) + if (_executionContext != null) { ExecutionContext.Run(_executionContext, _ccb, (Object)this); } @@ -101,7 +100,7 @@ namespace System.Threading { } } - public sealed class Thread : RuntimeThread + internal sealed class Thread : RuntimeThread { /*========================================================================= ** Data accessed from managed code that needs to be defined in @@ -111,10 +110,10 @@ namespace System.Threading { private ExecutionContext m_ExecutionContext; // this call context follows the logical thread private SynchronizationContext m_SynchronizationContext; // On CoreCLR, this is maintained separately from ExecutionContext - private String m_Name; - private Delegate m_Delegate; // Delegate + private String m_Name; + private Delegate m_Delegate; // Delegate - private Object m_ThreadStartArg; + private Object m_ThreadStartArg; /*========================================================================= ** The base implementation of Thread is all native. The following fields @@ -126,10 +125,10 @@ namespace System.Threading { #pragma warning disable 414 // These fields are not used from managed. // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit // fields on 64-bit platforms, where they will be sorted together. - - private IntPtr DONT_USE_InternalThread; // Pointer - private int m_Priority; // INT32 - private int m_ManagedThreadId; // INT32 + + private IntPtr DONT_USE_InternalThread; // Pointer + private int m_Priority; // INT32 + private int m_ManagedThreadId; // INT32 #pragma warning restore 414 #pragma warning restore 169 @@ -143,25 +142,12 @@ namespace System.Threading { // with native code // See code:#threadCultureInfo [ThreadStatic] - internal static CultureInfo m_CurrentCulture; + internal static CultureInfo m_CurrentCulture; [ThreadStatic] - internal static CultureInfo m_CurrentUICulture; - - static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture; - static AsyncLocal<CultureInfo> s_asyncLocalCurrentUICulture; - - static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args) - { - m_CurrentCulture = args.CurrentValue; - } - - static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args) - { - m_CurrentUICulture = args.CurrentValue; - } + internal static CultureInfo m_CurrentUICulture; // Adding an empty default ctor for annotation purposes - internal Thread(){} + internal Thread() { } /*========================================================================= ** Creates a new Thread object which will begin execution at @@ -169,37 +155,45 @@ namespace System.Threading { ** ** Exceptions: ArgumentNullException if start == null. =========================================================================*/ - public Thread(ThreadStart start) { - if (start == null) { + public Thread(ThreadStart start) + { + if (start == null) + { throw new ArgumentNullException(nameof(start)); } Contract.EndContractBlock(); - SetStartHelper((Delegate)start,0); //0 will setup Thread with default stackSize + SetStartHelper((Delegate)start, 0); //0 will setup Thread with default stackSize } - internal Thread(ThreadStart start, int maxStackSize) { - if (start == null) { + internal Thread(ThreadStart start, int maxStackSize) + { + if (start == null) + { throw new ArgumentNullException(nameof(start)); } if (0 > maxStackSize) - throw new ArgumentOutOfRangeException(nameof(maxStackSize),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum); Contract.EndContractBlock(); SetStartHelper((Delegate)start, maxStackSize); } - public Thread(ParameterizedThreadStart start) { - if (start == null) { + public Thread(ParameterizedThreadStart start) + { + if (start == null) + { throw new ArgumentNullException(nameof(start)); } Contract.EndContractBlock(); SetStartHelper((Delegate)start, 0); } - internal Thread(ParameterizedThreadStart start, int maxStackSize) { - if (start == null) { + internal Thread(ParameterizedThreadStart start, int maxStackSize) + { + if (start == null) + { throw new ArgumentNullException(nameof(start)); } if (0 > maxStackSize) - throw new ArgumentOutOfRangeException(nameof(maxStackSize),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum); Contract.EndContractBlock(); SetStartHelper((Delegate)start, maxStackSize); } @@ -224,7 +218,7 @@ namespace System.Threading { // There are ways how to create an unitialized objects through remoting, etc. Avoid AVing in the EE by throwing a nice // exception here. if (thread.IsNull()) - throw new ArgumentException(null, Environment.GetResourceString("Argument_InvalidHandle")); + throw new ArgumentException(null, SR.Argument_InvalidHandle); return new ThreadHandle(thread); } @@ -237,24 +231,24 @@ namespace System.Threading { ** ** Exceptions: ThreadStateException if the thread has already been started. =========================================================================*/ - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public new void Start() { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; Start(ref stackMark); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public new void Start(object parameter) { //In the case of a null delegate (second call to start on same thread) // StartInternal method will take care of the error reporting - if(m_Delegate is ThreadStart) + if (m_Delegate is ThreadStart) { //We expect the thread to be setup with a ParameterizedThreadStart // if this constructor is called. //If we got here then that wasn't the case - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ThreadWrongThreadStart")); + throw new InvalidOperationException(SR.InvalidOperation_ThreadWrongThreadStart); } m_ThreadStartArg = parameter; StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; @@ -285,7 +279,7 @@ namespace System.Threading { internal ExecutionContext ExecutionContext { - get { return m_ExecutionContext; } + get { return m_ExecutionContext; } set { m_ExecutionContext = value; } } @@ -320,15 +314,15 @@ namespace System.Threading { { SleepInternal(millisecondsTimeout); // Ensure we don't return to app code when the pause is underway - if(AppDomainPauseManager.IsPaused) + if (AppDomainPauseManager.IsPaused) AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS(); } public static void Sleep(TimeSpan timeout) { long tm = (long)timeout.TotalMilliseconds; - if (tm < -1 || tm > (long) Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + if (tm < -1 || tm > (long)Int32.MaxValue) + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Sleep((int)tm); } @@ -353,9 +347,11 @@ namespace System.Threading { { return YieldInternal(); } - - public static new Thread CurrentThread { - get { + + public static new Thread CurrentThread + { + get + { Contract.Ensures(Contract.Result<Thread>() != null); return GetCurrentThreadNative(); } @@ -368,14 +364,14 @@ namespace System.Threading { Debug.Assert(maxStackSize >= 0); ThreadHelper threadStartCallBack = new ThreadHelper(start); - if(start is ThreadStart) + if (start is ThreadStart) { SetStart(new ThreadStart(threadStartCallBack.ThreadStart), maxStackSize); } else { SetStart(new ParameterizedThreadStart(threadStartCallBack.ThreadStart), maxStackSize); - } + } } /*========================================================================= @@ -437,72 +433,26 @@ namespace System.Threading { // app domain get unloaded there is a code to clean up the culture from the thread // using the code in AppDomain::ReleaseDomainStores. - public CultureInfo CurrentUICulture { - get { + public CultureInfo CurrentUICulture + { + get + { Contract.Ensures(Contract.Result<CultureInfo>() != null); -#if FEATURE_APPX && !FEATURE_COREFX_GLOBALIZATION - if(AppDomain.IsAppXModel()) { - return CultureInfo.GetCultureInfoForUserPreferredLanguageInAppX() ?? GetCurrentUICultureNoAppX(); - } - else -#endif - { - return GetCurrentUICultureNoAppX(); - } + return CultureInfo.CurrentUICulture; } - set { - if (value == null) { - throw new ArgumentNullException(nameof(value)); - } - Contract.EndContractBlock(); - - //If they're trying to use a Culture with a name that we can't use in resource lookup, - //don't even let them set it on the thread. - CultureInfo.VerifyCultureName(value, true); - + set + { // If you add more pre-conditions to this method, check to see if you also need to // add them to CultureInfo.DefaultThreadCurrentUICulture.set. if (m_CurrentUICulture == null && m_CurrentCulture == null) nativeInitCultureAccessors(); - if (!AppContextSwitches.NoAsyncCurrentCulture) - { - if (s_asyncLocalCurrentUICulture == null) - { - Interlocked.CompareExchange(ref s_asyncLocalCurrentUICulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentUICulture), null); - } - - // this one will set m_CurrentUICulture too - s_asyncLocalCurrentUICulture.Value = value; - } - else - { - m_CurrentUICulture = value; - } + CultureInfo.CurrentUICulture = value; } } - internal CultureInfo GetCurrentUICultureNoAppX() { - - Contract.Ensures(Contract.Result<CultureInfo>() != null); - -#if FEATURE_COREFX_GLOBALIZATION - return CultureInfo.CurrentUICulture; -#else - - // Fetch a local copy of m_CurrentUICulture to - // avoid race conditions that malicious user can introduce - if (m_CurrentUICulture == null) { - CultureInfo appDomainDefaultUICulture = CultureInfo.DefaultThreadCurrentUICulture; - return (appDomainDefaultUICulture != null ? appDomainDefaultUICulture : CultureInfo.UserDefaultUICulture); - } - - return m_CurrentUICulture; -#endif - } - // This returns the exposed context for a given context ID. // As the culture can be customized object then we cannot hold any @@ -517,25 +467,16 @@ namespace System.Threading { // app domain get unloaded there is a code to clean up the culture from the thread // using the code in AppDomain::ReleaseDomainStores. - public CultureInfo CurrentCulture { - get { + public CultureInfo CurrentCulture + { + get + { Contract.Ensures(Contract.Result<CultureInfo>() != null); - -#if FEATURE_APPX && !FEATURE_COREFX_GLOBALIZATION - if(AppDomain.IsAppXModel()) { - return CultureInfo.GetCultureInfoForUserPreferredLanguageInAppX() ?? GetCurrentCultureNoAppX(); - } - else -#endif - { - return GetCurrentCultureNoAppX(); - } + return CultureInfo.CurrentCulture; } - set { - if (null==value) { - throw new ArgumentNullException(nameof(value)); - } + set + { Contract.EndContractBlock(); // If you add more pre-conditions to this method, check to see if you also need to @@ -543,39 +484,9 @@ namespace System.Threading { if (m_CurrentCulture == null && m_CurrentUICulture == null) nativeInitCultureAccessors(); - - if (!AppContextSwitches.NoAsyncCurrentCulture) - { - if (s_asyncLocalCurrentCulture == null) - { - Interlocked.CompareExchange(ref s_asyncLocalCurrentCulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentCulture), null); - } - // this one will set m_CurrentCulture too - s_asyncLocalCurrentCulture.Value = value; - } - else - { - m_CurrentCulture = value; - } - } - } - - private CultureInfo GetCurrentCultureNoAppX() { - -#if FEATURE_COREFX_GLOBALIZATION - return CultureInfo.CurrentCulture; -#else - Contract.Ensures(Contract.Result<CultureInfo>() != null); - - // Fetch a local copy of m_CurrentCulture to - // avoid race conditions that malicious user can introduce - if (m_CurrentCulture == null) { - CultureInfo appDomainDefaultCulture = CultureInfo.DefaultThreadCurrentCulture; - return (appDomainDefaultCulture != null ? appDomainDefaultCulture : CultureInfo.UserDefaultCulture); + + CultureInfo.CurrentCulture = value; } - - return m_CurrentCulture; -#endif } [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] @@ -616,14 +527,18 @@ namespace System.Threading { // Retrieves the name of the thread. // - public new String Name { - get { + public new String Name + { + get + { return m_Name; } - set { - lock(this) { + set + { + lock (this) + { if (m_Name != null) - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WriteOnce")); + throw new InvalidOperationException(SR.InvalidOperation_WriteOnce); m_Name = value; InformThreadNameChange(GetNativeHandle(), value, (value != null) ? value.Length : 0); @@ -635,9 +550,6 @@ namespace System.Threading { [SuppressUnmanagedCodeSecurity] private static extern void InformThreadNameChange(ThreadHandle t, String name, int len); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern void MemoryBarrier(); - } // End of class Thread // declaring a local var of this enum type and passing it by ref into a function that needs to do a @@ -651,5 +563,4 @@ namespace System.Threading { LookForMyCallersCaller = 2, LookForThread = 3 } - } diff --git a/src/mscorlib/src/System/Threading/ThreadAbortException.cs b/src/mscorlib/src/System/Threading/ThreadAbortException.cs deleted file mode 100644 index 25925048bf..0000000000 --- a/src/mscorlib/src/System/Threading/ThreadAbortException.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: An exception class which is thrown into a thread to cause it to -** abort. This is a special non-catchable exception and results in -** the thread's death. This is thrown by the VM only and can NOT be -** thrown by any user thread, and subclassing this is useless. -** -** -=============================================================================*/ - -namespace System.Threading -{ - using System; - using System.Runtime.Serialization; - using System.Runtime.CompilerServices; - - [Serializable] - public sealed class ThreadAbortException : SystemException - { - private ThreadAbortException() - : base(GetMessageFromNativeResources(ExceptionMessageKind.ThreadAbort)) - { - SetErrorCode(__HResults.COR_E_THREADABORTED); - } - - //required for serialization - internal ThreadAbortException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs index 71c09649e2..9122df0d3e 100644 --- a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs +++ b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs @@ -12,29 +12,36 @@ ** ** =============================================================================*/ -namespace System.Threading { - using System.Threading; - using System; - using System.Runtime.Serialization; +using System.Threading; +using System; +using System.Runtime.Serialization; + +namespace System.Threading +{ [Serializable] - public class ThreadInterruptedException : SystemException { - public ThreadInterruptedException() - : base(GetMessageFromNativeResources(ExceptionMessageKind.ThreadInterrupted)) { - SetErrorCode(__HResults.COR_E_THREADINTERRUPTED); + public class ThreadInterruptedException : SystemException + { + public ThreadInterruptedException() + : base(GetMessageFromNativeResources(ExceptionMessageKind.ThreadInterrupted)) + { + HResult = __HResults.COR_E_THREADINTERRUPTED; } - - public ThreadInterruptedException(String message) - : base(message) { - SetErrorCode(__HResults.COR_E_THREADINTERRUPTED); + + public ThreadInterruptedException(String message) + : base(message) + { + HResult = __HResults.COR_E_THREADINTERRUPTED; } - - public ThreadInterruptedException(String message, Exception innerException) - : base(message, innerException) { - SetErrorCode(__HResults.COR_E_THREADINTERRUPTED); + + public ThreadInterruptedException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.COR_E_THREADINTERRUPTED; } - protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) : base (info, context) { + protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/src/mscorlib/src/System/Threading/ThreadLocal.cs b/src/mscorlib/src/System/Threading/ThreadLocal.cs index eedf6d0c81..64b8a60196 100644 --- a/src/mscorlib/src/System/Threading/ThreadLocal.cs +++ b/src/mscorlib/src/System/Threading/ThreadLocal.cs @@ -36,7 +36,6 @@ namespace System.Threading [DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}, Count={ValuesCountForDebugDisplay}")] public class ThreadLocal<T> : IDisposable { - // a delegate that returns the created value, if null the created value will be default(T) private Func<T> m_valueFactory; @@ -48,10 +47,10 @@ namespace System.Threading // the ThreadLocal<T> instance. // [ThreadStatic] - static LinkedSlotVolatile[] ts_slotArray; + private static LinkedSlotVolatile[] ts_slotArray; [ThreadStatic] - static FinalizationHelper ts_finalizationHelper; + private static FinalizationHelper ts_finalizationHelper; // Slot ID of this ThreadLocal<> instance. We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish // between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or @@ -276,7 +275,7 @@ namespace System.Threading && id < slotArray.Length // Is the table large enough? && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID? && m_initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)? - ) + ) { // We verified that the instance has not been disposed *after* we got a reference to the slot. // This guarantees that we have a reference to the right slot. @@ -324,7 +323,7 @@ namespace System.Threading int id = ~m_idComplement; if (id < 0) { - throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); + throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } Debugger.NotifyOfCrossThreadDependency(); @@ -341,7 +340,7 @@ namespace System.Threading if (IsValueCreated) { - throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_Value_RecursiveCallsToValue")); + throw new InvalidOperationException(SR.ThreadLocal_Value_RecursiveCallsToValue); } } @@ -357,7 +356,7 @@ namespace System.Threading // If the object has been disposed, id will be -1. if (id < 0) { - throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); + throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } // If a slot array has not been created on this thread yet, create it. @@ -395,7 +394,7 @@ namespace System.Threading if (!m_initialized) { - throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); + throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } slot.Value = value; @@ -417,7 +416,7 @@ namespace System.Threading // Dispose also executes under a lock. if (!m_initialized) { - throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); + throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } LinkedSlot firstRealNode = m_linkedSlot.Next; @@ -455,11 +454,11 @@ namespace System.Threading { if (!m_trackAllValues) { - throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_ValuesNotAvailable")); + throw new InvalidOperationException(SR.ThreadLocal_ValuesNotAvailable); } var list = GetValuesAsList(); // returns null if disposed - if (list == null) throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); + if (list == null) throw new ObjectDisposedException(SR.ThreadLocal_Disposed); return list; } } @@ -512,7 +511,7 @@ namespace System.Threading int id = ~m_idComplement; if (id < 0) { - throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed")); + throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } LinkedSlotVolatile[] slotArray = ts_slotArray; diff --git a/src/mscorlib/src/System/Threading/ThreadPool.cs b/src/mscorlib/src/System/Threading/ThreadPool.cs index adf0615819..0084050c43 100644 --- a/src/mscorlib/src/System/Threading/ThreadPool.cs +++ b/src/mscorlib/src/System/Threading/ThreadPool.cs @@ -33,8 +33,6 @@ namespace System.Threading public static readonly int processorCount = Environment.ProcessorCount; - public static readonly bool tpHosted = ThreadPool.IsThreadPoolHosted(); - public static volatile bool vmTpInitialized; public static bool enableWorkerTracking; @@ -124,7 +122,7 @@ namespace System.Threading private volatile int m_headIndex = START_INDEX; private volatile int m_tailIndex = START_INDEX; - private SpinLock m_foreignLock = new SpinLock(enableThreadOwnerTracking:false); + private SpinLock m_foreignLock = new SpinLock(enableThreadOwnerTracking: false); public void LocalPush(IThreadPoolWorkItem obj) { @@ -158,7 +156,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(useMemoryBarrier:true); + m_foreignLock.Exit(useMemoryBarrier: true); } } @@ -200,7 +198,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(useMemoryBarrier:false); + m_foreignLock.Exit(useMemoryBarrier: false); } } } @@ -254,7 +252,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(useMemoryBarrier:false); + m_foreignLock.Exit(useMemoryBarrier: false); } } } @@ -321,7 +319,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(useMemoryBarrier:false); + m_foreignLock.Exit(useMemoryBarrier: false); } } } @@ -366,7 +364,7 @@ namespace System.Threading finally { if (taken) - m_foreignLock.Exit(useMemoryBarrier:false); + m_foreignLock.Exit(useMemoryBarrier: false); } missedSteal = true; @@ -381,10 +379,10 @@ namespace System.Threading internal readonly ConcurrentQueue<IThreadPoolWorkItem> workItems = new ConcurrentQueue<IThreadPoolWorkItem>(); private volatile int numOutstandingThreadRequests = 0; - + public ThreadPoolWorkQueue() { - loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer); + loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool | FrameworkEventSource.Keywords.ThreadTransfer); } public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue() => @@ -401,7 +399,7 @@ namespace System.Threading int count = numOutstandingThreadRequests; while (count < ThreadPoolGlobals.processorCount) { - int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count+1, count); + int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count + 1, count); if (prev == count) { ThreadPool.RequestWorkerThread(); @@ -439,7 +437,7 @@ namespace System.Threading ThreadPoolWorkQueueThreadLocals tl = null; if (!forceGlobal) tl = ThreadPoolWorkQueueThreadLocals.threadLocals; - + if (null != tl) { tl.workStealingQueue.LocalPush(callback); @@ -511,7 +509,7 @@ namespace System.Threading workQueue.MarkThreadRequestSatisfied(); // Has the desire for logging changed since the last time we entered? - workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer); + workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool | FrameworkEventSource.Keywords.ThreadTransfer); // // Assume that we're going to need another thread if this one returns to the VM. We'll set this to @@ -603,7 +601,7 @@ namespace System.Threading // who waits for the task to complete. // workItem?.MarkAborted(tae); - + // // In this case, the VM is going to request another thread on our behalf. No need to do it twice. // @@ -707,7 +705,7 @@ namespace System.Threading private volatile int m_lock = 0; internal IntPtr GetHandle() => registeredWaitHandle; - + internal void SetHandle(IntPtr handle) { registeredWaitHandle = handle; @@ -731,7 +729,7 @@ namespace System.Threading } internal bool Unregister( - WaitHandle waitObject // object to be notified when all callbacks to delegates have completed + WaitHandle waitObject // object to be notified when all callbacks to delegates have completed ) { bool result = false; @@ -745,7 +743,7 @@ namespace System.Threading // lock(this) cannot be used reliably in Cer since thin lock could be // promoted to syncblock and that is not a guaranteed operation bool bLockTaken = false; - do + do { if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) { @@ -807,8 +805,8 @@ namespace System.Threading // This will result in a "leak" of sorts (since the handle will not be cleaned up) // but the process is exiting anyway. // - // During AD-unload, we don’t finalize live objects until all threads have been - // aborted out of the AD. Since these locked regions are CERs, we won’t abort them + // During AD-unload, we don�t finalize live objects until all threads have been + // aborted out of the AD. Since these locked regions are CERs, we won�t abort them // while the lock is held. So there should be no leak on AD-unload. // if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) @@ -841,9 +839,10 @@ namespace System.Threading private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject); } - public sealed class RegisteredWaitHandle : MarshalByRefObject { + public sealed class RegisteredWaitHandle : MarshalByRefObject + { private readonly RegisteredWaitHandleSafe internalRegisteredWait; - + internal RegisteredWaitHandle() { internalRegisteredWait = new RegisteredWaitHandleSafe(); @@ -851,23 +850,23 @@ namespace System.Threading internal void SetHandle(IntPtr handle) { - internalRegisteredWait.SetHandle(handle); + internalRegisteredWait.SetHandle(handle); } internal void SetWaitObject(WaitHandle waitObject) { - internalRegisteredWait.SetWaitObject(waitObject); + internalRegisteredWait.SetWaitObject(waitObject); } // This is the only public method on this class public bool Unregister( - WaitHandle waitObject // object to be notified when all callbacks to delegates have completed + WaitHandle waitObject // object to be notified when all callbacks to delegates have completed ) { return internalRegisteredWait.Unregister(waitObject); } } - + public delegate void WaitCallback(Object state); public delegate void WaitOrTimerCallback(Object state, bool timedOut); // signalled or timed out @@ -905,16 +904,16 @@ namespace System.Threading private readonly Object state; #if DEBUG - volatile int executed; + private volatile int executed; ~QueueUserWorkItemCallback() { Debug.Assert( - executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(), + executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(), "A QueueUserWorkItemCallback was never called!"); } - void MarkExecuted(bool aborted) + private void MarkExecuted(bool aborted) { GC.SuppressFinalize(this); Debug.Assert( @@ -933,7 +932,7 @@ namespace System.Threading void IThreadPoolWorkItem.ExecuteWorkItem() { #if DEBUG - MarkExecuted(aborted:false); + MarkExecuted(aborted: false); #endif // call directly if it is an unsafe call OR EC flow is suppressed if (context == null) @@ -953,7 +952,7 @@ namespace System.Threading #if DEBUG // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem. // This counts as being executed for our purposes. - MarkExecuted(aborted:true); + MarkExecuted(aborted: true); #endif } @@ -983,7 +982,7 @@ namespace System.Threading "A QueueUserWorkItemCallbackDefaultContext was never called!"); } - void MarkExecuted(bool aborted) + private void MarkExecuted(bool aborted) { GC.SuppressFinalize(this); Debug.Assert( @@ -1001,7 +1000,7 @@ namespace System.Threading void IThreadPoolWorkItem.ExecuteWorkItem() { #if DEBUG - MarkExecuted(aborted:false); + MarkExecuted(aborted: false); #endif ExecutionContext.Run(ExecutionContext.Default, ccb, this); } @@ -1011,7 +1010,7 @@ namespace System.Threading #if DEBUG // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem. // This counts as being executed for our purposes. - MarkExecuted(aborted:true); + MarkExecuted(aborted: true); #endif } @@ -1029,9 +1028,9 @@ namespace System.Threading internal class _ThreadPoolWaitOrTimerCallback { - WaitOrTimerCallback _waitOrTimerCallback; - ExecutionContext _executionContext; - Object _state; + private WaitOrTimerCallback _waitOrTimerCallback; + private ExecutionContext _executionContext; + private Object _state; private static readonly ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t); private static readonly ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f); @@ -1046,23 +1045,23 @@ namespace System.Threading _executionContext = ExecutionContext.Capture(); } } - + private static void WaitOrTimerCallback_Context_t(Object state) => - WaitOrTimerCallback_Context(state, timedOut:true); + WaitOrTimerCallback_Context(state, timedOut: true); private static void WaitOrTimerCallback_Context_f(Object state) => - WaitOrTimerCallback_Context(state, timedOut:false); + WaitOrTimerCallback_Context(state, timedOut: false); private static void WaitOrTimerCallback_Context(Object state, bool timedOut) { _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; helper._waitOrTimerCallback(helper._state, timedOut); } - + // call back helper internal static void PerformWaitOrTimerCallback(Object state, bool timedOut) { - _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; + _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; Debug.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!"); // call directly if it is an unsafe call OR EC flow is suppressed if (helper._executionContext == null) @@ -1074,15 +1073,14 @@ namespace System.Threading { ExecutionContext.Run(helper._executionContext, timedOut ? _ccbt : _ccbf, helper); } - } - + } } [CLSCompliant(false)] unsafe public delegate void IOCompletionCallback(uint errorCode, // Error code uint numBytes, // No. of bytes transferred NativeOverlapped* pOVERLAP // ptr to OVERLAP structure - ); + ); public static class ThreadPool { @@ -1112,42 +1110,42 @@ namespace System.Threading } [CLSCompliant(false)] - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC ) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true); + return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true); } [CLSCompliant(false)] - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC ) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false); + return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false); } private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, // NOTE: we do not allow other options that allow the callback to be queued as an APC + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, // NOTE: we do not allow other options that allow the callback to be queued as an APC ref StackCrawlMark stackMark, - bool compressStack + bool compressStack ) { RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle(); @@ -1160,7 +1158,7 @@ namespace System.Threading // this could occur if callback were to fire before SetWaitObject does its addref registeredWaitHandle.SetWaitObject(waitObject); IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject, - state, + state, millisecondsTimeOutInterval, executeOnlyOnce, registeredWaitHandle, @@ -1176,104 +1174,104 @@ namespace System.Threading } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - int millisecondsTimeOutInterval, - bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + int millisecondsTimeOutInterval, + bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC ) { if (millisecondsTimeOutInterval < -1) - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Contract.EndContractBlock(); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - int millisecondsTimeOutInterval, - bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + int millisecondsTimeOutInterval, + bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC ) { if (millisecondsTimeOutInterval < -1) - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Contract.EndContractBlock(); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, + WaitHandle waitObject, WaitOrTimerCallback callBack, - Object state, - long millisecondsTimeOutInterval, - bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC + Object state, + long millisecondsTimeOutInterval, + bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC ) { if (millisecondsTimeOutInterval < -1) - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Contract.EndContractBlock(); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException - WaitHandle waitObject, + WaitHandle waitObject, WaitOrTimerCallback callBack, - Object state, - long millisecondsTimeOutInterval, - bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC + Object state, + long millisecondsTimeOutInterval, + bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC ) { if (millisecondsTimeOutInterval < -1) - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Contract.EndContractBlock(); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - TimeSpan timeout, - bool executeOnlyOnce + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + TimeSpan timeout, + bool executeOnlyOnce ) { long tm = (long)timeout.TotalMilliseconds; if (tm < -1) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); - if (tm > (long) Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + if (tm > (long)Int32.MaxValue) + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,true); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, ref stackMark, true); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - Object state, - TimeSpan timeout, - bool executeOnlyOnce + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + TimeSpan timeout, + bool executeOnlyOnce ) { long tm = (long)timeout.TotalMilliseconds; if (tm < -1) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); - if (tm > (long) Int32.MaxValue) - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + if (tm > (long)Int32.MaxValue) + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, ref stackMark, false); } public static bool QueueUserWorkItem(WaitCallback callBack) => @@ -1428,7 +1426,7 @@ namespace System.Threading EnsureVMInitializedCore(); // separate out to help with inlining } } - + private static void EnsureVMInitializedCore() { ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking); @@ -1436,7 +1434,7 @@ namespace System.Threading } // Native methods: - + [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads); @@ -1468,22 +1466,19 @@ namespace System.Threading [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern void NotifyWorkItemProgressNative(); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern bool IsThreadPoolHosted(); - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] private static extern void InitializeVMTp(ref bool enableWorkerTracking); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern IntPtr RegisterWaitForSingleObjectNative( - WaitHandle waitHandle, - Object state, - uint timeOutInterval, - bool executeOnlyOnce, - RegisteredWaitHandle registeredWaitHandle, - ref StackCrawlMark stackMark, - bool compressStack + private static extern IntPtr RegisterWaitForSingleObjectNative( + WaitHandle waitHandle, + Object state, + uint timeOutInterval, + bool executeOnlyOnce, + RegisteredWaitHandle registeredWaitHandle, + ref StackCrawlMark stackMark, + bool compressStack ); @@ -1497,15 +1492,17 @@ namespace System.Threading { if (osHandle == null) throw new ArgumentNullException(nameof(osHandle)); - + bool ret = false; bool mustReleaseSafeHandle = false; RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { osHandle.DangerousAddRef(ref mustReleaseSafeHandle); ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle()); } - finally { + finally + { if (mustReleaseSafeHandle) osHandle.DangerousRelease(); } diff --git a/src/mscorlib/src/System/Threading/ThreadPriority.cs b/src/mscorlib/src/System/Threading/ThreadPriority.cs deleted file mode 100644 index 6303c2fd94..0000000000 --- a/src/mscorlib/src/System/Threading/ThreadPriority.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: Enums for the priorities of a Thread -** -** -=============================================================================*/ - -namespace System.Threading { - using System.Threading; - - [Serializable] - public enum ThreadPriority - { - /*========================================================================= - ** Constants for thread priorities. - =========================================================================*/ - Lowest = 0, - BelowNormal = 1, - Normal = 2, - AboveNormal = 3, - Highest = 4 - - } -} diff --git a/src/mscorlib/src/System/Threading/ThreadStart.cs b/src/mscorlib/src/System/Threading/ThreadStart.cs deleted file mode 100644 index e4beddcd75..0000000000 --- a/src/mscorlib/src/System/Threading/ThreadStart.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: This class is a Delegate which defines the start method -** for starting a thread. That method must match this delegate. -** -** -=============================================================================*/ - -namespace System.Threading { - using System.Threading; - - // Define the delegate - // NOTE: If you change the signature here, there is code in COMSynchronization - // that invokes this delegate in native. - public delegate void ThreadStart(); -} diff --git a/src/mscorlib/src/System/Threading/ThreadStartException.cs b/src/mscorlib/src/System/Threading/ThreadStartException.cs deleted file mode 100644 index 33fb460b3d..0000000000 --- a/src/mscorlib/src/System/Threading/ThreadStartException.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// - -namespace System.Threading -{ - using System; - using System.Runtime.Serialization; - using System.Runtime.InteropServices; - - [Serializable] - public sealed class ThreadStartException : SystemException - { - private ThreadStartException() - : base(Environment.GetResourceString("Arg_ThreadStartException")) - { - SetErrorCode(__HResults.COR_E_THREADSTART); - } - - private ThreadStartException(Exception reason) - : base(Environment.GetResourceString("Arg_ThreadStartException"), reason) - { - SetErrorCode(__HResults.COR_E_THREADSTART); - } - - //required for serialization - internal ThreadStartException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - } -} - - diff --git a/src/mscorlib/src/System/Threading/ThreadState.cs b/src/mscorlib/src/System/Threading/ThreadState.cs deleted file mode 100644 index 2d953f384a..0000000000 --- a/src/mscorlib/src/System/Threading/ThreadState.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: Enum to represent the different thread states -** -** -=============================================================================*/ - -namespace System.Threading { - -[Serializable] -[Flags] - public enum ThreadState - { - /*========================================================================= - ** Constants for thread states. - =========================================================================*/ - Running = 0, - StopRequested = 1, - SuspendRequested = 2, - Background = 4, - Unstarted = 8, - Stopped = 16, - WaitSleepJoin = 32, - Suspended = 64, - AbortRequested = 128, - Aborted = 256 - } -} diff --git a/src/mscorlib/src/System/Threading/ThreadStateException.cs b/src/mscorlib/src/System/Threading/ThreadStateException.cs deleted file mode 100644 index 97c03ce06c..0000000000 --- a/src/mscorlib/src/System/Threading/ThreadStateException.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -/*============================================================================= -** -** -** -** Purpose: An exception class to indicate that the Thread class is in an -** invalid state for the method. -** -** -=============================================================================*/ - -namespace System.Threading { - using System; - using System.Runtime.Serialization; - [Serializable] - public class ThreadStateException : SystemException { - public ThreadStateException() - : base(Environment.GetResourceString("Arg_ThreadStateException")) { - SetErrorCode(__HResults.COR_E_THREADSTATE); - } - - public ThreadStateException(String message) - : base(message) { - SetErrorCode(__HResults.COR_E_THREADSTATE); - } - - public ThreadStateException(String message, Exception innerException) - : base(message, innerException) { - SetErrorCode(__HResults.COR_E_THREADSTATE); - } - - protected ThreadStateException(SerializationInfo info, StreamingContext context) : base (info, context) { - } - } - -} diff --git a/src/mscorlib/src/System/Threading/Timeout.cs b/src/mscorlib/src/System/Threading/Timeout.cs deleted file mode 100644 index 80bdbccf4e..0000000000 --- a/src/mscorlib/src/System/Threading/Timeout.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Threading { - using System.Threading; - using System; - // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep - // etc) to indicate that no timeout should occur. - // - public static class Timeout - { - public static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, Timeout.Infinite); - - public const int Infinite = -1; - internal const uint UnsignedInfinite = unchecked((uint)-1); - } - -} diff --git a/src/mscorlib/src/System/Threading/Timer.cs b/src/mscorlib/src/System/Threading/Timer.cs index 93d2922799..960f815d64 100644 --- a/src/mscorlib/src/System/Threading/Timer.cs +++ b/src/mscorlib/src/System/Threading/Timer.cs @@ -4,7 +4,7 @@ // -namespace System.Threading +namespace System.Threading { using System; using System.Security; @@ -19,7 +19,7 @@ namespace System.Threading using Microsoft.Win32.SafeHandles; - + public delegate void TimerCallback(Object state); // @@ -43,12 +43,12 @@ namespace System.Threading // // Note that all instance methods of this class require that the caller hold a lock on TimerQueue.Instance. // - class TimerQueue + internal class TimerQueue { #region singleton pattern implementation // The one-and-only TimerQueue for the AppDomain. - static TimerQueue s_queue = new TimerQueue(); + private static TimerQueue s_queue = new TimerQueue(); public static TimerQueue Instance { @@ -100,7 +100,7 @@ namespace System.Threading // // We use a SafeHandle to ensure that the native timer is destroyed when the AppDomain is unloaded. // - class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid + private class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public AppDomainTimerSafeHandle() : base(true) @@ -113,11 +113,11 @@ namespace System.Threading } } - AppDomainTimerSafeHandle m_appDomainTimer; + private AppDomainTimerSafeHandle m_appDomainTimer; - bool m_isAppDomainTimerScheduled; - int m_currentAppDomainTimerStartTicks; - uint m_currentAppDomainTimerDuration; + private bool m_isAppDomainTimerScheduled; + private int m_currentAppDomainTimerStartTicks; + private uint m_currentAppDomainTimerDuration; private bool EnsureAppDomainTimerFiresBy(uint requestedDuration) { @@ -145,13 +145,13 @@ namespace System.Threading // If Pause is underway then do not schedule the timers // A later update during resume will re-schedule - if(m_pauseTicks != 0) + if (m_pauseTicks != 0) { Debug.Assert(!m_isAppDomainTimerScheduled); Debug.Assert(m_appDomainTimer == null); return true; } - + if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid) { Debug.Assert(!m_isAppDomainTimerScheduled); @@ -195,15 +195,15 @@ namespace System.Threading [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] - static extern AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime); + private static extern AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] - static extern bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime); + private static extern bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] - static extern bool DeleteAppDomainTimer(IntPtr handle); + private static extern bool DeleteAppDomainTimer(IntPtr handle); #endregion @@ -212,10 +212,10 @@ namespace System.Threading // // The list of timers // - TimerQueueTimer m_timers; + private TimerQueueTimer m_timers; - volatile int m_pauseTicks = 0; // Time when Pause was called + private volatile int m_pauseTicks = 0; // Time when Pause was called // @@ -386,7 +386,7 @@ namespace System.Threading // // A timer in our TimerQueue. // - sealed class TimerQueueTimer + internal sealed class TimerQueueTimer { // // All fields of this class are protected by a lock on TimerQueue.Instance. @@ -414,9 +414,9 @@ namespace System.Threading // // Info about the user's callback // - readonly TimerCallback m_timerCallback; - readonly Object m_state; - readonly ExecutionContext m_executionContext; + private readonly TimerCallback m_timerCallback; + private readonly Object m_state; + private readonly ExecutionContext m_executionContext; // @@ -426,9 +426,9 @@ namespace System.Threading // m_callbacksRunning. We set m_notifyWhenNoCallbacksRunning only when m_callbacksRunning // reaches zero. // - int m_callbacksRunning; - volatile bool m_canceled; - volatile WaitHandle m_notifyWhenNoCallbacksRunning; + private int m_callbacksRunning; + private volatile bool m_canceled; + private volatile WaitHandle m_notifyWhenNoCallbacksRunning; internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period) @@ -455,7 +455,7 @@ namespace System.Threading lock (TimerQueue.Instance) { if (m_canceled) - throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic")); + throw new ObjectDisposedException(null, SR.ObjectDisposed_Generic); // prevent ThreadAbort while updating state try { } @@ -612,17 +612,17 @@ namespace System.Threading // change, because any code that happened to be suppressing finalization of Timer objects would now // unwittingly be changing the lifetime of those timers. // - sealed class TimerHolder + internal sealed class TimerHolder { internal TimerQueueTimer m_timer; - - public TimerHolder(TimerQueueTimer timer) - { - m_timer = timer; + + public TimerHolder(TimerQueueTimer timer) + { + m_timer = timer; } - ~TimerHolder() - { + ~TimerHolder() + { // // If shutdown has started, another thread may be suspended while holding the timer lock. // So we can't safely close the timer. @@ -636,7 +636,7 @@ namespace System.Threading if (Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()) return; - m_timer.Close(); + m_timer.Close(); } public void Close() @@ -651,7 +651,6 @@ namespace System.Threading GC.SuppressFinalize(this); return result; } - } @@ -661,64 +660,64 @@ namespace System.Threading private TimerHolder m_timer; - public Timer(TimerCallback callback, - Object state, - int dueTime, - int period) + public Timer(TimerCallback callback, + Object state, + int dueTime, + int period) { if (dueTime < -1) - throw new ArgumentOutOfRangeException(nameof(dueTime), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); - if (period < -1 ) - throw new ArgumentOutOfRangeException(nameof(period), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + if (period < -1) + throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Contract.EndContractBlock(); - TimerSetup(callback,state,(UInt32)dueTime,(UInt32)period); + TimerSetup(callback, state, (UInt32)dueTime, (UInt32)period); } - public Timer(TimerCallback callback, - Object state, - TimeSpan dueTime, - TimeSpan period) - { + public Timer(TimerCallback callback, + Object state, + TimeSpan dueTime, + TimeSpan period) + { long dueTm = (long)dueTime.TotalMilliseconds; if (dueTm < -1) - throw new ArgumentOutOfRangeException(nameof(dueTm),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(dueTm), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (dueTm > MAX_SUPPORTED_TIMEOUT) - throw new ArgumentOutOfRangeException(nameof(dueTm),Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge")); + throw new ArgumentOutOfRangeException(nameof(dueTm), SR.ArgumentOutOfRange_TimeoutTooLarge); long periodTm = (long)period.TotalMilliseconds; if (periodTm < -1) - throw new ArgumentOutOfRangeException(nameof(periodTm),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(periodTm), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (periodTm > MAX_SUPPORTED_TIMEOUT) - throw new ArgumentOutOfRangeException(nameof(periodTm),Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge")); + throw new ArgumentOutOfRangeException(nameof(periodTm), SR.ArgumentOutOfRange_PeriodTooLarge); - TimerSetup(callback,state,(UInt32)dueTm,(UInt32)periodTm); + TimerSetup(callback, state, (UInt32)dueTm, (UInt32)periodTm); } [CLSCompliant(false)] - public Timer(TimerCallback callback, - Object state, - UInt32 dueTime, - UInt32 period) + public Timer(TimerCallback callback, + Object state, + UInt32 dueTime, + UInt32 period) { - TimerSetup(callback,state,dueTime,period); + TimerSetup(callback, state, dueTime, period); } - public Timer(TimerCallback callback, - Object state, - long dueTime, - long period) + public Timer(TimerCallback callback, + Object state, + long dueTime, + long period) { if (dueTime < -1) - throw new ArgumentOutOfRangeException(nameof(dueTime),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (period < -1) - throw new ArgumentOutOfRangeException(nameof(period),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (dueTime > MAX_SUPPORTED_TIMEOUT) - throw new ArgumentOutOfRangeException(nameof(dueTime),Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge")); + throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_TimeoutTooLarge); if (period > MAX_SUPPORTED_TIMEOUT) - throw new ArgumentOutOfRangeException(nameof(period),Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge")); + throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_PeriodTooLarge); Contract.EndContractBlock(); - TimerSetup(callback,state,(UInt32) dueTime, (UInt32) period); + TimerSetup(callback, state, (UInt32)dueTime, (UInt32)period); } public Timer(TimerCallback callback) @@ -727,12 +726,12 @@ namespace System.Threading int period = -1; // Change after a timer instance is created. This is to avoid the potential // for a timer to be fired before the returned value is assigned to the variable, // potentially causing the callback to reference a bogus value (if passing the timer to the callback). - + TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period); } private void TimerSetup(TimerCallback callback, - Object state, + Object state, UInt32 dueTime, UInt32 period) { @@ -742,13 +741,13 @@ namespace System.Threading m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period)); } - + public bool Change(int dueTime, int period) { - if (dueTime < -1 ) - throw new ArgumentOutOfRangeException(nameof(dueTime),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + if (dueTime < -1) + throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (period < -1) - throw new ArgumentOutOfRangeException(nameof(period),Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); Contract.EndContractBlock(); return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period); @@ -756,7 +755,7 @@ namespace System.Threading public bool Change(TimeSpan dueTime, TimeSpan period) { - return Change((long) dueTime.TotalMilliseconds, (long) period.TotalMilliseconds); + return Change((long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds); } [CLSCompliant(false)] @@ -767,28 +766,28 @@ namespace System.Threading public bool Change(long dueTime, long period) { - if (dueTime < -1 ) - throw new ArgumentOutOfRangeException(nameof(dueTime), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + if (dueTime < -1) + throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (period < -1) - throw new ArgumentOutOfRangeException(nameof(period), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (dueTime > MAX_SUPPORTED_TIMEOUT) - throw new ArgumentOutOfRangeException(nameof(dueTime), Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge")); + throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_TimeoutTooLarge); if (period > MAX_SUPPORTED_TIMEOUT) - throw new ArgumentOutOfRangeException(nameof(period), Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge")); + throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_PeriodTooLarge); Contract.EndContractBlock(); return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period); } - + public bool Dispose(WaitHandle notifyObject) { - if (notifyObject==null) + if (notifyObject == null) throw new ArgumentNullException(nameof(notifyObject)); Contract.EndContractBlock(); return m_timer.Close(notifyObject); } - + public void Dispose() { m_timer.Close(); diff --git a/src/mscorlib/src/System/Threading/Volatile.cs b/src/mscorlib/src/System/Threading/Volatile.cs index c94a69ab7b..6aac8d63cd 100644 --- a/src/mscorlib/src/System/Threading/Volatile.cs +++ b/src/mscorlib/src/System/Threading/Volatile.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. // + using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -33,7 +34,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -45,7 +46,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -56,7 +57,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -67,7 +68,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -79,7 +80,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -90,7 +91,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -102,7 +103,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -114,7 +115,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -126,7 +127,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } #else @@ -165,7 +166,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -177,7 +178,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -188,7 +189,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -211,7 +212,7 @@ namespace System.Threading // The VM will replace this with a more efficient implementation. // var value = location; - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); return value; } @@ -224,7 +225,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -235,7 +236,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -245,7 +246,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -255,7 +256,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -266,7 +267,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -276,7 +277,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -287,7 +288,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -298,7 +299,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -309,7 +310,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } #else @@ -353,7 +354,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -364,7 +365,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -374,7 +375,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } @@ -396,7 +397,7 @@ namespace System.Threading // // The VM will replace this with a more efficient implementation. // - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); location = value; } } diff --git a/src/mscorlib/src/System/Threading/WaitHandle.cs b/src/mscorlib/src/System/Threading/WaitHandle.cs index d4dcd710be..f3412d264f 100644 --- a/src/mscorlib/src/System/Threading/WaitHandle.cs +++ b/src/mscorlib/src/System/Threading/WaitHandle.cs @@ -26,8 +26,9 @@ namespace System.Threading using System.Diagnostics.CodeAnalysis; using Win32Native = Microsoft.Win32.Win32Native; - public abstract class WaitHandle : MarshalByRefObject, IDisposable { - public const int WaitTimeout = 0x102; + public abstract class WaitHandle : MarshalByRefObject, IDisposable + { + public const int WaitTimeout = 0x102; private const int MAX_WAITHANDLES = 64; @@ -57,7 +58,7 @@ namespace System.Threading NameInvalid } - protected WaitHandle() + protected WaitHandle() { Init(); } @@ -68,12 +69,12 @@ namespace System.Threading waitHandle = InvalidHandle; hasThreadAffinity = false; } - - + + [Obsolete("Use the SafeWaitHandle property instead.")] - public virtual IntPtr Handle + public virtual IntPtr Handle { - get { return safeWaitHandle == null ? InvalidHandle : safeWaitHandle.DangerousGetHandle();} + get { return safeWaitHandle == null ? InvalidHandle : safeWaitHandle.DangerousGetHandle(); } set { if (value == InvalidHandle) @@ -92,13 +93,13 @@ namespace System.Threading } else { - safeWaitHandle = new SafeWaitHandle(value, true); + safeWaitHandle = new SafeWaitHandle(value, true); } waitHandle = value; } } - public SafeWaitHandle SafeWaitHandle + public SafeWaitHandle SafeWaitHandle { get { @@ -111,23 +112,23 @@ namespace System.Threading set { - // Set safeWaitHandle and waitHandle in a CER so we won't take - // a thread abort between the statements and leave the wait - // handle in an invalid state. Note this routine is not thread - // safe however. - RuntimeHelpers.PrepareConstrainedRegions(); + // Set safeWaitHandle and waitHandle in a CER so we won't take + // a thread abort between the statements and leave the wait + // handle in an invalid state. Note this routine is not thread + // safe however. + RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { if (value == null) { - safeWaitHandle = null; - waitHandle = InvalidHandle; + safeWaitHandle = null; + waitHandle = InvalidHandle; } else { - safeWaitHandle = value; - waitHandle = safeWaitHandle.DangerousGetHandle(); + safeWaitHandle = value; + waitHandle = safeWaitHandle.DangerousGetHandle(); } } } @@ -147,41 +148,41 @@ namespace System.Threading safeWaitHandle = handle; waitHandle = handle.DangerousGetHandle(); } - - public virtual bool WaitOne (int millisecondsTimeout, bool exitContext) + + public virtual bool WaitOne(int millisecondsTimeout, bool exitContext) { if (millisecondsTimeout < -1) { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } Contract.EndContractBlock(); - return WaitOne((long)millisecondsTimeout,exitContext); + return WaitOne((long)millisecondsTimeout, exitContext); } - public virtual bool WaitOne (TimeSpan timeout, bool exitContext) + public virtual bool WaitOne(TimeSpan timeout, bool exitContext) { long tm = (long)timeout.TotalMilliseconds; - if (-1 > tm || (long) Int32.MaxValue < tm) + if (-1 > tm || (long)Int32.MaxValue < tm) { - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - return WaitOne(tm,exitContext); + return WaitOne(tm, exitContext); } - public virtual bool WaitOne () + public virtual bool WaitOne() { //Infinite Timeout - return WaitOne(-1,false); + return WaitOne(-1, false); } public virtual bool WaitOne(int millisecondsTimeout) { - return WaitOne(millisecondsTimeout, false); + return WaitOne(millisecondsTimeout, false); } public virtual bool WaitOne(TimeSpan timeout) { - return WaitOne(timeout, false); + return WaitOne(timeout, false); } [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")] @@ -194,28 +195,28 @@ namespace System.Threading { if (waitableSafeHandle == null) { - throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic")); + throw new ObjectDisposedException(null, SR.ObjectDisposed_Generic); } Contract.EndContractBlock(); int ret = WaitOneNative(waitableSafeHandle, (uint)millisecondsTimeout, hasThreadAffinity, exitContext); - if(AppDomainPauseManager.IsPaused) + if (AppDomainPauseManager.IsPaused) AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS(); - + if (ret == WAIT_ABANDONED) { ThrowAbandonedMutexException(); } return (ret != WaitTimeout); } - + internal bool WaitOneWithoutFAS() { // version of waitone without fast application switch (FAS) support // This is required to support the Wait which FAS needs (otherwise recursive dependency comes in) if (safeWaitHandle == null) { - throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic")); + throw new ObjectDisposedException(null, SR.ObjectDisposed_Generic); } Contract.EndContractBlock(); @@ -226,11 +227,11 @@ namespace System.Threading ThrowAbandonedMutexException(); } return (ret != WaitTimeout); - } + } [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext); - + /*======================================================================== ** Waits for signal from all the objects. ** timeout indicates how long to wait before the method returns. @@ -239,17 +240,17 @@ namespace System.Threading ** If exitContext is true then the synchronization domain for the context ** (if in a synchronized context) is exited before the wait and reacquired ========================================================================*/ - - [MethodImplAttribute(MethodImplOptions.InternalCall)] + + [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern int WaitMultiple(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext, bool WaitAll); public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) { if (waitHandles == null) { - throw new ArgumentNullException(nameof(waitHandles), Environment.GetResourceString("ArgumentNull_Waithandles")); + throw new ArgumentNullException(nameof(waitHandles), SR.ArgumentNull_Waithandles); } - if(waitHandles.Length == 0) + if (waitHandles.Length == 0) { // // Some history: in CLR 1.0 and 1.1, we threw ArgumentException in this case, which was correct. @@ -260,24 +261,24 @@ namespace System.Threading // in CoreCLR, and ArgumentNullException in the desktop CLR. This is ugly, but so is breaking // user code. // - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyWaithandleArray")); + throw new ArgumentException(SR.Argument_EmptyWaithandleArray); } if (waitHandles.Length > MAX_WAITHANDLES) { - throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles")); + throw new NotSupportedException(SR.NotSupported_MaxWaitHandles); } if (-1 > millisecondsTimeout) { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } Contract.EndContractBlock(); WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length]; - for (int i = 0; i < waitHandles.Length; i ++) + for (int i = 0; i < waitHandles.Length; i++) { WaitHandle waitHandle = waitHandles[i]; if (waitHandle == null) - throw new ArgumentNullException("waitHandles[" + i + "]", Environment.GetResourceString("ArgumentNull_ArrayElement")); + throw new ArgumentNullException("waitHandles[" + i + "]", SR.ArgumentNull_ArrayElement); internalWaitHandles[i] = waitHandle; } @@ -288,51 +289,51 @@ namespace System.Threading int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, true /* waitall*/ ); - if(AppDomainPauseManager.IsPaused) + if (AppDomainPauseManager.IsPaused) AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS(); - if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret)) + if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED + internalWaitHandles.Length > ret)) { //In the case of WaitAll the OS will only provide the // information that mutex was abandoned. // It won't tell us which one. So we can't set the Index or provide access to the Mutex ThrowAbandonedMutexException(); - } + } GC.KeepAlive(internalWaitHandles); return (ret != WaitTimeout); } public static bool WaitAll( - WaitHandle[] waitHandles, + WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) { long tm = (long)timeout.TotalMilliseconds; - if (-1 > tm || (long) Int32.MaxValue < tm) + if (-1 > tm || (long)Int32.MaxValue < tm) { - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - return WaitAll(waitHandles,(int)tm, exitContext); + return WaitAll(waitHandles, (int)tm, exitContext); } - + /*======================================================================== ** Shorthand for WaitAll with timeout = Timeout.Infinite and exitContext = true ========================================================================*/ public static bool WaitAll(WaitHandle[] waitHandles) { - return WaitAll(waitHandles, Timeout.Infinite, true); + return WaitAll(waitHandles, Timeout.Infinite, true); } public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout) { - return WaitAll(waitHandles, millisecondsTimeout, true); + return WaitAll(waitHandles, millisecondsTimeout, true); } public static bool WaitAll(WaitHandle[] waitHandles, TimeSpan timeout) { - return WaitAll(waitHandles, timeout, true); + return WaitAll(waitHandles, timeout, true); } @@ -344,33 +345,33 @@ namespace System.Threading ** If exitContext is true then the synchronization domain for the context ** (if in a synchronized context) is exited before the wait and reacquired ========================================================================*/ - + public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) { - if (waitHandles==null) + if (waitHandles == null) { - throw new ArgumentNullException(nameof(waitHandles), Environment.GetResourceString("ArgumentNull_Waithandles")); + throw new ArgumentNullException(nameof(waitHandles), SR.ArgumentNull_Waithandles); } - if(waitHandles.Length == 0) + if (waitHandles.Length == 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyWaithandleArray")); + throw new ArgumentException(SR.Argument_EmptyWaithandleArray); } if (MAX_WAITHANDLES < waitHandles.Length) { - throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles")); + throw new NotSupportedException(SR.NotSupported_MaxWaitHandles); } if (-1 > millisecondsTimeout) { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } Contract.EndContractBlock(); WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length]; - for (int i = 0; i < waitHandles.Length; i ++) + for (int i = 0; i < waitHandles.Length; i++) { WaitHandle waitHandle = waitHandles[i]; if (waitHandle == null) - throw new ArgumentNullException("waitHandles[" + i + "]", Environment.GetResourceString("ArgumentNull_ArrayElement")); + throw new ArgumentNullException("waitHandles[" + i + "]", SR.ArgumentNull_ArrayElement); internalWaitHandles[i] = waitHandle; } @@ -380,41 +381,41 @@ namespace System.Threading #endif int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, false /* waitany*/ ); - if(AppDomainPauseManager.IsPaused) + if (AppDomainPauseManager.IsPaused) AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS(); - if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret)) + if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED + internalWaitHandles.Length > ret)) { - int mutexIndex = ret -WAIT_ABANDONED; - if(0 <= mutexIndex && mutexIndex < internalWaitHandles.Length) + int mutexIndex = ret - WAIT_ABANDONED; + if (0 <= mutexIndex && mutexIndex < internalWaitHandles.Length) { - ThrowAbandonedMutexException(mutexIndex,internalWaitHandles[mutexIndex]); + ThrowAbandonedMutexException(mutexIndex, internalWaitHandles[mutexIndex]); } else { ThrowAbandonedMutexException(); } } - + GC.KeepAlive(internalWaitHandles); - return ret; + return ret; } public static int WaitAny( - WaitHandle[] waitHandles, + WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) { long tm = (long)timeout.TotalMilliseconds; - if (-1 > tm || (long) Int32.MaxValue < tm) + if (-1 > tm || (long)Int32.MaxValue < tm) { - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - return WaitAny(waitHandles,(int)tm, exitContext); + return WaitAny(waitHandles, (int)tm, exitContext); } public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout) { - return WaitAny(waitHandles, timeout, true); + return WaitAny(waitHandles, timeout, true); } @@ -428,7 +429,7 @@ namespace System.Threading public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout) { - return WaitAny(waitHandles, millisecondsTimeout, true); + return WaitAny(waitHandles, millisecondsTimeout, true); } /*================================================= @@ -436,20 +437,20 @@ namespace System.Threading == SignalAndWait == ==================================================*/ -#if !PLATFORM_UNIX - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern int SignalAndWaitOne(SafeWaitHandle waitHandleToSignal,SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout, - bool hasThreadAffinity, bool exitContext); -#endif // !PLATFORM_UNIX +#if PLATFORM_WINDOWS + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int SignalAndWaitOne(SafeWaitHandle waitHandleToSignal, SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout, + bool hasThreadAffinity, bool exitContext); +#endif // PLATFORM_WINDOWS public static bool SignalAndWait( WaitHandle toSignal, WaitHandle toWaitOn) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(); + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // https://github.com/dotnet/coreclr/issues/10441 #else - return SignalAndWait(toSignal,toWaitOn,-1,false); + return SignalAndWait(toSignal, toWaitOn, -1, false); #endif } @@ -460,14 +461,14 @@ namespace System.Threading bool exitContext) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(); + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // https://github.com/dotnet/coreclr/issues/10441 #else long tm = (long)timeout.TotalMilliseconds; - if (-1 > tm || (long) Int32.MaxValue < tm) + if (-1 > tm || (long)Int32.MaxValue < tm) { - throw new ArgumentOutOfRangeException(nameof(timeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - return SignalAndWait(toSignal,toWaitOn,(int)tm,exitContext); + return SignalAndWait(toSignal, toWaitOn, (int)tm, exitContext); #endif } @@ -479,38 +480,38 @@ namespace System.Threading bool exitContext) { #if PLATFORM_UNIX - throw new PlatformNotSupportedException(); + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // https://github.com/dotnet/coreclr/issues/10441 #else - if(null == toSignal) + if (null == toSignal) { throw new ArgumentNullException(nameof(toSignal)); } - if(null == toWaitOn) + if (null == toWaitOn) { throw new ArgumentNullException(nameof(toWaitOn)); } if (-1 > millisecondsTimeout) { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1")); + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } Contract.EndContractBlock(); //NOTE: This API is not supporting Pause/Resume as it's not exposed in CoreCLR (not in WP or SL) - int ret = SignalAndWaitOne(toSignal.safeWaitHandle,toWaitOn.safeWaitHandle,millisecondsTimeout, - toWaitOn.hasThreadAffinity,exitContext); + int ret = SignalAndWaitOne(toSignal.safeWaitHandle, toWaitOn.safeWaitHandle, millisecondsTimeout, + toWaitOn.hasThreadAffinity, exitContext); - if(WAIT_ABANDONED == ret) + if (WAIT_ABANDONED == ret) { ThrowAbandonedMutexException(); } - if(ERROR_TOO_MANY_POSTS == ret) + if (ERROR_TOO_MANY_POSTS == ret) { - throw new InvalidOperationException(Environment.GetResourceString("Threading.WaitHandleTooManyPosts")); + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); } //Object was signaled - if(WAIT_OBJECT_0 == ret) + if (WAIT_OBJECT_0 == ret) { return true; } @@ -535,7 +536,7 @@ namespace System.Threading Dispose(true); GC.SuppressFinalize(this); } - + protected virtual void Dispose(bool explicitDisposing) { if (safeWaitHandle != null) diff --git a/src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs deleted file mode 100644 index 68445a78d9..0000000000 --- a/src/mscorlib/src/System/Threading/WaitHandleCannotBeOpenedException.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -namespace System.Threading -{ - using System; - using System.Runtime.Serialization; - using System.Runtime.InteropServices; - - [Serializable] - [ComVisibleAttribute(false)] - - public class WaitHandleCannotBeOpenedException : ApplicationException { - public WaitHandleCannotBeOpenedException() : base(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException")) - { - SetErrorCode(__HResults.COR_E_WAITHANDLECANNOTBEOPENED); - } - - public WaitHandleCannotBeOpenedException(String message) : base(message) - { - SetErrorCode(__HResults.COR_E_WAITHANDLECANNOTBEOPENED); - } - - public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException) - { - SetErrorCode(__HResults.COR_E_WAITHANDLECANNOTBEOPENED); - } - - protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base (info, context) - { - } - } -} - |