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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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 uint64.
  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. // BitsetInitialize initializes a bitset.
  9. func BitsetInitialize(set []uint64) {
  10. // XXX re-zero the bitset using atomic stores. it's unclear whether this is required,
  11. // however, golang issue #5045 suggests that you shouldn't mix atomic operations
  12. // with non-atomic operations (such as the runtime's automatic zero-initialization) on
  13. // the same word
  14. for i := 0; i < len(set); i++ {
  15. atomic.StoreUint64(&set[i], 0)
  16. }
  17. }
  18. // BitsetGet returns whether a given bit of the bitset is set.
  19. func BitsetGet(set []uint64, position uint) bool {
  20. idx := position / 64
  21. bit := position % 64
  22. block := atomic.LoadUint64(&set[idx])
  23. return (block & (1 << bit)) != 0
  24. }
  25. // BitsetSet sets a given bit of the bitset to 0 or 1, returning whether it changed.
  26. func BitsetSet(set []uint64, position uint, on bool) (changed bool) {
  27. idx := position / 64
  28. bit := position % 64
  29. addr := &set[idx]
  30. var mask uint64
  31. mask = 1 << bit
  32. for {
  33. current := atomic.LoadUint64(addr)
  34. var desired uint64
  35. if on {
  36. desired = current | mask
  37. } else {
  38. desired = current & (^mask)
  39. }
  40. if current == desired {
  41. return false
  42. } else if atomic.CompareAndSwapUint64(addr, current, desired) {
  43. return true
  44. }
  45. }
  46. }
  47. // BitsetEmpty returns whether the bitset is empty.
  48. // This has false positives under concurrent modification (i.e., it can return true
  49. // even though w.r.t. the sequence of atomic modifications, there was no point at
  50. // which the bitset was completely empty), but that's not how we're using this method.
  51. func BitsetEmpty(set []uint64) (empty bool) {
  52. for i := 0; i < len(set); i++ {
  53. if atomic.LoadUint64(&set[i]) != 0 {
  54. return false
  55. }
  56. }
  57. return true
  58. }
  59. // BitsetUnion modifies `set` to be the union of `set` and `other`.
  60. // This has race conditions in that we don't necessarily get a single
  61. // consistent view of `other` across word boundaries.
  62. func BitsetUnion(set []uint64, other []uint64) {
  63. for i := 0; i < len(set); i++ {
  64. for {
  65. ourAddr := &set[i]
  66. ourBlock := atomic.LoadUint64(ourAddr)
  67. otherBlock := atomic.LoadUint64(&other[i])
  68. newBlock := ourBlock | otherBlock
  69. if atomic.CompareAndSwapUint64(ourAddr, ourBlock, newBlock) {
  70. break
  71. }
  72. }
  73. }
  74. }
  75. // BitsetCopy copies the contents of `other` over `set`.
  76. // Similar caveats about race conditions as with `BitsetUnion` apply.
  77. func BitsetCopy(set []uint64, other []uint64) {
  78. for i := 0; i < len(set); i++ {
  79. data := atomic.LoadUint64(&other[i])
  80. atomic.StoreUint64(&set[i], data)
  81. }
  82. }
  83. // BitsetSubtract modifies `set` to subtract the contents of `other`.
  84. // Similar caveats about race conditions as with `BitsetUnion` apply.
  85. func BitsetSubtract(set []uint64, other []uint64) {
  86. for i := 0; i < len(set); i++ {
  87. for {
  88. ourAddr := &set[i]
  89. ourBlock := atomic.LoadUint64(ourAddr)
  90. otherBlock := atomic.LoadUint64(&other[i])
  91. newBlock := ourBlock & (^otherBlock)
  92. if atomic.CompareAndSwapUint64(ourAddr, ourBlock, newBlock) {
  93. break
  94. }
  95. }
  96. }
  97. }