Skip to content

Commit

Permalink
changes to lockmanager/tests
Browse files Browse the repository at this point in the history
  • Loading branch information
OliverLok committed Feb 1, 2024
1 parent 9fd336b commit 2778bd9
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 57 deletions.
41 changes: 24 additions & 17 deletions common/lockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

type lockData struct {
timestamp time.Time;
mutex sync.Mutex;
mutex sync.RWMutex;
isLocked bool;
}

Expand All @@ -33,7 +33,6 @@ func Lock(key string) {
val, _ := lockMap.LoadOrStore(key, &lockData{});
val.(*lockData).mutex.Lock();


val.(*lockData).timestamp = time.Now();
val.(*lockData).isLocked = true;
}
Expand All @@ -43,12 +42,12 @@ func Unlock(key string) error {
defer lockManagerMutex.Unlock();

val, exists := lockMap.Load(key);
lock := val.(*lockData);
if !exists {
log.Error().Str("key", key).Msg("Key does not exist");
return fmt.Errorf("Key not found: %v", key);
}


lock := val.(*lockData);
if !lock.isLocked {
log.Error().Str("key", key).Msg("Tried to unlock a lock that is unlocked");
return fmt.Errorf("Key: %v Tried to unlock a lock that is unlocked", key);
Expand All @@ -60,22 +59,30 @@ func Unlock(key string) error {
return nil;
}


func removeStaleLocks() {
ticker := time.NewTicker(time.Duration(5) * time.Second);

for range ticker.C {
staleDuration := time.Duration(time.Duration(config.STALELOCK_DURATION.Get()) * time.Second);

lockMap.Range(func(key, val any) bool {
lock := val.(*lockData);
if (time.Since(lock.timestamp) > staleDuration) && (!lock.isLocked) {
lockManagerMutex.Lock();
defer lockManagerMutex.Unlock();
if (time.Since(lock.timestamp) > staleDuration) && (lock.mutex.TryLock()) {
lockMap.Delete(key);
}
}
return true;
})
RemoveStaleLocksOnce();
}
}

func RemoveStaleLocksOnce() bool {
staleDuration := time.Duration(time.Duration(config.STALELOCK_DURATION.Get()) * time.Second);
removed := false;

lockMap.Range(func(key, val any) bool {
lock := val.(*lockData);
if (time.Since(lock.timestamp) > staleDuration) && (!lock.isLocked) {
lockManagerMutex.Lock();
defer lockManagerMutex.Unlock();
if (time.Since(lock.timestamp) > staleDuration) && (lock.mutex.TryLock()) {
lockMap.Delete(key);
removed = true;
}
}
return true;
})
return removed;
}
101 changes: 63 additions & 38 deletions common/lockmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,86 @@ import (
)

var (
key = "testkey"
key2 = "testkey2"
removeKey = "remove"
key = "testkey1"
key2 = "testkey2"
doesNotExistKey = "dne"
)


func testLock(t *testing.T) {
func TestLock(test *testing.T) {
var wg sync.WaitGroup

Lock(key)
Unlock(key)
time.Sleep(4 * time.Second) // test remove stale lock
// Testing Lock
Lock(key)

// Testing Unlock
err := Unlock(key);
if (err != nil) {
test.Errorf("Failed to unlock a key");
}

Lock(key)
Lock(key2)
fmt.Println("Main Thread!")
// Testing remove stale locks
Lock(removeKey)
Unlock(removeKey)
wg.Add(1)
go func() {
defer wg.Done();
time.Sleep(2 * time.Second)
if !RemoveStaleLocksOnce() {
test.Errorf("Failed to remove stale locks");
}
}()

// Testing concurrent Locking/Unlocking
Lock(key);
Lock(key2);
wg.Add(2);
go func() {
wg.Add(1)
defer wg.Done()
fmt.Println("Go routine started")
Lock(key);
time.Sleep(time.Second)
defer Unlock(key)
go func() {
wg.Add(1)
defer wg.Done()
fmt.Println("Another go routine started")
Lock(key2)
defer Unlock(key2)
fmt.Println("Another go routine done")
}()
fmt.Println("Go routine done")

}()

time.Sleep(5000 * time.Millisecond);

go func() {
wg.Add(1)
defer wg.Done()
fmt.Println("2nd go routine started")
Lock(key2);
defer Unlock(key2);

fmt.Println("2nd go routine done")
}()
}();
Unlock(key);
Unlock(key2);

wg.Wait();

// Testing Unlocking an unlocked lock
err = Unlock(key);
if err == nil {
test.Errorf("Lockmanager unlocked an unlocked key");
}

// Testing Unlocking a key that doesn't exist
err = Unlock(doesNotExistKey);
if err == nil {
test.Errorf("Lockmanager unlocked a key that doesn't exist");
}

//Stress Test

fmt.Println("starting")
keys := []string{"key1", "key2", "key3"}

fmt.Println("Main Thread Almost Done!")
Unlock(key)
fmt.Println("got here")
Unlock(key2)

wg.Wait()
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
key := keys[i%len(keys)]
fmt.Println("key: ", key)
Lock(key)
time.Sleep(10 * time.Millisecond)
Unlock(key)
fmt.Println("here")

}()
}

fmt.Println("Main Thread Done")
wg.Wait()

}
4 changes: 2 additions & 2 deletions config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var (
EMAIL_USER = MustNewStringOption("email.user", "", "SMTP username for emails sent from the autograder.");

// Docker
DOCKER_DISABLE = MustNewBoolOption("docker.disable", false, "Disable the use of docker (usually for testing).");
DOCKER_DISABLE = MustNewBoolOption("docker.disable", true, "Disable the use of docker (usually for testing).");

// Tasks
NO_TASKS = MustNewBoolOption("tasks.disable", false, "Disable all scheduled tasks.");
Expand All @@ -46,5 +46,5 @@ var (
DB_TYPE = MustNewStringOption("db.type", "disk", "The type of database to use.");
DB_PG_URI = MustNewStringOption("db.pg.uri", "", "Connection string to connect to a Postgres Databse. Empty if not using Postgres.");

STALELOCK_DURATION = MustNewIntOption("stale.duration", 5, "Amount of time a lock can be unused before getting removed");
STALELOCK_DURATION = MustNewIntOption("stale.duration", 2, "Amount of time a lock can be unused before getting removed");
)

0 comments on commit 2778bd9

Please sign in to comment.