f3c58174c4
downloads still need the old copy code because they aren't
parallel in the same way uploads are. revert all the code
that removed the parallel copy, only use the non-parallel
copy for uploads, and add back the parallelism and chunk
size flags and have them set the maximum concurrent pieces
flags to values based on each other when only one is set
for backwards compatibility.
mostly reverts 54ef1c8ca2
Change-Id: I8b5f62bf18a6548fa60865c6c61b5f34fbcec14c
294 lines
5.5 KiB
Go
294 lines
5.5 KiB
Go
// Copyright (C) 2021 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package ulfs
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/zeebo/errs"
|
|
)
|
|
|
|
//
|
|
// read handles
|
|
//
|
|
|
|
// GenericReader is an interface that can be turned into a GenericMultiReadHandle.
|
|
type GenericReader interface {
|
|
io.Closer
|
|
io.ReaderAt
|
|
}
|
|
|
|
// NewGenericMultiReadHandle implements MultiReadHandle for *os.Files.
|
|
func NewGenericMultiReadHandle(r GenericReader, info ObjectInfo) *GenericMultiReadHandle {
|
|
return &GenericMultiReadHandle{
|
|
r: r,
|
|
info: info,
|
|
}
|
|
}
|
|
|
|
// GenericMultiReadHandle can turn any GenericReader into a MultiReadHandle.
|
|
type GenericMultiReadHandle struct {
|
|
r GenericReader
|
|
info ObjectInfo
|
|
|
|
mu sync.Mutex
|
|
off int64
|
|
done bool
|
|
}
|
|
|
|
// Close closes the GenericMultiReadHandle.
|
|
func (o *GenericMultiReadHandle) Close() error {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if o.done {
|
|
return nil
|
|
}
|
|
o.done = true
|
|
|
|
return o.r.Close()
|
|
}
|
|
|
|
// SetOffset will set the offset for the next call to NextPart.
|
|
func (o *GenericMultiReadHandle) SetOffset(offset int64) error {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if o.done {
|
|
return errs.New("already closed")
|
|
}
|
|
|
|
o.off = offset
|
|
return nil
|
|
}
|
|
|
|
// NextPart returns a ReadHandle of length bytes at the current offset.
|
|
func (o *GenericMultiReadHandle) NextPart(ctx context.Context, length int64) (ReadHandle, error) {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if o.done {
|
|
return nil, errs.New("already closed")
|
|
}
|
|
|
|
if o.off < 0 {
|
|
o.off += o.info.ContentLength
|
|
}
|
|
if o.off < 0 || o.off > o.info.ContentLength {
|
|
return nil, errs.New("invalid offset: %d for length %d", o.off, o.info.ContentLength)
|
|
}
|
|
if o.off == o.info.ContentLength {
|
|
return nil, io.EOF
|
|
}
|
|
if length < 0 {
|
|
length = o.info.ContentLength
|
|
}
|
|
if o.off+length > o.info.ContentLength {
|
|
length = o.info.ContentLength - o.off
|
|
}
|
|
|
|
r := &genericReadHandle{
|
|
r: o.r,
|
|
info: o.info,
|
|
off: o.off,
|
|
len: length,
|
|
}
|
|
o.off += length
|
|
|
|
return r, nil
|
|
}
|
|
|
|
// Info returns the object info.
|
|
func (o *GenericMultiReadHandle) Info(ctx context.Context) (*ObjectInfo, error) {
|
|
info := o.info
|
|
return &info, nil
|
|
}
|
|
|
|
// Length returns the size of the object.
|
|
func (o *GenericMultiReadHandle) Length() int64 {
|
|
return o.info.ContentLength
|
|
}
|
|
|
|
type genericReadHandle struct {
|
|
r GenericReader
|
|
info ObjectInfo
|
|
off int64
|
|
len int64
|
|
}
|
|
|
|
func (o *genericReadHandle) Close() error { return nil }
|
|
func (o *genericReadHandle) Info() ObjectInfo { return o.info }
|
|
|
|
func (o *genericReadHandle) Read(p []byte) (int, error) {
|
|
if o.len <= 0 {
|
|
return 0, io.EOF
|
|
} else if o.len < int64(len(p)) {
|
|
p = p[:o.len]
|
|
}
|
|
n, err := o.r.ReadAt(p, o.off)
|
|
o.off += int64(n)
|
|
o.len -= int64(n)
|
|
return n, err
|
|
}
|
|
|
|
//
|
|
// write handles
|
|
//
|
|
|
|
// GenericWriter is an interface that can be turned into a GenericMultiWriteHandle.
|
|
type GenericWriter interface {
|
|
io.WriterAt
|
|
Commit() error
|
|
Abort() error
|
|
}
|
|
|
|
// GenericMultiWriteHandle implements MultiWriteHandle for *os.Files.
|
|
type GenericMultiWriteHandle struct {
|
|
w GenericWriter
|
|
|
|
mu sync.Mutex
|
|
off int64
|
|
tail bool
|
|
done bool
|
|
abort bool
|
|
}
|
|
|
|
// NewGenericMultiWriteHandle constructs an *GenericMultiWriteHandle from a GenericWriter.
|
|
func NewGenericMultiWriteHandle(w GenericWriter) *GenericMultiWriteHandle {
|
|
return &GenericMultiWriteHandle{
|
|
w: w,
|
|
}
|
|
}
|
|
|
|
func (o *GenericMultiWriteHandle) childAbort() {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if !o.done {
|
|
o.abort = true
|
|
}
|
|
}
|
|
|
|
func (o *GenericMultiWriteHandle) status() (done, abort bool) {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
return o.done, o.abort
|
|
}
|
|
|
|
// NextPart returns a WriteHandle expecting length bytes to be written to it.
|
|
func (o *GenericMultiWriteHandle) NextPart(ctx context.Context, length int64) (WriteHandle, error) {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if o.done {
|
|
return nil, errs.New("already closed")
|
|
} else if o.tail {
|
|
return nil, errs.New("unable to make part after tail part")
|
|
}
|
|
|
|
w := &genericWriteHandle{
|
|
parent: o,
|
|
w: o.w,
|
|
off: o.off,
|
|
tail: length < 0,
|
|
len: length,
|
|
}
|
|
|
|
if w.tail {
|
|
o.tail = true
|
|
} else {
|
|
o.off += length
|
|
}
|
|
|
|
return w, nil
|
|
}
|
|
|
|
// Commit commits the overall GenericMultiWriteHandle. It errors if
|
|
// any parts were aborted.
|
|
func (o *GenericMultiWriteHandle) Commit(ctx context.Context) error {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if o.done {
|
|
return nil
|
|
}
|
|
o.done = true
|
|
|
|
if o.abort {
|
|
return errs.Combine(
|
|
errs.New("commit failed: not every child was committed"),
|
|
o.w.Abort(),
|
|
)
|
|
}
|
|
|
|
return o.w.Commit()
|
|
}
|
|
|
|
// Abort aborts the overall GenericMultiWriteHandle.
|
|
func (o *GenericMultiWriteHandle) Abort(ctx context.Context) error {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
|
|
if o.done {
|
|
return nil
|
|
}
|
|
o.done = true
|
|
o.abort = true
|
|
|
|
return o.w.Abort()
|
|
}
|
|
|
|
type genericWriteHandle struct {
|
|
parent *GenericMultiWriteHandle
|
|
w GenericWriter
|
|
done bool
|
|
off int64
|
|
tail bool
|
|
len int64
|
|
}
|
|
|
|
func (o *genericWriteHandle) Write(p []byte) (int, error) {
|
|
if !o.tail {
|
|
if o.len <= 0 {
|
|
return 0, errs.New("write past maximum length")
|
|
} else if o.len < int64(len(p)) {
|
|
p = p[:o.len]
|
|
}
|
|
}
|
|
n, err := o.w.WriteAt(p, o.off)
|
|
o.off += int64(n)
|
|
if !o.tail {
|
|
o.len -= int64(n)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func (o *genericWriteHandle) Commit() error {
|
|
if o.done {
|
|
return nil
|
|
}
|
|
o.done = true
|
|
|
|
done, abort := o.parent.status()
|
|
if abort {
|
|
return errs.New("commit failed: parent write handle aborted")
|
|
} else if done {
|
|
return errs.New("commit failed: parent write handle done")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (o *genericWriteHandle) Abort() error {
|
|
if o.done {
|
|
return nil
|
|
}
|
|
o.done = true
|
|
|
|
o.parent.childAbort()
|
|
return nil
|
|
}
|