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 2.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  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. previouslyOn := (current & mask) != 0
  35. if on == previouslyOn {
  36. return false
  37. }
  38. var desired uint64
  39. if on {
  40. desired = current | mask
  41. } else {
  42. desired = current & (^mask)
  43. }
  44. if atomic.CompareAndSwapUint64(addr, current, desired) {
  45. return true
  46. }
  47. }
  48. }
  49. // BitsetEmpty returns whether the bitset is empty.
  50. // Right now, this is technically free of race conditions because we don't
  51. // have a method that can simultaneously modify two bits separated by a word boundary
  52. // such that one of those modifications is an unset. If we did, there would be a race
  53. // that could produce false positives. It's probably better to assume that they are
  54. // already possible under concurrent modification (which is not how we're using this).
  55. func BitsetEmpty(set []uint64) (empty bool) {
  56. for i := 0; i < len(set); i++ {
  57. if atomic.LoadUint64(&set[i]) != 0 {
  58. return false
  59. }
  60. }
  61. return true
  62. }
  63. // BitsetUnion modifies `set` to be the union of `set` and `other`.
  64. // This has race conditions in that we don't necessarily get a single
  65. // consistent view of `other` across word boundaries.
  66. func BitsetUnion(set []uint64, other []uint64) {
  67. for i := 0; i < len(set); i++ {
  68. for {
  69. ourAddr := &set[i]
  70. ourBlock := atomic.LoadUint64(ourAddr)
  71. otherBlock := atomic.LoadUint64(&other[i])
  72. newBlock := ourBlock | otherBlock
  73. if atomic.CompareAndSwapUint64(ourAddr, ourBlock, newBlock) {
  74. break
  75. }
  76. }
  77. }
  78. }