.. | ||
keylock.go | ||
keylock_test.go | ||
README.md | ||
speed_test.go |
keylock
Напишите примитив синхронизации, позволяющий "лочить" строки из множества.
В обычном sync.Mutex
лок всего один. В один момент времени этот лок может находиться
у одной горутины. В нашем примитиве локов может быть сколько угодно. Каждый лок мы идентифицируем
ключём - строкой. Каждая горутина приходит к нам со списком ключей и хочет захватить сразу
все локи из этого списка. (Наша аналогия с sync.Mutex
вовсе не значит, что нужно использовать
sync.Mutex
в реализации. Лучше использовать каналы, чтобы проще было реализовать отмену.)
В итоге, внутри критической секции может быть сколько угодно горутин, но у всех них множества ключей попарно не пересекаются.
Например, если к нам придёт 3 горутины:
LockKeys({a, b})
LockKeys({b})
LockKeys({c})
То в критической секции одновременно могут находиться:
- 1 и 3
- 2 и 3
А вот 1 и 2 одновременно в критическую секцию зайти не могут, потому что у них множества ключей пересекаются.
Метод LockKeys
должен блокироваться, пока не удастся
захватить все ключи, либо пока операция не будет
отменена через канал cancel
.
package keylock
type KeyLock interface {
// LockKeys locks all keys from provided set.
//
// Upon successful completion, function guarantees that no other call with intersecting set of keys
// will finish, until unlock() is called.
//
// If cancel channel is closed, function stops trying to lock received keys and returns immediately
LockKeys(keys []string, cancel <-chan struct{}) (canceled bool, unlock func())
}
Реализация не должна содержать busy wait. То есть, если вызов LockKeys не может выполниться, потому что какие-то из ключей залочены другими горутинами, то текущая горутина должна засыпать.