From ad938f5d05651e70d8911ccd8221c9a46790e9de Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 1 Jan 2024 19:56:40 -0800 Subject: [PATCH] first iteration of lockmanager with tests --- util/lockmanager.go | 56 ++++++++++++++++++++++++++++++++++++++++ util/lockmanager_test.go | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 util/lockmanager.go create mode 100644 util/lockmanager_test.go diff --git a/util/lockmanager.go b/util/lockmanager.go new file mode 100644 index 00000000..32be5c26 --- /dev/null +++ b/util/lockmanager.go @@ -0,0 +1,56 @@ +package util + +import ( + "fmt" + "log" + "sync" + "time" +) + +type Lock struct { + key string; + timestamp time.Time; +} + +type LockManager struct { + locks map[string]*Lock; + mutex sync.Mutex; + staleDuration time.Duration; +} + +func NewLockManager(staleDuration time.Duration) *LockManager { + lm := &LockManager{ + locks: make(map[string]*Lock), + staleDuration: staleDuration, + } + go lm.removeStaleLocks(); + return lm; +} + +func (lm *LockManager) Lock(key string) { + lm.mutex.Lock(); + lm.locks[key] = &Lock{key: key, timestamp: time.Now()}; +} + +func (lm *LockManager) Unlock(key string) error { + // Check if key exists in the lock map + _, ok := lm.locks[key]; + if ok { + defer lm.mutex.Unlock(); + delete(lm.locks, key); + return nil; + } else { + log.Fatal("Key not found.") + return fmt.Errorf("Error. Key not found."); + } +} + +func (lm *LockManager) removeStaleLocks() { + time.Sleep(lm.staleDuration); + for key, lock := range lm.locks { + if time.Since(lock.timestamp) > lm.staleDuration { + delete(lm.locks, key); + lm.mutex.Unlock(); + } + } +} \ No newline at end of file diff --git a/util/lockmanager_test.go b/util/lockmanager_test.go new file mode 100644 index 00000000..36b0b948 --- /dev/null +++ b/util/lockmanager_test.go @@ -0,0 +1,51 @@ +package util + +import ( + "fmt" + "testing" + "time" +) + + +func TestLockAcquisition(t *testing.T) { + lm := NewLockManager(3 * time.Second); + key := "testkey" + fmt.Println("Main Thread!") + lm.Lock(key) + lm.Lock(key) + + fmt.Println("Got past the first lock from removeStaleLocks go routine") + + go func() { + fmt.Println("1st Go Routine Started") + + startTime := time.Now(); + lm.Lock(key) + lockWaitDuration := time.Since(startTime); + fmt.Printf("Waited %v long to aquire the lock in the 1st go routine\n", lockWaitDuration); + + go func() { + fmt.Println("2nd Go Routine Started") + startTime := time.Now(); + lm.Lock(key); + lockWaitDuration := time.Since(startTime); + fmt.Printf("Waited %v long to aquire the lock in the 2nd go routine\n", lockWaitDuration); + defer lm.Unlock(key); + fmt.Println("2nd Go Routine Done") + }() + + + defer lm.Unlock(key) + fmt.Println("1st Go Routine Done") + time.Sleep(3 * time.Second) + }() + + fmt.Println("Main Thread Almost Done") + time.Sleep(1 * time.Second) + lm.Unlock(key) + + time.Sleep(4 * time.Second) + + fmt.Println("Main Thread Done!") + +} \ No newline at end of file