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.

146 lines
3.5 KiB

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