Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 72 additions & 6 deletions code/go/0chain.net/core/lock/lock.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,83 @@
package lock

import "sync"
import (
"sync"
"time"
)

var lockPool = make(map[string]*sync.Mutex, 0) //nolint:gosimple // need more time to verify: declaring capacity is probably necessary?
var lockMutex = &sync.Mutex{}
var (
// MutexCleanInterval start to clean unsed mutex at specified interval
MutexCleanInterval = 10 * time.Minute
)

func GetMutex(tablename string, key string) *sync.Mutex {
var (
lockPool = make(map[string]*Mutex)
lockMutex sync.Mutex
)

// Mutex a mutual exclusion lock.
type Mutex struct {
// key lock key in pool
key string
// usedby how objects it is used by
usedby int

sync.Mutex
}

// Lock implements Locker.Lock
func (m *Mutex) Lock() {
m.Mutex.Lock()
}

// Unlock implements Locker.Unlock, and mark mutex as unlock object
func (m *Mutex) Unlock() {
lockMutex.Lock()
defer lockMutex.Unlock()

m.usedby--
m.Mutex.Unlock()
}

// GetMutex get mutex by table and key
func GetMutex(tablename string, key string) *Mutex {
lockKey := tablename + ":" + key
lockMutex.Lock()

defer lockMutex.Unlock()
if eLock, ok := lockPool[lockKey]; ok {
eLock.usedby++
return eLock
}
lockPool[lockKey] = &sync.Mutex{}
return lockPool[lockKey]

m := &Mutex{key: lockKey, usedby: 1}

lockPool[lockKey] = m

return m
}

func init() {
go startWorker()
}

func cleanUnusedMutexs() {
lockMutex.Lock()

for k, v := range lockPool {
if v.usedby < 1 {
delete(lockPool, k)
}
}

lockMutex.Unlock()
}

func startWorker() {
for {
time.Sleep(MutexCleanInterval)

cleanUnusedMutexs()

}
}
43 changes: 43 additions & 0 deletions code/go/0chain.net/core/lock/lock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package lock

import (
"strconv"
"testing"

"github.com/stretchr/testify/require"
)

func TestLock(t *testing.T) {

max := 100

for i := 0; i < max; i++ {

lock1 := GetMutex("testlock", strconv.Itoa(i))

lock1.Lock()

require.Equal(t, 1, lock1.usedby)

lock1.Unlock()

require.Equal(t, 0, lock1.usedby)

lock2 := GetMutex("testlock", strconv.Itoa(i))
lock2.Lock()

require.Equal(t, 1, lock2.usedby)

lock2.Unlock()

require.Equal(t, 0, lock2.usedby)
}

cleanUnusedMutexs()

for i := 0; i < max; i++ {
_, ok := lockPool["testlock:"+strconv.Itoa(i)]
require.Equal(t, false, ok)
}

}