-
Notifications
You must be signed in to change notification settings - Fork 17.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
sync/atomic: add Uint64Pair #61236
Comments
I think the proposal here is // CompareAndSwapUint128 executes the compare-and-swap operation for a 128-bit
// integer, represented as a pair of 64-bit integers.
func CompareAndSwapUint128(addr *uint64, old1, old2, new1, new2 uint64) (swapped bool) But see also #9455. If we adopt that, then this version of the function would no longer make sense. So perhaps the proposal here should be // CompareAndSwapUint64Pair executes the compare-and-swap operation for a 128-bit
// integer, represented as a pair of 64-bit integers.
func CompareAndSwapUint64Pair(addr *uint64, old1, old2, new1, new2 uint64) (swapped bool) Presumably hardware does not have a 128-bit CAS operation could implement this using something like the lock table in runtime/internal/atomic/atomic_arm.go. |
Perhaps
On x86 at least, the hardware instruction |
We could have |
There is a Go package that uses array of uint64, [2]uint64 in its api: Generally, the data used is not a 128-bit number but a composite of often two 64-bit numbers that are changed atomically as a pair |
FTR this was just out of necessity (due to the lack of uint128) more than anything else. It's true that both intel and arm define their DWCAS instructions as operating on pairs of 64-bit values, but that does not automatically mean we should expose that in our APIs (especially given the alignment requirements that are uncharacteristic of |
Which systems would support this function? |
This proposal has been added to the active column of the proposals project |
Natively: I'm aware of at least amd64 with CMPXCHG16B (AMD64v2 and above) and aarch64/ARMv8.1 with LSE Others will have to be emulated (I just use a mutex in my library in this case, but other implementations are possible e.g. for aarch64 there are DW LL/SC instructions that were available before the native DWCAS) |
We don't want to have an API that's unavailable or always fails on 32-bit systems, so what do we do on 32-bit systems? Perhaps the right path forward is to define a type wrapper like atomic.Uint64 and not the un-wrapped one.
But there would not be corresponding top-level functions, to avoid direct access to the atomic uint64 values. Edit: Added Swap. |
How about add something like MarkableReference and TimeStampedReference in java,just extend the val :
This is useful for lock free data structure: |
@someview, I am not sure I understand what you mean by "just extend the val" here. It sounds like you mean assume that oldPtr and newPtr both point to a pair of uint64 values, but if we do that, then there will be a problem on 32-bit systems (which must use locks to implement cas128) that the individual uint64s will be available for ordinary reads or even atomic.LoadUint64, neither of which will be correct since they will not use the lock too. The point of the Uint64Pair type is to hide the actual uint64 values behind an abstraction that only allows using them in this specific way. |
This may be double-word CAS,not atomic128 with 32-bit system |
It sounds like people are generally happy with #61236 (comment). |
We probably want |
Added Swap, thanks. |
I suspect quite a few uses of a DWCAS would be to deal with pointers... Should we consider also |
I suggest that |
Based on the discussion above, this proposal seems like a likely accept. |
The generic version seems untenable to me. How do I put one of those in a struct?
is pretty awkward. And what if one type is uintptr?
What goes in the
is very awkward. We don't have answers for these questions in the current generics, nor in the future plans. If we don't try to generify, it seems like we need three types: Uint64Pair, PointerPair[T1, T2], and Uint64AndPointer[T]. |
This should maybe be UintptrAndPointer[T], considering 32 bit architectures. |
This proposal has been added to the active column of the proposals project |
Right now it looks like the proposal is to add three types with this API:
And maybe also UintptrAndPointer. The arrays are gone because they only apply in Uint64Pair. It's not clear if this is the road we want to go down, but I don't see another one. |
This is the API we think we'd use. Is there anyone who has a use case that isn't addressed by this? And is there anyone who has a use case that is addressed by this? (We want to make sure we're not adding something no one needs.) Thanks! |
PointerPair with CAS makes it straightforward to implement an efficient lock-free linked list with arbitrary elements, which directly addresses a use case I have. Uint64AndPointer does the same for uint64 elements, which is probably still useful. It's a bit of a shame that having them as separate types means that the same type can't provide both varieties of linked list, but I'm not sure how common the latter is anyway. If @haraldrudell or someone else could check that my reading of wCQ in #61236 (comment) is correct, that would confirm another use case for Uint64AndPointer. |
Have all remaining concerns about this proposal been addressed? Add to sync/atomic:
|
Note that like with atomic.Int64, the compiler and toolchain will provide the necessary alignment automatically, so there's no need to worry about it or mention it here. |
This seems like a likely accept but we may well not get to it until Go 1.23 at this point. |
Based on the discussion above, this proposal seems like a likely accept. Add to sync/atomic:
|
No change in consensus, so accepted. 🎉 Add to sync/atomic:
|
In https://pkg.go.dev/sync/atomic#pkg-note-BUG , it is said
So far, we don't have 16-byte aligned data structure in Go. Will we provide the capability to cast something else to *Uint64Pair, for example the address of a struct to *Uint64Pair? More specifically, can we guarantee below
|
@Zheng-Xu as commented in #61236 (comment) , |
I would be happy to work on this for the 1.23 cycle, given that I have previous experience implementing internal and sync/atomic APIs. I'll keep it assigned unless someone else is interested. |
I suggest CAS2 is implemented in the atomic package similar to atomic.align64, operational for hardware that supports it.
possibly also:
—
darwin-arm64 darwin-amd64 linux-amd64
I think also atomic generics could do with another polish
Like this AtomicMax that is more flexible in what underlying integer types and named types can be used: https://github.com/haraldrudell/parl/blob/main/atomic-max.go
The text was updated successfully, but these errors were encountered: