-
Notifications
You must be signed in to change notification settings - Fork 11
/
atomic.go
64 lines (53 loc) · 2.31 KB
/
atomic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package bitmap
import (
"sync/atomic"
"unsafe"
)
var oobPanic = "SetAtomic not allowed on a bitmapSlice of cap() < 4"
// SetAtomic is similar to Set except that it performs the operation atomically.
func SetAtomic(bitmap []byte, targetBit int, targetValue bool) {
ov := (*[1]uint32)(unsafe.Pointer(&bitmap[targetBit/32]))[:]
SetAtomicUint32(ov, targetBit%32, targetValue)
}
// SetAtomic is similar to Set except that it performs the operation atomically.
// It needs a bitmapSlice where the capacity is at least 4 bytes.
func _SetAtomic(bitmapSlice []byte, targetBit int, targetValue bool) {
targetByteIndex := targetBit / 8
targetBitIndex := targetBit % 8
targetOffset := 0
// SetAtomic needs to modify 4 bytes of data so we panic when the slice
// doesn't have a capacity of at least 4 bytes.
if cap(bitmapSlice) < 4 {
panic(oobPanic)
}
// Calculate the Offset of the targetByte inside the 4-byte atomic batch.
// This is needed to ensure that atomic operations can happen as long as
// the bitmapSlice equals 4 bytes or more.
if cap(bitmapSlice) < targetByteIndex+3 {
targetOffset = cap(bitmapSlice) - targetByteIndex
}
// This gets a pointer to the memory of 4 bytes inside the bitmapSlice.
// It stores this pointer as an *uint32 so that it can be used to
// execute sync.atomic operations.
targetBytePointer := (*uint32)(unsafe.Pointer(&bitmapSlice[targetByteIndex-targetOffset]))
for {
// localValue is a copy of the uint32 value at *targetBytePointer.
// It's used to check whether the targetBit must be updated,
// and if so, to construct the new value for targetBytePointer.
localValue := atomic.LoadUint32(targetBytePointer)
// This "neutralizes" the uint32 conversion by getting a pointer to the
// 4-byte array stored undereneath the uint32.
targetByteCopyPointer := (*[4]byte)(unsafe.Pointer(&localValue))
// Work is done when targetBit is already set to targetValue.
if GetBit(targetByteCopyPointer[targetOffset], targetBitIndex) == targetValue {
return
}
// Modify the targetBit and update memory so that the targetBit is the only bit
// that has been modified in the batch.
referenceValue := localValue
SetBitRef(&targetByteCopyPointer[targetOffset], targetBitIndex, targetValue)
if atomic.CompareAndSwapUint32(targetBytePointer, referenceValue, localValue) {
break
}
}
}