deals: Use temp files for AddPiece

This commit is contained in:
Łukasz Magiera
2019-08-07 20:02:05 -07:00
committed by whyrusleeping
parent 5e176eed78
commit 3e2d04a540
4 changed files with 43 additions and 197 deletions
-106
View File
@@ -1,106 +0,0 @@
// +build !windows
// TODO: windows now has pipes, verify if it maybe works
// TODO: extract from filecoin
package bytesink
import (
"fmt"
"io/ioutil"
"os"
"syscall"
"github.com/pkg/errors"
)
// FifoByteSink is not safe for concurrent access, as writes to underlying pipe are atomic only
// if len(buf) is less than the OS-specific PIPE_BUF value.
type FifoByteSink struct {
file *os.File
path string
}
// Open prepares the sink for writing by opening the backing FIFO file. Open
// will block until someone opens the FIFO file for reading.
func (s *FifoByteSink) Open() error {
file, err := os.OpenFile(s.path, os.O_WRONLY, os.ModeNamedPipe)
if err != nil {
return errors.Wrap(err, "failed to open pipe")
}
s.file = file
return nil
}
// Write writes the provided buffer to the underlying pipe. Write will block
// until the provided buffer's bytes have been read from the read end of the
// pipe.
//
// Warning: Writes are atomic only if len(buf) is less than the OS-specific
// PIPE_BUF value. For more information, see:
//
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
func (s *FifoByteSink) Write(buf []byte) (int, error) {
return s.file.Write(buf)
}
// Close ensures that the underlying file is closed and removed.
func (s *FifoByteSink) Close() (retErr error) {
cerr := s.file.Close()
if cerr != nil {
return cerr
}
defer func() {
rerr := os.Remove(s.path)
if retErr == nil {
retErr = rerr
}
}()
return
}
// ID produces a string-identifier for this byte sink. For now, this is just the
// path of the FIFO file. This string may get more structured in the future.
func (s *FifoByteSink) ID() string {
return s.path
}
// NewFifo creates a FIFO pipe and returns a pointer to a FifoByteSink, which
// satisfies the ByteSink interface. The FIFO pipe is used to stream bytes to
// rust-fil-proofs from Go during the piece-adding flow. Writes to the pipe are
// buffered automatically by the OS; the size of the buffer varies.
func NewFifo() (*FifoByteSink, error) {
path, err := createTmpFifoPath()
if err != nil {
return nil, errors.Wrap(err, "creating FIFO path failed")
}
err = syscall.Mkfifo(path, 0600)
if err != nil {
return nil, errors.Wrap(err, "mkfifo failed")
}
return &FifoByteSink{
path: path,
}, nil
}
// createTmpFifoPath creates a path with which a temporary FIFO file may be
// created.
func createTmpFifoPath() (string, error) {
file, err := ioutil.TempFile("", "")
if err != nil {
return "", err
}
err = file.Close()
if err != nil {
return "", err
}
return fmt.Sprintf("%s.fifo", file.Name()), nil
}
+2 -82
View File
@@ -3,17 +3,13 @@ package sectorbuilder
import (
"context"
"encoding/binary"
"io"
"unsafe"
"golang.org/x/xerrors"
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
logging "github.com/ipfs/go-log"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/lib/bytesink"
)
var log = logging.Logger("sectorbuilder")
@@ -72,84 +68,8 @@ func (sb *SectorBuilder) Destroy() {
sectorbuilder.DestroySectorBuilder(sb.handle)
}
func (sb *SectorBuilder) AddPiece(ctx context.Context, pieceRef string, pieceSize uint64, pieceReader io.ReadCloser) (uint64, error) {
fifoFile, err := bytesink.NewFifo()
if err != nil {
return 0, err
}
// errCh holds any error encountered when streaming bytes or making the CGO
// call. The channel is buffered so that the goroutines can exit, which will
// close the pipe, which unblocks the CGO call.
errCh := make(chan error, 2)
// sectorIDCh receives a value if the CGO call indicates that the client
// piece has successfully been added to a sector. The channel is buffered
// so that the goroutine can exit if a value is sent to errCh before the
// CGO call completes.
sectorIDCh := make(chan uint64, 1)
// goroutine attempts to copy bytes from piece's reader to the fifoFile
go func() {
// opening the fifoFile blocks the goroutine until a reader is opened on the
// other end of the FIFO pipe
err := fifoFile.Open()
if err != nil {
errCh <- xerrors.Errorf("failed to open fifoFile: %w", err)
return
}
// closing theg s fifoFile signals to the reader that we're done writing, which
// unblocks the reader
defer func() {
err := fifoFile.Close()
if err != nil {
log.Warnf("failed to close fifoFile: %s", err)
}
}()
n, err := io.Copy(fifoFile, pieceReader)
if err != nil {
errCh <- xerrors.Errorf("failed to copy to pipe: %w", err)
return
}
if uint64(n) != pieceSize {
errCh <- xerrors.Errorf("expected to write %d bytes but wrote %d", pieceSize, n)
return
}
}()
// goroutine makes CGO call, which blocks until FIFO pipe opened for writing
// from within other goroutine
go func() {
id, err := sectorbuilder.AddPiece(sb.handle, pieceRef, pieceSize, fifoFile.ID())
if err != nil {
msg := "CGO add_piece returned an error (err=%s, fifo path=%s)"
log.Errorf(msg, err, fifoFile.ID())
errCh <- err
return
}
sectorIDCh <- id
}()
select {
case <-ctx.Done():
errStr := "context completed before CGO call could return"
strFmt := "%s (sinkPath=%s)"
log.Errorf(strFmt, errStr, fifoFile.ID())
return 0, xerrors.New(errStr)
case err := <-errCh:
errStr := "error streaming piece-bytes"
strFmt := "%s (sinkPath=%s)"
log.Errorf(strFmt, errStr, fifoFile.ID())
return 0, xerrors.Errorf("%w: %s", errStr, err)
case sectorID := <-sectorIDCh:
return sectorID, nil
}
func (sb *SectorBuilder) AddPiece(pieceKey string, pieceSize uint64, piecePath string) (uint64, error) {
return sectorbuilder.AddPiece(sb.handle, pieceKey, pieceSize, piecePath)
}
// TODO: should *really really* return an io.ReadCloser