package lock import ( "fmt" "runtime" "sort" "strconv" "strings" "sync" "testing" "time" ) const ( prime32 = uint32(16777619) ) type Locks struct { table []*sync.RWMutex } func Make(tableSize int) *Locks { table := make([]*sync.RWMutex, tableSize) for i := 0; i < tableSize; i++ { table[i] = &sync.RWMutex{} } return &Locks{ table: table, } } func fnv32(key string) uint32 { hash := uint32(2166136261) for i := 0; i < len(key); i++ { hash *= prime32 hash ^= uint32(key[i]) } return hash } func (locks *Locks) spread(hashCode uint32) uint32 { if locks == nil { panic("dict is nil") } tableSize := uint32(len(locks.table)) return (tableSize - 1) & uint32(hashCode) } func (locks *Locks) Lock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.Lock() } func (locks *Locks) RLock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.RLock() } func (locks *Locks) UnLock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.Unlock() } func (locks *Locks) RUnLock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.RUnlock() } func (locks *Locks) toLockIndices(keys []string, reverse bool) []uint32 { indexMap := make(map[uint32]bool) for _, key := range keys { index := locks.spread(fnv32(key)) indexMap[index] = true } indices := make([]uint32, 0, len(indexMap)) for index := range indexMap { indices = append(indices, index) } sort.Slice(indices, func(i, j int) bool { if !reverse { return indices[i] < indices[j] } else { return indices[i] > indices[j] } }) return indices } func (locks *Locks) Locks(keys ...string) { indices := locks.toLockIndices(keys, false) for _, index := range indices { mu := locks.table[index] mu.Lock() } } func (locks *Locks) RLocks(keys ...string) { indices := locks.toLockIndices(keys, false) for _, index := range indices { mu := locks.table[index] mu.RLock() } } func (locks *Locks) UnLocks(keys ...string) { indices := locks.toLockIndices(keys, true) for _, index := range indices { mu := locks.table[index] mu.Unlock() } } func (locks *Locks) RUnLocks(keys ...string) { indices := locks.toLockIndices(keys, true) for _, index := range indices { mu := locks.table[index] mu.RUnlock() } } func GoID() int { var buf [64]byte n := runtime.Stack(buf[:], false) idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] id, err := strconv.Atoi(idField) if err != nil { panic(fmt.Sprintf("cannot get goroutine id: %v", err)) } return id } func debug(testing.T) { lm := Locks{} size := 10 var wg sync.WaitGroup wg.Add(size) for i := 0; i < size; i++ { go func(i int) { lm.Locks("1", "2") println("go: " + strconv.Itoa(GoID())) time.Sleep(time.Second) println("go: " + strconv.Itoa(GoID())) lm.UnLocks("1", "2") wg.Done() }(i) } wg.Wait() }