-
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
Remove Interlocked.{Compare}Exchange generic constraint #65184
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @mangod9 Issue DetailsIn principle, Motivating for this request is a repeated need to update a "state" field in a class. The state is most cleanly tracked as an enum. Since this API does not exist yet, I use integers and constants. That's of course viable but it would be cleaner to be able to use enums. A workaround is to use There's a recent discussion about small integer types that is pertinent to this issue: This is related because enums can have small integer types. If we don't allow small types, then the generic constraint So maybe we can add enum overloads iff we add small integer types. The alternative would be to add it even without that and fail at runtime. Design issues:
For visitors looking for a workaround: static T InterlockedCompareExchange32<T>(ref T location, T value, T comparand) where T : unmanaged
{
var result = Interlocked.CompareExchange(ref Unsafe.As<T, int>(ref location), Unsafe.As<T, int>(ref value), Unsafe.As<T, int>(ref comparand));
return Unsafe.As<int, T>(ref result);
}
<table>
<tr>
<th align="left">Author:</th>
<td>GSPP</td>
</tr>
<tr>
<th align="left">Assignees:</th>
<td>-</td>
</tr>
<tr>
<th align="left">Labels:</th>
<td>
`api-suggestion`, `area-System.Threading`, `untriaged`
</td>
</tr>
<tr>
<th align="left">Milestone:</th>
<td>-</td>
</tr>
</table>
</details> |
Is it even possible to implement this API effectively without making it a special case in the compiler? And sure the shorter integer types would have to be supported first anyway. |
Right, the implementation of this API would have to be special-cased as runtime intrinsic. It is not something that can be done efficiently in pure C# today. |
This results in the desired codegen though, we've been using this for a while in our codebase. using System;
using System.Runtime.CompilerServices;
using System.Threading;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static unsafe T CompareExchange<T>(ref T location, T value, T comparand)
where T: unmanaged
{
switch (sizeof(T))
{
case sizeof(int):
var x = Interlocked.CompareExchange(ref Unsafe.As<T, int>(ref location), Unsafe.As<T, int>(ref value), Unsafe.As<T, int>(ref comparand));
return Unsafe.As<int,T>(ref x);
default:
ThrowUnsupported();
return default;
}
static void ThrowUnsupported() => throw new NotSupportedException();
} |
AFAIR at this moment all there's needed is available in pure C#.
Since CompareExchange does bitwise equality, this will break for non bitwise equatable types like floats for example. |
I think we should do this not by adding new APIs, but by removing the class constraint on the existing |
namespace System.Threading;
public static class Interlocked
{
public static T CompareExchange<T> (ref T location1, T value, T comparand);
public static T Exchange<T>(ref T location1, T value);
} |
Addressed by #104558 |
EDITED by @stephentoub on 7/9/2024 to update the proposal:
Remove the
T : class
constraint from the existing Interlocked.{Compare}Exchange generic methods. They will be usable with not only reference type Ts but also primitive and enum Ts. Unsupported Ts will result in a NotSupportedException.(We could also consider removing / making internal the new public overloads added in .NET 9 for byte/sbyte/ushort/short and just have the generic version to handle those.)
In principle,
Interlocked.CompareExchange
can be made to work on enums. Enums are just integers in disguise.Motivating for this request is a repeated need to update a "state" field in a class. The state is most cleanly tracked as an enum. Since this API does not exist yet, I use integers and constants. That's of course viable but it would be cleaner to be able to use enums.
A workaround is to use
Unsafe
. There is nothing wrong with that in principle. It could be "nicer", though.There's a recent discussion about small integer types that is pertinent to this issue:
This is related because enums can have small integer types. If we don't allow small types, then the generic constraint
: enum
would not ensure runtime safety.So maybe we can add enum overloads iff we add small integer types. The alternative would be to add it even without that and fail at runtime.
Design issues:
CompareExchange
. I can tell from the existing discussion that the team only wants to add API scope if there is demand. In principle, allInterlocked
APIs could be upgraded.unmanaged
type of at most 64 bits in size?a. This would mean that there could be a runtime error depending on
T
. IfT : unmanaged
, then the JIT can ensure that this validation is without runtime overhead because each struct type generates specialized code.b. Could this result in unaligned access?
ImmutableArray
. Most likely, this is not going to happen, but I'm mentioning this for completeness.For visitors looking for a workaround:
The text was updated successfully, but these errors were encountered: