Documentation
¶
Overview ¶
Package rowlock provides an implementation of row lock.
A row lock is a set of locks associated with rows. Instead of locking and unlocking globally, you only operate locks on a row level.
RowLock provides optional RLock and RUnlock functions to use separated read and write locks. In order to take advantage of them, NewLocker function used in NewRowLock must returns an implementation of RWLocker (for example, RWMutexNewLocker returns a new sync.RWMutex). If the locker returned by NewLocker didn't implement RLocker function defined in RWLocker, RLock will work the same as Lock and RUnlock will work the same as Unlock.
Example ¶
package main import ( "fmt" "sync" "time" "go.yhsif.com/rowlock" ) func main() { lock := rowlock.NewRowLock[string](rowlock.MutexNewLocker) key1 := "key1" key2 := "key2" round := time.Millisecond * 50 keys := []string{key1, key1, key2, key2} sleeps := []time.Duration{ time.Millisecond * 250, time.Millisecond * 200, time.Millisecond * 350, time.Millisecond * 300, } var wg sync.WaitGroup wg.Add(len(keys)) for i := range keys { go func(key string, sleep time.Duration) { started := time.Now() defer wg.Done() time.Sleep(sleep) lock.Lock(key) defer lock.Unlock(key) elapsed := time.Now().Sub(started).Round(round) // The same key with longer sleep will get an elapsed time about // 2 * the same key with shorter sleep instead of its own sleep time, // because that's when the other goroutine releases the lock. fmt.Printf("%s got lock after about %v\n", key, elapsed) time.Sleep(sleep) }(keys[i], sleeps[i]) } wg.Wait() }
Output: key1 got lock after about 200ms key2 got lock after about 300ms key1 got lock after about 400ms key2 got lock after about 600ms
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MutexNewLocker ¶
MutexNewLocker is a NewLocker using sync.Mutex.
func RWMutexNewLocker ¶
RWMutexNewLocker is a NewLocker using sync.RWMutex.
Example ¶
package main import ( "fmt" "sync" "time" "go.yhsif.com/rowlock" ) func main() { lock := rowlock.NewRowLock[string](rowlock.RWMutexNewLocker) key1 := "key1" key2 := "key2" round := time.Millisecond * 50 var wg sync.WaitGroup readKeys := []string{key1, key1, key2, key2} readSleeps := []time.Duration{ time.Millisecond * 250, time.Millisecond * 200, time.Millisecond * 350, time.Millisecond * 300, } wg.Add(len(readKeys)) writeKeys := []string{key1, key1, key2, key2} writeSleeps := []time.Duration{ time.Millisecond * 350, time.Millisecond * 150, time.Millisecond * 450, time.Millisecond * 200, } wg.Add(len(writeKeys)) // Read locks for i := range readKeys { go func(key string, sleep time.Duration) { started := time.Now() defer wg.Done() time.Sleep(sleep) lock.RLock(key) defer lock.RUnlock(key) elapsed := time.Now().Sub(started).Round(round) // Should be: // max(shorter write sleep time * 2, self sleep time) fmt.Printf("%s got read lock after about %v\n", key, elapsed) time.Sleep(sleep) }(readKeys[i], readSleeps[i]) } // Write locks for i := range writeKeys { go func(key string, sleep time.Duration) { started := time.Now() defer wg.Done() time.Sleep(sleep) lock.Lock(key) defer lock.Unlock(key) elapsed := time.Now().Sub(started).Round(round) // For the longer sleep one, it should be // max(shorter write * 2, longer read) + longer read // instead of it's self sleep time fmt.Printf("%s got lock after about %v\n", key, elapsed) time.Sleep(sleep) }(writeKeys[i], writeSleeps[i]) } wg.Wait() }
Output: key1 got lock after about 150ms key2 got lock after about 200ms key1 got read lock after about 300ms key1 got read lock after about 300ms key2 got read lock after about 400ms key2 got read lock after about 400ms key1 got lock after about 550ms key2 got lock after about 750ms
Types ¶
type RowLock ¶
type RowLock[T comparable] struct { // contains filtered or unexported fields }
RowLock defines a set of locks.
When you do Lock/Unlock operations, you don't do them on a global scale. Instead, a Lock/Unlock operation is operated on a given row.
If NewLocker returns an implementation of RWLocker in NewRowLock, the RowLock can be locked separately for read in RLock and RUnlock functions. Otherwise, RLock is the same as Lock and RUnlock is the same as Unlock.
func NewRowLock ¶
func NewRowLock[T comparable](f NewLocker) *RowLock[T]
NewRowLock creates a new RowLock with the given NewLocker.
func (*RowLock[T]) Lock ¶
func (rl *RowLock[T]) Lock(row T)
Lock locks a row.
If this is a new row, a new locker will be created using the NewLocker specified in NewRowLock.
func (*RowLock[T]) RLock ¶
func (rl *RowLock[T]) RLock(row T)
RLock locks a row for read.
It only works as expected when NewLocker specified in NewRowLock returns an implementation of RWLocker. Otherwise, it's the same as Lock.