package sectorblocks import ( "context" "sync" logging "github.com/ipfs/go-log" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/lib/sectorbuilder" ) var log = logging.Logger("sectorblocks") type unsealedBlocks struct { lk sync.Mutex sb *sectorbuilder.SectorBuilder // TODO: Treat this as some sort of cache, one with rather aggressive GC // TODO: This REALLY, REALLY needs to be on-disk unsealed map[string][]byte unsealing map[string]chan struct{} } func (ub *unsealedBlocks) getRef(ctx context.Context, refs []api.SealedRef, approveUnseal func() error) ([]byte, error) { var best api.SealedRef ub.lk.Lock() for _, ref := range refs { b, ok := ub.unsealed[ref.Piece] if ok { ub.lk.Unlock() return b[ref.Offset : ref.Offset+uint64(ref.Size)], nil } // TODO: pick unsealing based on how long it's running (or just select all relevant, usually it'll be just one) _, ok = ub.unsealing[ref.Piece] if ok { best = ref break } best = ref } ub.lk.Unlock() b, err := ub.maybeUnseal(ctx, best.Piece, approveUnseal) if err != nil { return nil, err } return b[best.Offset : best.Offset+uint64(best.Size)], nil } func (ub *unsealedBlocks) maybeUnseal(ctx context.Context, pieceKey string, approveUnseal func() error) ([]byte, error) { ub.lk.Lock() defer ub.lk.Unlock() out, ok := ub.unsealed[pieceKey] if ok { return out, nil } wait, ok := ub.unsealing[pieceKey] if ok { ub.lk.Unlock() select { case <-wait: ub.lk.Lock() // TODO: make sure this is not racy with gc when it's implemented return ub.unsealed[pieceKey], nil case <-ctx.Done(): ub.lk.Lock() return nil, ctx.Err() } } // TODO: doing this under a lock is suboptimal.. but simpler err := approveUnseal() if err != nil { return nil, err } ub.unsealing[pieceKey] = make(chan struct{}) ub.lk.Unlock() log.Infof("Unsealing piece '%s'", pieceKey) data, err := ub.sb.ReadPieceFromSealedSector(pieceKey) ub.lk.Lock() if err != nil { // TODO: tell subs log.Error(err) return nil, err } ub.unsealed[pieceKey] = data close(ub.unsealing[pieceKey]) return data, nil }