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.

162 lines
4.3 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. package util
  2. import (
  3. "fmt"
  4. "github.com/seaweedfs/seaweedfs/weed/glog"
  5. "sync"
  6. "sync/atomic"
  7. )
  8. // LockTable is a table of locks that can be acquired.
  9. // Locks are acquired in order of request.
  10. type LockTable[T comparable] struct {
  11. lockIdSeq int64
  12. mu sync.Mutex
  13. locks map[T]*LockEntry
  14. }
  15. type LockEntry struct {
  16. mu sync.Mutex
  17. waiters []*ActiveLock // ordered waiters that are blocked by exclusive locks
  18. activeSharedLockOwnerCount int32
  19. activeExclusiveLockOwnerCount int32
  20. cond *sync.Cond
  21. }
  22. type LockType int
  23. const (
  24. SharedLock LockType = iota
  25. ExclusiveLock
  26. )
  27. type ActiveLock struct {
  28. ID int64
  29. isDeleted bool
  30. intention string // for debugging
  31. lockType LockType
  32. }
  33. func NewLockTable[T comparable]() *LockTable[T] {
  34. return &LockTable[T]{
  35. locks: make(map[T]*LockEntry),
  36. }
  37. }
  38. func (lt *LockTable[T]) NewActiveLock(intention string, lockType LockType) *ActiveLock {
  39. id := atomic.AddInt64(&lt.lockIdSeq, 1)
  40. l := &ActiveLock{ID: id, intention: intention, lockType: lockType}
  41. return l
  42. }
  43. func (lt *LockTable[T]) AcquireLock(intention string, key T, lockType LockType) (lock *ActiveLock) {
  44. lt.mu.Lock()
  45. // Get or create the lock entry for the key
  46. entry, exists := lt.locks[key]
  47. if !exists {
  48. entry = &LockEntry{}
  49. entry.cond = sync.NewCond(&entry.mu)
  50. lt.locks[key] = entry
  51. }
  52. lt.mu.Unlock()
  53. lock = lt.NewActiveLock(intention, lockType)
  54. // If the lock is held exclusively, wait
  55. entry.mu.Lock()
  56. if len(entry.waiters) > 0 || lockType == ExclusiveLock || entry.activeExclusiveLockOwnerCount > 0 {
  57. if glog.V(4) {
  58. fmt.Printf("ActiveLock %d %s wait for %+v type=%v with waiters %d active r%d w%d.\n", lock.ID, lock.intention, key, lockType, len(entry.waiters), entry.activeSharedLockOwnerCount, entry.activeExclusiveLockOwnerCount)
  59. if len(entry.waiters) > 0 {
  60. for _, waiter := range entry.waiters {
  61. fmt.Printf(" %d", waiter.ID)
  62. }
  63. fmt.Printf("\n")
  64. }
  65. }
  66. entry.waiters = append(entry.waiters, lock)
  67. if lockType == ExclusiveLock {
  68. for !lock.isDeleted && ((len(entry.waiters) > 0 && lock.ID != entry.waiters[0].ID) || entry.activeExclusiveLockOwnerCount > 0 || entry.activeSharedLockOwnerCount > 0) {
  69. entry.cond.Wait()
  70. }
  71. } else {
  72. for !lock.isDeleted && (len(entry.waiters) > 0 && lock.ID != entry.waiters[0].ID) || entry.activeExclusiveLockOwnerCount > 0 {
  73. entry.cond.Wait()
  74. }
  75. }
  76. // Remove the transaction from the waiters list
  77. if len(entry.waiters) > 0 && lock.ID == entry.waiters[0].ID {
  78. entry.waiters = entry.waiters[1:]
  79. entry.cond.Broadcast()
  80. }
  81. }
  82. // Otherwise, grant the lock
  83. if glog.V(4) {
  84. fmt.Printf("ActiveLock %d %s locked %+v type=%v with waiters %d active r%d w%d.\n", lock.ID, lock.intention, key, lockType, len(entry.waiters), entry.activeSharedLockOwnerCount, entry.activeExclusiveLockOwnerCount)
  85. if len(entry.waiters) > 0 {
  86. for _, waiter := range entry.waiters {
  87. fmt.Printf(" %d", waiter.ID)
  88. }
  89. fmt.Printf("\n")
  90. }
  91. }
  92. if lock.lockType == ExclusiveLock {
  93. entry.activeExclusiveLockOwnerCount++
  94. } else {
  95. entry.activeSharedLockOwnerCount++
  96. }
  97. entry.mu.Unlock()
  98. return lock
  99. }
  100. func (lt *LockTable[T]) ReleaseLock(key T, lock *ActiveLock) {
  101. lt.mu.Lock()
  102. defer lt.mu.Unlock()
  103. entry, exists := lt.locks[key]
  104. if !exists {
  105. return
  106. }
  107. entry.mu.Lock()
  108. defer entry.mu.Unlock()
  109. // Remove the transaction from the waiters list
  110. for i, waiter := range entry.waiters {
  111. if waiter == lock {
  112. waiter.isDeleted = true
  113. entry.waiters = append(entry.waiters[:i], entry.waiters[i+1:]...)
  114. break
  115. }
  116. }
  117. if lock.lockType == ExclusiveLock {
  118. entry.activeExclusiveLockOwnerCount--
  119. } else {
  120. entry.activeSharedLockOwnerCount--
  121. }
  122. // If there are no waiters, release the lock
  123. if len(entry.waiters) == 0 && entry.activeExclusiveLockOwnerCount <= 0 && entry.activeSharedLockOwnerCount <= 0 {
  124. delete(lt.locks, key)
  125. }
  126. if glog.V(4) {
  127. fmt.Printf("ActiveLock %d %s unlocked %+v type=%v with waiters %d active r%d w%d.\n", lock.ID, lock.intention, key, lock.lockType, len(entry.waiters), entry.activeSharedLockOwnerCount, entry.activeExclusiveLockOwnerCount)
  128. if len(entry.waiters) > 0 {
  129. for _, waiter := range entry.waiters {
  130. fmt.Printf(" %d", waiter.ID)
  131. }
  132. fmt.Printf("\n")
  133. }
  134. }
  135. // Notify the next waiter
  136. entry.cond.Broadcast()
  137. }
  138. func main() {
  139. }