lotus/extern/sector-storage/stores/index_locks.go

157 lines
3.2 KiB
Go
Raw Normal View History

package stores
import (
"context"
"sync"
"golang.org/x/xerrors"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/abi"
2020-09-30 17:32:19 +00:00
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
)
type sectorLock struct {
2020-06-05 18:04:59 +00:00
cond *ctxCond
2020-09-06 16:54:00 +00:00
r [storiface.FileTypes]uint
w storiface.SectorFileType
refs uint // access with indexLocks.lk
}
2020-09-06 16:54:00 +00:00
func (l *sectorLock) canLock(read storiface.SectorFileType, write storiface.SectorFileType) bool {
for i, b := range write.All() {
if b && l.r[i] > 0 {
return false
}
}
2020-06-05 08:21:21 +00:00
// check that there are no locks taken for either read or write file types we want
return l.w&read == 0 && l.w&write == 0
}
2020-09-06 16:54:00 +00:00
func (l *sectorLock) tryLock(read storiface.SectorFileType, write storiface.SectorFileType) bool {
if !l.canLock(read, write) {
return false
}
for i, set := range read.All() {
if set {
l.r[i]++
}
}
l.w |= write
return true
}
2020-09-06 16:54:00 +00:00
type lockFn func(l *sectorLock, ctx context.Context, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error)
2020-06-08 16:47:59 +00:00
2020-09-06 16:54:00 +00:00
func (l *sectorLock) tryLockSafe(ctx context.Context, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) {
2020-06-08 16:47:59 +00:00
l.cond.L.Lock()
defer l.cond.L.Unlock()
return l.tryLock(read, write), nil
}
2020-09-06 16:54:00 +00:00
func (l *sectorLock) lock(ctx context.Context, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) {
2020-06-05 18:04:59 +00:00
l.cond.L.Lock()
defer l.cond.L.Unlock()
2020-06-05 18:04:59 +00:00
for !l.tryLock(read, write) {
if err := l.cond.Wait(ctx); err != nil {
2020-06-08 16:47:59 +00:00
return false, err
}
}
2020-06-05 18:04:59 +00:00
2020-06-08 16:47:59 +00:00
return true, nil
}
2020-09-06 16:54:00 +00:00
func (l *sectorLock) unlock(read storiface.SectorFileType, write storiface.SectorFileType) {
2020-06-05 18:04:59 +00:00
l.cond.L.Lock()
defer l.cond.L.Unlock()
for i, set := range read.All() {
if set {
l.r[i]--
}
}
l.w &= ^write
2020-06-05 18:04:59 +00:00
l.cond.Broadcast()
}
type indexLocks struct {
lk sync.Mutex
locks map[abi.SectorID]*sectorLock
}
2020-09-06 16:54:00 +00:00
func (i *indexLocks) lockWith(ctx context.Context, lockFn lockFn, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) {
2020-06-03 20:00:34 +00:00
if read|write == 0 {
2020-06-08 16:47:59 +00:00
return false, nil
}
2020-09-06 16:54:00 +00:00
if read|write > (1<<storiface.FileTypes)-1 {
2020-06-08 16:47:59 +00:00
return false, xerrors.Errorf("unknown file types specified")
}
i.lk.Lock()
slk, ok := i.locks[sector]
if !ok {
slk = &sectorLock{}
2020-06-05 18:04:59 +00:00
slk.cond = newCtxCond(&sync.Mutex{})
i.locks[sector] = slk
}
slk.refs++
i.lk.Unlock()
2020-06-08 16:47:59 +00:00
locked, err := lockFn(slk, ctx, read, write)
if err != nil {
return false, err
}
if !locked {
return false, nil
}
go func() {
// TODO: we can avoid this goroutine with a bit of creativity and reflect
<-ctx.Done()
i.lk.Lock()
slk.unlock(read, write)
slk.refs--
if slk.refs == 0 {
delete(i.locks, sector)
}
i.lk.Unlock()
}()
2020-06-08 16:47:59 +00:00
return true, nil
}
2020-09-06 16:54:00 +00:00
func (i *indexLocks) StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error {
2020-06-08 16:47:59 +00:00
ok, err := i.lockWith(ctx, (*sectorLock).lock, sector, read, write)
if err != nil {
return err
}
if !ok {
return xerrors.Errorf("failed to acquire lock")
}
return nil
}
2020-06-08 16:47:59 +00:00
2020-09-06 16:54:00 +00:00
func (i *indexLocks) StorageTryLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) {
2020-06-08 16:47:59 +00:00
return i.lockWith(ctx, (*sectorLock).tryLockSafe, sector, read, write)
}