summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/Threading
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System/Threading')
-rw-r--r--src/mscorlib/shared/System/Threading/AbandonedMutexException.cs77
-rw-r--r--src/mscorlib/shared/System/Threading/ApartmentState.cs16
-rw-r--r--src/mscorlib/shared/System/Threading/AsyncLocal.cs484
-rw-r--r--src/mscorlib/shared/System/Threading/AutoResetEvent.cs12
-rw-r--r--src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs116
-rw-r--r--src/mscorlib/shared/System/Threading/EventResetMode.cs22
-rw-r--r--src/mscorlib/shared/System/Threading/ExecutionContext.cs370
-rw-r--r--src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs44
-rw-r--r--src/mscorlib/shared/System/Threading/LockRecursionException.cs29
-rw-r--r--src/mscorlib/shared/System/Threading/ManualResetEvent.cs12
-rw-r--r--src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs18
-rw-r--r--src/mscorlib/shared/System/Threading/SemaphoreFullException.cs29
-rw-r--r--src/mscorlib/shared/System/Threading/SendOrPostCallback.cs8
-rw-r--r--src/mscorlib/shared/System/Threading/SynchronizationLockException.cs44
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs89
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs48
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs77
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadAbortException.cs36
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadPriority.cs18
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStart.cs18
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStartException.cs29
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadState.cs24
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStateException.cs45
-rw-r--r--src/mscorlib/shared/System/Threading/Timeout.cs20
-rw-r--r--src/mscorlib/shared/System/Threading/TimeoutHelper.cs54
-rw-r--r--src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs31
26 files changed, 1770 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
new file mode 100644
index 0000000000..8056a3b330
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
@@ -0,0 +1,77 @@
+// 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.
+////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Threading;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class AbandonedMutexException : SystemException
+ {
+ private int _mutexIndex = -1;
+ private Mutex _mutex = null;
+
+ public AbandonedMutexException()
+ : base(SR.Threading_AbandonedMutexException)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ }
+
+ public AbandonedMutexException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ }
+
+ public AbandonedMutexException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ }
+
+ public AbandonedMutexException(int location, WaitHandle handle)
+ : base(SR.Threading_AbandonedMutexException)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ SetupException(location, handle);
+ }
+
+ public AbandonedMutexException(String message, int location, WaitHandle handle)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ SetupException(location, handle);
+ }
+
+ public AbandonedMutexException(String message, Exception inner, int location, WaitHandle handle)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ SetupException(location, handle);
+ }
+
+ protected AbandonedMutexException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ private void SetupException(int location, WaitHandle handle)
+ {
+ _mutexIndex = location;
+ if (handle != null)
+ _mutex = handle as Mutex;
+ }
+
+ public Mutex Mutex => _mutex;
+ public int MutexIndex => _mutexIndex;
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ApartmentState.cs b/src/mscorlib/shared/System/Threading/ApartmentState.cs
new file mode 100644
index 0000000000..47c1677cb5
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ApartmentState.cs
@@ -0,0 +1,16 @@
+// 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
+{
+ public enum ApartmentState
+ {
+ /*=========================================================================
+ ** Constants for thread apartment states.
+ =========================================================================*/
+ STA = 0,
+ MTA = 1,
+ Unknown = 2
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/AsyncLocal.cs b/src/mscorlib/shared/System/Threading/AsyncLocal.cs
new file mode 100644
index 0000000000..59c8fb3c88
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/AsyncLocal.cs
@@ -0,0 +1,484 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+
+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.
+ //
+ internal 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/shared/System/Threading/AutoResetEvent.cs b/src/mscorlib/shared/System/Threading/AutoResetEvent.cs
new file mode 100644
index 0000000000..8320d7ad5a
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/AutoResetEvent.cs
@@ -0,0 +1,12 @@
+// 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
+{
+ public sealed class AutoResetEvent : EventWaitHandle
+ {
+ public AutoResetEvent(bool initialState) : base(initialState, EventResetMode.AutoReset) { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
new file mode 100644
index 0000000000..89380fee60
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
@@ -0,0 +1,116 @@
+// 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;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Provides callbacks to objects whose lifetime is managed by <see cref="DeferredDisposableLifetime{T}"/>.
+ /// </summary>
+ internal interface IDeferredDisposable
+ {
+ /// <summary>
+ /// Called when the object's refcount reaches zero.
+ /// </summary>
+ /// <param name="disposed">
+ /// Indicates whether the object has been disposed.
+ /// </param>
+ /// <remarks>
+ /// If the refount reaches zero before the object is disposed, this method will be called with
+ /// <paramref name="disposed"/> set to false. If the object is then disposed, this method will be
+ /// called again, with <paramref name="disposed"/> set to true. If the refcount reaches zero
+ /// after the object has already been disposed, this will be called a single time, with
+ /// <paramref name="disposed"/> set to true.
+ /// </remarks>
+ void OnFinalRelease(bool disposed);
+ }
+
+ /// <summary>
+ /// Manages the lifetime of an object which implements IDisposable, but which must defer the actual
+ /// cleanup of state until all existing uses of the object are complete.
+ /// </summary>
+ /// <typeparam name="T">The type of object whose lifetime will be managed.</typeparam>
+ /// <remarks>
+ /// This type maintains a reference count, and tracks whether the object has been disposed. When
+ /// Callbacks are made to <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when the refcount
+ /// reaches zero. Objects that need to defer cleanup until they have been disposed *and* they have
+ /// no more references can do so in <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when
+ /// 'disposed' is true.
+ /// </remarks>
+ internal struct DeferredDisposableLifetime<T> where T : class, IDeferredDisposable
+ {
+ //
+ // _count is positive until Dispose is called, after which it's (-1 - refcount).
+ //
+ private int _count;
+
+ public bool AddRef(T obj)
+ {
+ while (true)
+ {
+ int oldCount = Volatile.Read(ref _count);
+
+ // Have we been disposed?
+ if (oldCount < 0)
+ throw new ObjectDisposedException(typeof(T).ToString());
+
+ int newCount = checked(oldCount + 1);
+
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ return true;
+ }
+ }
+
+ public void Release(T obj)
+ {
+ while (true)
+ {
+ int oldCount = Volatile.Read(ref _count);
+ if (oldCount > 0)
+ {
+ // We haven't been disposed. Decrement _count.
+ int newCount = oldCount - 1;
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ {
+ if (newCount == 0)
+ obj.OnFinalRelease(disposed: false);
+ return;
+ }
+ }
+ else
+ {
+ Debug.Assert(oldCount != 0 && oldCount != -1);
+
+ // We've been disposed. Increment _count.
+ int newCount = oldCount + 1;
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ {
+ if (newCount == -1)
+ obj.OnFinalRelease(disposed: true);
+ return;
+ }
+ }
+ }
+ }
+
+ public void Dispose(T obj)
+ {
+ while (true)
+ {
+ int oldCount = Volatile.Read(ref _count);
+ if (oldCount < 0)
+ return; // already disposed
+
+ int newCount = -1 - oldCount;
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ {
+ if (newCount == -1)
+ obj.OnFinalRelease(disposed: true);
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/EventResetMode.cs b/src/mscorlib/shared/System/Threading/EventResetMode.cs
new file mode 100644
index 0000000000..7aac0f51eb
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/EventResetMode.cs
@@ -0,0 +1,22 @@
+// 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
+{
+ public enum EventResetMode
+ {
+ AutoReset = 0,
+ ManualReset = 1
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ExecutionContext.cs b/src/mscorlib/shared/System/Threading/ExecutionContext.cs
new file mode 100644
index 0000000000..67857e9b11
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ExecutionContext.cs
@@ -0,0 +1,370 @@
+// 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
+**
+**
+===========================================================*/
+
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.ExceptionServices;
+using System.Runtime.Serialization;
+
+using Thread = Internal.Runtime.Augments.RuntimeThread;
+
+namespace System.Threading
+{
+ 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(SR.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(SR.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;
+ }
+
+ public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state)
+ {
+ if (executionContext == null)
+ throw new InvalidOperationException(SR.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);
+ }
+ }
+
+ internal static void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw)
+ {
+ Debug.Assert(currentThread == Thread.CurrentThread);
+
+ ecsw.m_ec = currentThread.ExecutionContext;
+ ecsw.m_sc = currentThread.SynchronizationContext;
+ }
+
+ 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(
+ SR.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(SR.InvalidOperation_CannotUseAFCMultiple);
+ }
+ if (Thread.CurrentThread != _thread)
+ {
+ throw new InvalidOperationException(SR.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(SR.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/shared/System/Threading/LazyThreadSafetyMode.cs b/src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs
new file mode 100644
index 0000000000..2d13f23762
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs
@@ -0,0 +1,44 @@
+// 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 set of lightweight static helpers for lazy initialization.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+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
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/LockRecursionException.cs b/src/mscorlib/shared/System/Threading/LockRecursionException.cs
new file mode 100644
index 0000000000..2f296cb14e
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/LockRecursionException.cs
@@ -0,0 +1,29 @@
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class LockRecursionException : System.Exception
+ {
+ public LockRecursionException()
+ {
+ }
+
+ public LockRecursionException(string message)
+ : base(message)
+ {
+ }
+
+ public LockRecursionException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected LockRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ManualResetEvent.cs b/src/mscorlib/shared/System/Threading/ManualResetEvent.cs
new file mode 100644
index 0000000000..4b8d61f960
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ManualResetEvent.cs
@@ -0,0 +1,12 @@
+// 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
+{
+ public sealed class ManualResetEvent : EventWaitHandle
+ {
+ public ManualResetEvent(bool initialState) : base(initialState, EventResetMode.ManualReset) { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs b/src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs
new file mode 100644
index 0000000000..c0f29e8e80
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs
@@ -0,0 +1,18 @@
+// 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
+{
+ public delegate void ParameterizedThreadStart(object obj);
+}
diff --git a/src/mscorlib/shared/System/Threading/SemaphoreFullException.cs b/src/mscorlib/shared/System/Threading/SemaphoreFullException.cs
new file mode 100644
index 0000000000..19ac19d6e0
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/SemaphoreFullException.cs
@@ -0,0 +1,29 @@
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class SemaphoreFullException : SystemException
+ {
+ public SemaphoreFullException() : base(SR.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/shared/System/Threading/SendOrPostCallback.cs b/src/mscorlib/shared/System/Threading/SendOrPostCallback.cs
new file mode 100644
index 0000000000..6692d35ab2
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/SendOrPostCallback.cs
@@ -0,0 +1,8 @@
+// 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
+{
+ public delegate void SendOrPostCallback(Object state);
+}
diff --git a/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
new file mode 100644
index 0000000000..120577fdcf
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
@@ -0,0 +1,44 @@
+// 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.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class SynchronizationLockException : SystemException
+ {
+ public SynchronizationLockException()
+ : base(SR.Arg_SynchronizationLockException)
+ {
+ HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ }
+
+ public SynchronizationLockException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ }
+
+ public SynchronizationLockException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ }
+
+ protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs
new file mode 100644
index 0000000000..d7690d4c7c
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs
@@ -0,0 +1,89 @@
+// 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(SR.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(SR.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/shared/System/Threading/Tasks/TaskExtensions.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs
new file mode 100644
index 0000000000..1098299517
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs
@@ -0,0 +1,48 @@
+// 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.Tasks
+{
+ /// <summary>Provides a set of static methods for working with specific kinds of <see cref="Task"/> instances.</summary>
+ public static class TaskExtensions
+ {
+ /// <summary>Creates a proxy <see cref="Task"/> that represents the asynchronous operation of a <see cref="Task{Task}"/>.</summary>
+ /// <param name="task">The <see cref="Task{Task}"/> to unwrap.</param>
+ /// <returns>A <see cref="Task"/> that represents the asynchronous operation of the provided <see cref="Task{Task}"/>.</returns>
+ public static Task Unwrap(this Task<Task> task)
+ {
+ if (task == null)
+ {
+ throw new ArgumentNullException(nameof(task));
+ }
+
+ // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise,
+ // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner
+ // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise.
+ return
+ !task.IsRanToCompletion ? Task.CreateUnwrapPromise<VoidTaskResult>(task, lookForOce: false) :
+ task.Result ??
+ Task.FromCanceled(new CancellationToken(true));
+ }
+
+ /// <summary>Creates a proxy <see cref="Task{TResult}"/> that represents the asynchronous operation of a <see cref="Task{Task{TResult}}"/>.</summary>
+ /// <param name="task">The <see cref="Task{Task{TResult}}"/> to unwrap.</param>
+ /// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous operation of the provided <see cref="Task{Task{TResult}}"/>.</returns>
+ public static Task<TResult> Unwrap<TResult>(this Task<Task<TResult>> task)
+ {
+ if (task == null)
+ {
+ throw new ArgumentNullException(nameof(task));
+ }
+
+ // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise,
+ // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner
+ // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise.
+ return
+ !task.IsRanToCompletion ? Task.CreateUnwrapPromise<TResult>(task, lookForOce: false) :
+ task.Result ??
+ Task.FromCanceled<TResult>(new CancellationToken(true));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs
new file mode 100644
index 0000000000..148b6300ef
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs
@@ -0,0 +1,77 @@
+// 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(SR.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(SR.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/shared/System/Threading/ThreadAbortException.cs b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
new file mode 100644
index 0000000000..e693e7192f
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
@@ -0,0 +1,36 @@
+// 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.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public sealed class ThreadAbortException : SystemException
+ {
+ private ThreadAbortException()
+ {
+ HResult = __HResults.COR_E_THREADABORTED;
+ }
+
+ internal ThreadAbortException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public object ExceptionState => null;
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadPriority.cs b/src/mscorlib/shared/System/Threading/ThreadPriority.cs
new file mode 100644
index 0000000000..3b34bd5eac
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadPriority.cs
@@ -0,0 +1,18 @@
+// 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
+{
+ public enum ThreadPriority
+ {
+ /*=========================================================================
+ ** Constants for thread priorities.
+ =========================================================================*/
+ Lowest = 0,
+ BelowNormal = 1,
+ Normal = 2,
+ AboveNormal = 3,
+ Highest = 4
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadStart.cs b/src/mscorlib/shared/System/Threading/ThreadStart.cs
new file mode 100644
index 0000000000..5532539fc7
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadStart.cs
@@ -0,0 +1,18 @@
+// 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
+{
+ public delegate void ThreadStart();
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadStartException.cs b/src/mscorlib/shared/System/Threading/ThreadStartException.cs
new file mode 100644
index 0000000000..2ff77bc5fd
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadStartException.cs
@@ -0,0 +1,29 @@
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public sealed class ThreadStartException : SystemException
+ {
+ internal ThreadStartException()
+ : base(SR.Arg_ThreadStartException)
+ {
+ HResult = __HResults.COR_E_THREADSTART;
+ }
+
+ internal ThreadStartException(Exception reason)
+ : base(SR.Arg_ThreadStartException, reason)
+ {
+ HResult = __HResults.COR_E_THREADSTART;
+ }
+
+ internal ThreadStartException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadState.cs b/src/mscorlib/shared/System/Threading/ThreadState.cs
new file mode 100644
index 0000000000..4bf3b5184d
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadState.cs
@@ -0,0 +1,24 @@
+// 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
+{
+ [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/shared/System/Threading/ThreadStateException.cs b/src/mscorlib/shared/System/Threading/ThreadStateException.cs
new file mode 100644
index 0000000000..33bc8baee6
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadStateException.cs
@@ -0,0 +1,45 @@
+// 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.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class ThreadStateException : SystemException
+ {
+ public ThreadStateException()
+ : base(SR.Arg_ThreadStateException)
+ {
+ HResult = __HResults.COR_E_THREADSTATE;
+ }
+
+ public ThreadStateException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_THREADSTATE;
+ }
+
+ public ThreadStateException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_THREADSTATE;
+ }
+
+ protected ThreadStateException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Timeout.cs b/src/mscorlib/shared/System/Threading/Timeout.cs
new file mode 100644
index 0000000000..df1ea5f2bc
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Timeout.cs
@@ -0,0 +1,20 @@
+// 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.Threading;
+using System;
+
+namespace System.Threading
+{
+ // 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/shared/System/Threading/TimeoutHelper.cs b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
new file mode 100644
index 0000000000..c66c9add92
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
@@ -0,0 +1,54 @@
+// 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;
+
+namespace System.Threading
+{
+ /// <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/shared/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
new file mode 100644
index 0000000000..e44946a669
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
@@ -0,0 +1,31 @@
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class WaitHandleCannotBeOpenedException : ApplicationException
+ {
+ public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotBeOpenedException)
+ {
+ HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ }
+
+ public WaitHandleCannotBeOpenedException(String message) : base(message)
+ {
+ HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ }
+
+ public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ }
+
+ protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}