forked from toolshed/abra
93
vendor/github.com/muesli/cancelreader/cancelreader.go
generated
vendored
Normal file
93
vendor/github.com/muesli/cancelreader/cancelreader.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
package cancelreader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrCanceled gets returned when trying to read from a canceled reader.
|
||||
var ErrCanceled = fmt.Errorf("read canceled")
|
||||
|
||||
// CancelReader is a io.Reader whose Read() calls can be canceled without data
|
||||
// being consumed. The cancelReader has to be closed.
|
||||
type CancelReader interface {
|
||||
io.ReadCloser
|
||||
|
||||
// Cancel cancels ongoing and future reads an returns true if it succeeded.
|
||||
Cancel() bool
|
||||
}
|
||||
|
||||
// File represents an input/output resource with a file descriptor.
|
||||
type File interface {
|
||||
io.ReadWriteCloser
|
||||
|
||||
// Fd returns its file descriptor
|
||||
Fd() uintptr
|
||||
|
||||
// Name returns its file name.
|
||||
Name() string
|
||||
}
|
||||
|
||||
// fallbackCancelReader implements cancelReader but does not actually support
|
||||
// cancelation during an ongoing Read() call. Thus, Cancel() always returns
|
||||
// false. However, after calling Cancel(), new Read() calls immediately return
|
||||
// errCanceled and don't consume any data anymore.
|
||||
type fallbackCancelReader struct {
|
||||
r io.Reader
|
||||
cancelMixin
|
||||
}
|
||||
|
||||
// newFallbackCancelReader is a fallback for NewReader that cannot actually
|
||||
// cancel an ongoing read but will immediately return on future reads if it has
|
||||
// been canceled.
|
||||
func newFallbackCancelReader(reader io.Reader) (CancelReader, error) {
|
||||
return &fallbackCancelReader{r: reader}, nil
|
||||
}
|
||||
|
||||
func (r *fallbackCancelReader) Read(data []byte) (int, error) {
|
||||
if r.isCanceled() {
|
||||
return 0, ErrCanceled
|
||||
}
|
||||
|
||||
n, err := r.r.Read(data)
|
||||
/*
|
||||
If the underlying reader is a blocking reader (e.g. an open connection),
|
||||
it might happen that 1 goroutine cancels the reader while its stuck in
|
||||
the read call waiting for something.
|
||||
If that happens, we should still cancel the read.
|
||||
*/
|
||||
if r.isCanceled() {
|
||||
return 0, ErrCanceled
|
||||
}
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (r *fallbackCancelReader) Cancel() bool {
|
||||
r.setCanceled()
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *fallbackCancelReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// cancelMixin represents a goroutine-safe cancelation status.
|
||||
type cancelMixin struct {
|
||||
unsafeCanceled bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (c *cancelMixin) isCanceled() bool {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.unsafeCanceled
|
||||
}
|
||||
|
||||
func (c *cancelMixin) setCanceled() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.unsafeCanceled = true
|
||||
}
|
Reference in New Issue
Block a user