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

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