79 lines
1.6 KiB
Go
79 lines
1.6 KiB
Go
|
// Copyright (C) 2019 Storj Labs, Inc.
|
||
|
// See LICENSE for copying information
|
||
|
|
||
|
package sync2
|
||
|
|
||
|
import (
|
||
|
"runtime"
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
)
|
||
|
|
||
|
// Fence allows to wait for something to happen.
|
||
|
type Fence struct {
|
||
|
status uint32
|
||
|
wait sync.Mutex
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
General flow of the fence:
|
||
|
|
||
|
init:
|
||
|
first arriver caller to `init` will setup a lock in `wait`
|
||
|
|
||
|
Wait callers:
|
||
|
try to lock/unlock in Wait, this will block until the initial lock will be released
|
||
|
|
||
|
Release caller:
|
||
|
first caller will release the initial lock
|
||
|
*/
|
||
|
|
||
|
const (
|
||
|
statusUninitialized = iota
|
||
|
statusInitializing
|
||
|
statusBlocked
|
||
|
statusReleased
|
||
|
)
|
||
|
|
||
|
// init sets up the initial lock into wait
|
||
|
func (fence *Fence) init() {
|
||
|
// wait for initialization
|
||
|
for atomic.LoadUint32(&fence.status) <= statusInitializing {
|
||
|
// first arriver sets up lock
|
||
|
if atomic.CompareAndSwapUint32(&fence.status, statusUninitialized, statusInitializing) {
|
||
|
fence.wait.Lock()
|
||
|
atomic.StoreUint32(&fence.status, statusBlocked)
|
||
|
} else {
|
||
|
runtime.Gosched()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Wait waits for wait to be unlocked
|
||
|
func (fence *Fence) Wait() {
|
||
|
// fast-path
|
||
|
if fence.Released() {
|
||
|
return
|
||
|
}
|
||
|
fence.init()
|
||
|
// start waiting on the initial lock to be released
|
||
|
fence.wait.Lock()
|
||
|
// intentionally empty critical section to wait for Release
|
||
|
//nolint
|
||
|
fence.wait.Unlock()
|
||
|
}
|
||
|
|
||
|
// Released returns whether the fence has been released.
|
||
|
func (fence *Fence) Released() bool {
|
||
|
return atomic.LoadUint32(&fence.status) >= statusReleased
|
||
|
}
|
||
|
|
||
|
// Release releases everyone from Wait
|
||
|
func (fence *Fence) Release() {
|
||
|
fence.init()
|
||
|
// the first one releases the status
|
||
|
if atomic.CompareAndSwapUint32(&fence.status, statusBlocked, statusReleased) {
|
||
|
fence.wait.Unlock()
|
||
|
}
|
||
|
}
|