Description
Update, Aug 16 2023: Current proposal at #61395 (comment).
Original related proposal: #31748
Use case: we have types with methods that set a value. These methods manipulate a bitset indicating that the value was set, which is used (e.g.) for data serialization. Users of this API know to use a lock to manage concurrently reading/writing the same field, but they are allowed to concurrently write to different fields. Given that the bitsets are logically shared between different fields, we must manipulate them atomically. Currently that takes the form of a CAS loop:
type MyStruct struct{
x int32
y int32
// ...
present uint32 // Contains "present" bits for 32 fields.
}
func (m *MyStruct) SetX(v int32) {
m.x = v
setPresent(&m.present[0], 1)
}
func (m *MyStruct) SetY(v int32) {
m.y = v
setPresent(&m.present[0], 2)
}
func setPresent(part *uint32, num uint32) {
for {
old := atomic.LoadUint32(part)
swapped := atomic.CompareAndSwapUint32(part, old, old|(1<<(num%32)))
if swapped {
return
}
// Yield and then try the swap again.
runtime.Gosched()
}
}
// similar for clearPresent, but with AND.
But, on x86-64, there are atomic versions of AND/OR that do this in one go, as mentioned in #31748. Using this would not only make the setters faster, it would likely also allow inlining them: setPresent
is too complex to inline.