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.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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. // BitsetEmpty returns whether the bitset is empty.
  45. // This has false positives under concurrent modification (i.e., it can return true
  46. // even though w.r.t. the sequence of atomic modifications, there was no point at
  47. // which the bitset was completely empty), but that's not how we're using this method.
  48. func BitsetEmpty(set []uint32) (empty bool) {
  49. for i := 0; i < len(set); i++ {
  50. if atomic.LoadUint32(&set[i]) != 0 {
  51. return false
  52. }
  53. }
  54. return true
  55. }
  56. // BitsetUnion modifies `set` to be the union of `set` and `other`.
  57. // This has race conditions in that we don't necessarily get a single
  58. // consistent view of `other` across word boundaries.
  59. func BitsetUnion(set []uint32, other []uint32) {
  60. for i := 0; i < len(set); i++ {
  61. for {
  62. ourAddr := &set[i]
  63. ourBlock := atomic.LoadUint32(ourAddr)
  64. otherBlock := atomic.LoadUint32(&other[i])
  65. newBlock := ourBlock | otherBlock
  66. if atomic.CompareAndSwapUint32(ourAddr, ourBlock, newBlock) {
  67. break
  68. }
  69. }
  70. }
  71. }
  72. // BitsetCopy copies the contents of `other` over `set`.
  73. // Similar caveats about race conditions as with `BitsetUnion` apply.
  74. func BitsetCopy(set []uint32, other []uint32) {
  75. for i := 0; i < len(set); i++ {
  76. data := atomic.LoadUint32(&other[i])
  77. atomic.StoreUint32(&set[i], data)
  78. }
  79. }
  80. // BitsetCopyLocal copies the contents of `other` over `set`,
  81. // without synchronizing the writes to `set`.
  82. func BitsetCopyLocal(set []uint32, other []uint32) {
  83. for i := 0; i < len(set); i++ {
  84. data := atomic.LoadUint32(&other[i])
  85. set[i] = data
  86. }
  87. }
  88. // BitsetSubtract modifies `set` to subtract the contents of `other`.
  89. // Similar caveats about race conditions as with `BitsetUnion` apply.
  90. func BitsetSubtract(set []uint32, other []uint32) {
  91. for i := 0; i < len(set); i++ {
  92. for {
  93. ourAddr := &set[i]
  94. ourBlock := atomic.LoadUint32(ourAddr)
  95. otherBlock := atomic.LoadUint32(&other[i])
  96. newBlock := ourBlock & (^otherBlock)
  97. if atomic.CompareAndSwapUint32(ourAddr, ourBlock, newBlock) {
  98. break
  99. }
  100. }
  101. }
  102. }