-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API Proposal]: Volatile Read/Write for the ImmutableArray<T> struct #67014
Comments
Tagging subscribers to this area: @dotnet/area-system-collections Issue DetailsBackground and motivationThe API Proposalnamespace System.Threading
{
public static class Volatile
{
public static ImmutableArray<T> Read<T>(ref ImmutableArray<T> location);
public static void Write<T>(ref ImmutableArray<T> location, ImmutableArray<T> value);
}
} API UsageExample of using the private ImmutableArray<Item> _items = ImmutableArray<Item>.Empty;
public void AddItem(Item item)
{
ImmutableInterlocked.Update(ref _items, (x, y) => x.Add(y), item);
}
public void ProcessSnapshot()
{
// Ensure that we don't enumerate a stale snapshot of the _items array.
foreach (var item in Volatile.Read(ref _items))
{
//...
}
}
public void Reset(ImmutableArray<Item> items)
{
Volatile.Write(ref _items, items);
} Alternative DesignsThese two methods could also be placed inside the namespace System.Collections.Immutable
{
public static class ImmutableInterlocked
{
public static ImmutableArray<T> VolatileRead<T>(ref ImmutableArray<T> location);
public static void VolatileWrite<T>(ref ImmutableArray<T> location, ImmutableArray<T> value);
}
} ...or inside a new static class namespace System.Collections.Immutable
{
public static class ImmutableVolatile
{
public static ImmutableArray<T> Read<T>(ref ImmutableArray<T> location);
public static void Write<T>(ref ImmutableArray<T> location, ImmutableArray<T> value);
}
} RisksNone that I am aware of.
|
Volatile doesn't really make any guarantees about how stale the data is, just about reordering of instructions around that access.
We've had a slow trickle of requests for APIs related to |
@stephentoub thanks for the feedback!
What API would you suggest for the purpose of avoiding reading stale data, without acquiring a |
The There are quite a few types that could receive volatile-like treatment but it's not supported in-box. For example, Clearly, we don't want to add APIs for all of this to Maybe the new guidance can be: Add APIs for cases that are worthwhile. For everything else, the user makes a workaround using unsafe casting. |
@GSPP do you mean something like this? DateTime VolatileRead(ref DateTime location)
{
ref ulong unsafeLocation = ref Unsafe.As<DateTime, ulong>(ref location);
ulong result = Volatile.Read(ref unsafeLocation);
return Unsafe.As<ulong, DateTime>(ref result);
}
void VolatileWrite(ref DateTime location, DateTime value)
{
ref ulong unsafeLocation = ref Unsafe.As<DateTime, ulong>(ref location);
ref ulong unsafeValue = ref Unsafe.As<DateTime, ulong>(ref value);
Volatile.Write(ref unsafeLocation, unsafeValue);
} Is this guaranteed to be forward-compatible, or it's as brittle as using reflection? My argument for going the extra mile and offering this functionality out of the box for the |
Yes, @theodorzoulias. This is what I had in mind, and I have posted code to this effect in Your request is to add a specialized API for For In practical terms, I'd personally be comfortable doing this, though. Altering |
@GSPP regarding where to put this API, personally I don't have a strong opinion. It could be located anywhere, in an obscure class in the
It doesn't specify on what types these volatile memory operations are performed. Personally if I ever needed to perform a volatile read on a |
@stephentoub do you mean something like this API? public static class CollectionsMarshal
{
public static ref T[] GetArrayRef<T> (ref ImmutableArray<T> immutableArray);
} I tried to make a static ImmutableArray<T> VolatileRead<T>(ref ImmutableArray<T> location)
{
ref T[] array = ref CollectionsMarshal.GetArrayRef(ref location);
array = Volatile.Read(ref array);
return location;
} Overwriting the |
Maybe this is the correct way to do it: static ImmutableArray<T> VolatileRead<T>(ref ImmutableArray<T> location)
{
ImmutableArray<T> result = default;
ref T[] locationArray = ref CollectionsMarshal.GetArrayRef(ref location);
ref T[] resultArray = ref CollectionsMarshal.GetArrayRef(ref result);
resultArray = Volatile.Read(ref locationArray);
return result;
} Not thrilled with having to maintain such code in my codebase though. Not easy to reason about its correctness. |
I had a need for this API again recently. I used the public static ImmutableArray<T> VolatileRead<T>(ref ImmutableArray<T> location)
{
T[] array = Unsafe.As<ImmutableArray<T>, T[]>(ref location);
T[] result = Volatile.Read(ref array);
return Unsafe.As<T[], ImmutableArray<T>>(ref result);
} I would like to ask if this implementation is correct. Does it read the immutable array, while also preventing the reordering of instructions that appears after this method in the code, to be moved before this method? |
This will read the ImmutableArray with standard volatile read guarantees. Note, the version you posted was incorrect as it read the memory on the first line normally, and then volatile read the variable on the stack (which achieves nothing). public static ImmutableArray<T> VolatileRead<T>(ref ImmutableArray<T> location)
{
ref T[] array = ref Unsafe.As<ImmutableArray<T>, T[]>(ref location);
T[] result = Volatile.Read(ref array);
return Unsafe.As<T[], ImmutableArray<T>>(ref result);
} |
Background and motivation
The
ImmutableInterlocked
class contains static methods that allow to perform lock-free operations on theImmutableArray<T>
struct, like theUpdate
and theInterlockedCompareExchange
methods, but there is no API available that allows to perform a direct volatile read on this struct. As far as I can tell, the lack of this API is probably an oversight, rather than intentional. So I am proposing the introduction of aVolatile.Read
overload for this struct, along with a correspondingVolatile.Write
overload for consistency and completeness. The availability of this API will facilitate the use of theImmutableArray<T>
struct in low-lock multithreading scenarios, without needing to resort to workarounds and hacks, like the ones mentioned in this related issue.API Proposal
API Usage
Example of using the
Volatile.Read
andVolatile.Write
methods as part of a thread-safe class with membersAddItem
,ProcessSnapshot
andReset
:Alternative Designs
These two methods could also be placed inside the
ImmutableInterlocked
class:...or inside a new static class
ImmutableVolatile
, located in theSystem.Collections.Immutable
namespace:Risks
None that I am aware of.
The text was updated successfully, but these errors were encountered: