You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bitset.go 3.4KB

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