Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs')
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs120
1 files changed, 67 insertions, 53 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs b/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs
index 3531265be..12d650243 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/AsyncLocal.cs
@@ -61,7 +61,7 @@ namespace System.Threading
get
{
object obj = ExecutionContext.GetLocalValue(this);
- return (obj == null) ? default(T) : (T)obj;
+ return (obj == null) ? default : (T)obj;
}
set
{
@@ -72,8 +72,8 @@ namespace System.Threading
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;
+ T previousValue = previousValueObj == null ? default : (T)previousValueObj;
+ T currentValue = currentValueObj == null ? default : (T)currentValueObj;
m_valueChangedHandler(new AsyncLocalValueChangedArgs<T>(previousValue, currentValue, contextChanged));
}
}
@@ -86,19 +86,18 @@ namespace System.Threading
void OnValueChanged(object previousValue, object currentValue, bool contextChanged);
}
- public struct AsyncLocalValueChangedArgs<T>
+ public readonly struct AsyncLocalValueChangedArgs<T>
{
- public T PreviousValue { get; private set; }
- public T CurrentValue { get; private set; }
+ public T PreviousValue { get; }
+ public T CurrentValue { get; }
//
// 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; }
+ public bool ThreadContextChanged { get; }
internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged)
- : this()
{
PreviousValue = previousValue;
CurrentValue = currentValue;
@@ -114,7 +113,7 @@ namespace System.Threading
internal interface IAsyncLocalValueMap
{
bool TryGetValue(IAsyncLocal key, out object value);
- IAsyncLocalValueMap Set(IAsyncLocal key, object value);
+ IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent);
}
//
@@ -124,14 +123,31 @@ namespace System.Threading
{
public static IAsyncLocalValueMap Empty { get; } = new EmptyAsyncLocalValueMap();
+ public static bool IsEmpty(IAsyncLocalValueMap asyncLocalValueMap)
+ {
+ Debug.Assert(asyncLocalValueMap != null);
+ Debug.Assert(asyncLocalValueMap == Empty || asyncLocalValueMap.GetType() != typeof(EmptyAsyncLocalValueMap));
+
+ return asyncLocalValueMap == Empty;
+ }
+
+ public static IAsyncLocalValueMap Create(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
+ {
+ // If the value isn't null or a null value may not be treated as nonexistent, then create a new one-element map
+ // to store the key/value pair. Otherwise, use the empty map.
+ return value != null || !treatNullValueAsNonexistent ?
+ new OneElementAsyncLocalValueMap(key, value) :
+ Empty;
+ }
+
// Instance without any key/value pairs. Used as a singleton/
- internal sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap
+ private sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap
{
- public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
{
- // 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 ?
+ // If the value isn't null or a null value may not be treated as nonexistent, then create a new one-element map
+ // to store the key/value pair. Otherwise, use the empty map.
+ return value != null || !treatNullValueAsNonexistent ?
new OneElementAsyncLocalValueMap(key, value) :
(IAsyncLocalValueMap)this;
}
@@ -144,7 +160,7 @@ namespace System.Threading
}
// Instance with one key/value pair.
- internal sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap
+ private sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap
{
private readonly IAsyncLocal _key1;
private readonly object _value1;
@@ -154,21 +170,20 @@ namespace System.Threading
_key1 = key; _value1 = value;
}
- public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
{
- if (value != null)
+ if (value != null || !treatNullValueAsNonexistent)
{
- // 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.
+ // 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.
+ // 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;
@@ -202,13 +217,12 @@ namespace System.Threading
_key2 = key2; _value2 = value2;
}
- public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
{
- if (value != null)
+ if (value != null || !treatNullValueAsNonexistent)
{
- // 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.
+ // 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) :
@@ -216,8 +230,8 @@ namespace System.Threading
}
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.
+ // 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) :
@@ -258,12 +272,12 @@ namespace System.Threading
_key3 = key3; _value3 = value3;
}
- public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
{
- if (value != null)
+ if (value != null || !treatNullValueAsNonexistent)
{
- // 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 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);
@@ -279,8 +293,8 @@ namespace System.Threading
}
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.
+ // 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) :
@@ -332,17 +346,18 @@ namespace System.Threading
_keyValues[index] = new KeyValuePair<IAsyncLocal, object>(key, value);
}
- public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
{
// 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)
+ // The key is in the map.
+ if (value != null || !treatNullValueAsNonexistent)
{
+ // Create a new map of the same size that has all of the same pairs, with this new key/value pair
+ // overwriting the old.
var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length);
Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length);
multi._keyValues[i] = new KeyValuePair<IAsyncLocal, object>(key, value);
@@ -350,8 +365,8 @@ namespace System.Threading
}
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.
+ // 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) :
@@ -360,8 +375,8 @@ namespace System.Threading
}
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.
+ // 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);
@@ -372,9 +387,9 @@ namespace System.Threading
// 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)
+ if (value == null && treatNullValueAsNonexistent)
{
+ // We can simply return this same map, as there's nothing to add or remove.
return this;
}
@@ -418,14 +433,14 @@ namespace System.Threading
{
public ManyElementAsyncLocalValueMap(int capacity) : base(capacity) { }
- public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value, bool treatNullValueAsNonexistent)
{
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)
+ if (value != null || !treatNullValueAsNonexistent)
{
var map = new ManyElementAsyncLocalValueMap(count + (containsKey ? 0 : 1));
foreach (KeyValuePair<IAsyncLocal, object> pair in this)
@@ -436,15 +451,14 @@ namespace System.Threading
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.
+ // Otherwise, the value is null and a null value may be treated as nonexistent. 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.
+ // downgrade to the multi 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)
{
@@ -475,8 +489,8 @@ namespace System.Threading
}
}
- // We were storing null, but the key wasn't in the map, so there's nothing to change.
- // Just return this instance.
+ // We were storing null and a null value may be treated as nonexistent, but the key wasn't in the map, so
+ // there's nothing to change. Just return this instance.
return this;
}
}