Browse Source

fix #527

Use []uint32 in bitset instead of []uint64, because it's harder to guarantee
64-bit alignment of []uint64 than I had realized:

https://go101.org/article/memory-layout.html
tags/v1.1.0-rc1
Shivaram Lingamneni 5 years ago
parent
commit
9fe65223db
5 changed files with 37 additions and 38 deletions
  1. 2
    2
      gencapdefs.py
  2. 1
    1
      irc/caps/set.go
  3. 8
    9
      irc/modes/modes.go
  4. 25
    25
      irc/utils/bitset.go
  5. 1
    1
      irc/utils/bitset_test.go

+ 2
- 2
gencapdefs.py View File

@@ -204,8 +204,8 @@ package caps
204 204
 
205 205
 
206 206
     numCapabs = len(CAPDEFS)
207
-    bitsetLen = numCapabs // 64
208
-    if numCapabs % 64 > 0:
207
+    bitsetLen = numCapabs // 32
208
+    if numCapabs % 32 > 0:
209 209
         bitsetLen += 1
210 210
     print ("""
211 211
 const (

+ 1
- 1
irc/caps/set.go View File

@@ -11,7 +11,7 @@ import (
11 11
 )
12 12
 
13 13
 // Set holds a set of enabled capabilities.
14
-type Set [bitsetLen]uint64
14
+type Set [bitsetLen]uint32
15 15
 
16 16
 // NewSet returns a new Set, with the given capabilities enabled.
17 17
 func NewSet(capabs ...Capability) *Set {

+ 8
- 9
irc/modes/modes.go View File

@@ -7,7 +7,6 @@ package modes
7 7
 
8 8
 import (
9 9
 	"strings"
10
-	"sync/atomic"
11 10
 
12 11
 	"github.com/oragono/oragono/irc/utils"
13 12
 )
@@ -318,12 +317,13 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
318 317
 }
319 318
 
320 319
 // ModeSet holds a set of modes.
321
-type ModeSet [1]uint64
320
+type ModeSet [2]uint32
322 321
 
323 322
 // valid modes go from 65 ('A') to 122 ('z'), making at most 58 possible values;
324
-// subtract 65 from the mode value and use that bit of the uint64 to represent it
323
+// subtract 65 from the mode value and use that bit of the uint32 to represent it
325 324
 const (
326
-	minMode = 65 // 'A'
325
+	minMode = 65  // 'A'
326
+	maxMode = 122 // 'z'
327 327
 )
328 328
 
329 329
 // returns a pointer to a new ModeSet
@@ -357,11 +357,10 @@ func (set *ModeSet) AllModes() (result []Mode) {
357 357
 		return
358 358
 	}
359 359
 
360
-	block := atomic.LoadUint64(&set[0])
361
-	var i uint
362
-	for i = 0; i < 64; i++ {
363
-		if block&(1<<i) != 0 {
364
-			result = append(result, Mode(minMode+i))
360
+	var i Mode
361
+	for i = minMode; i <= maxMode; i++ {
362
+		if set.HasMode(i) {
363
+			result = append(result, i)
365 364
 		}
366 365
 	}
367 366
 	return

+ 25
- 25
irc/utils/bitset.go View File

@@ -5,28 +5,28 @@ package utils
5 5
 
6 6
 import "sync/atomic"
7 7
 
8
-// Library functions for lock-free bitsets, typically (constant-sized) arrays of uint64.
8
+// Library functions for lock-free bitsets, typically (constant-sized) arrays of uint32.
9 9
 // For examples of use, see caps.Set and modes.ModeSet; the array has to be converted to a
10 10
 // slice to use these functions.
11 11
 
12 12
 // BitsetGet returns whether a given bit of the bitset is set.
13
-func BitsetGet(set []uint64, position uint) bool {
14
-	idx := position / 64
15
-	bit := position % 64
16
-	block := atomic.LoadUint64(&set[idx])
13
+func BitsetGet(set []uint32, position uint) bool {
14
+	idx := position / 32
15
+	bit := position % 32
16
+	block := atomic.LoadUint32(&set[idx])
17 17
 	return (block & (1 << bit)) != 0
18 18
 }
19 19
 
20 20
 // BitsetSet sets a given bit of the bitset to 0 or 1, returning whether it changed.
21
-func BitsetSet(set []uint64, position uint, on bool) (changed bool) {
22
-	idx := position / 64
23
-	bit := position % 64
21
+func BitsetSet(set []uint32, position uint, on bool) (changed bool) {
22
+	idx := position / 32
23
+	bit := position % 32
24 24
 	addr := &set[idx]
25
-	var mask uint64
25
+	var mask uint32
26 26
 	mask = 1 << bit
27 27
 	for {
28
-		current := atomic.LoadUint64(addr)
29
-		var desired uint64
28
+		current := atomic.LoadUint32(addr)
29
+		var desired uint32
30 30
 		if on {
31 31
 			desired = current | mask
32 32
 		} else {
@@ -34,7 +34,7 @@ func BitsetSet(set []uint64, position uint, on bool) (changed bool) {
34 34
 		}
35 35
 		if current == desired {
36 36
 			return false
37
-		} else if atomic.CompareAndSwapUint64(addr, current, desired) {
37
+		} else if atomic.CompareAndSwapUint32(addr, current, desired) {
38 38
 			return true
39 39
 		}
40 40
 	}
@@ -44,9 +44,9 @@ func BitsetSet(set []uint64, position uint, on bool) (changed bool) {
44 44
 // This has false positives under concurrent modification (i.e., it can return true
45 45
 // even though w.r.t. the sequence of atomic modifications, there was no point at
46 46
 // which the bitset was completely empty), but that's not how we're using this method.
47
-func BitsetEmpty(set []uint64) (empty bool) {
47
+func BitsetEmpty(set []uint32) (empty bool) {
48 48
 	for i := 0; i < len(set); i++ {
49
-		if atomic.LoadUint64(&set[i]) != 0 {
49
+		if atomic.LoadUint32(&set[i]) != 0 {
50 50
 			return false
51 51
 		}
52 52
 	}
@@ -56,14 +56,14 @@ func BitsetEmpty(set []uint64) (empty bool) {
56 56
 // BitsetUnion modifies `set` to be the union of `set` and `other`.
57 57
 // This has race conditions in that we don't necessarily get a single
58 58
 // consistent view of `other` across word boundaries.
59
-func BitsetUnion(set []uint64, other []uint64) {
59
+func BitsetUnion(set []uint32, other []uint32) {
60 60
 	for i := 0; i < len(set); i++ {
61 61
 		for {
62 62
 			ourAddr := &set[i]
63
-			ourBlock := atomic.LoadUint64(ourAddr)
64
-			otherBlock := atomic.LoadUint64(&other[i])
63
+			ourBlock := atomic.LoadUint32(ourAddr)
64
+			otherBlock := atomic.LoadUint32(&other[i])
65 65
 			newBlock := ourBlock | otherBlock
66
-			if atomic.CompareAndSwapUint64(ourAddr, ourBlock, newBlock) {
66
+			if atomic.CompareAndSwapUint32(ourAddr, ourBlock, newBlock) {
67 67
 				break
68 68
 			}
69 69
 		}
@@ -72,23 +72,23 @@ func BitsetUnion(set []uint64, other []uint64) {
72 72
 
73 73
 // BitsetCopy copies the contents of `other` over `set`.
74 74
 // Similar caveats about race conditions as with `BitsetUnion` apply.
75
-func BitsetCopy(set []uint64, other []uint64) {
75
+func BitsetCopy(set []uint32, other []uint32) {
76 76
 	for i := 0; i < len(set); i++ {
77
-		data := atomic.LoadUint64(&other[i])
78
-		atomic.StoreUint64(&set[i], data)
77
+		data := atomic.LoadUint32(&other[i])
78
+		atomic.StoreUint32(&set[i], data)
79 79
 	}
80 80
 }
81 81
 
82 82
 // BitsetSubtract modifies `set` to subtract the contents of `other`.
83 83
 // Similar caveats about race conditions as with `BitsetUnion` apply.
84
-func BitsetSubtract(set []uint64, other []uint64) {
84
+func BitsetSubtract(set []uint32, other []uint32) {
85 85
 	for i := 0; i < len(set); i++ {
86 86
 		for {
87 87
 			ourAddr := &set[i]
88
-			ourBlock := atomic.LoadUint64(ourAddr)
89
-			otherBlock := atomic.LoadUint64(&other[i])
88
+			ourBlock := atomic.LoadUint32(ourAddr)
89
+			otherBlock := atomic.LoadUint32(&other[i])
90 90
 			newBlock := ourBlock & (^otherBlock)
91
-			if atomic.CompareAndSwapUint64(ourAddr, ourBlock, newBlock) {
91
+			if atomic.CompareAndSwapUint32(ourAddr, ourBlock, newBlock) {
92 92
 				break
93 93
 			}
94 94
 		}

+ 1
- 1
irc/utils/bitset_test.go View File

@@ -5,7 +5,7 @@ package utils
5 5
 
6 6
 import "testing"
7 7
 
8
-type testBitset [2]uint64
8
+type testBitset [4]uint32
9 9
 
10 10
 func TestSets(t *testing.T) {
11 11
 	var t1 testBitset

Loading…
Cancel
Save