| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- 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
- }
|