File: safe.go

package info (click to toggle)
golang-github-aws-smithy-go 1.20.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,116 kB
  • sloc: java: 19,678; xml: 166; sh: 131; makefile: 70
file content (75 lines) | stat: -rw-r--r-- 2,206 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package io

import (
	"io"
	"sync"
)

// NewSafeReadCloser returns a new safeReadCloser that wraps readCloser.
func NewSafeReadCloser(readCloser io.ReadCloser) io.ReadCloser {
	sr := &safeReadCloser{
		readCloser: readCloser,
	}

	if _, ok := readCloser.(io.WriterTo); ok {
		return &safeWriteToReadCloser{safeReadCloser: sr}
	}

	return sr
}

// safeWriteToReadCloser wraps a safeReadCloser but exposes a WriteTo interface implementation. This will panic
// if the underlying io.ReadClose does not support WriteTo. Use NewSafeReadCloser to ensure the proper handling of this
// type.
type safeWriteToReadCloser struct {
	*safeReadCloser
}

// WriteTo implements the io.WriteTo interface.
func (r *safeWriteToReadCloser) WriteTo(w io.Writer) (int64, error) {
	r.safeReadCloser.mtx.Lock()
	defer r.safeReadCloser.mtx.Unlock()

	if r.safeReadCloser.closed {
		return 0, io.EOF
	}

	return r.safeReadCloser.readCloser.(io.WriterTo).WriteTo(w)
}

// safeReadCloser wraps a io.ReadCloser and presents an io.ReadCloser interface. When Close is called on safeReadCloser
// the underlying Close method will be executed, and then the reference to the reader will be dropped. This type
// is meant to be used with the net/http library which will retain a reference to the request body for the lifetime
// of a goroutine connection. Wrapping in this manner will ensure that no data race conditions are falsely reported.
// This type is thread-safe.
type safeReadCloser struct {
	readCloser io.ReadCloser
	closed     bool
	mtx        sync.Mutex
}

// Read reads up to len(p) bytes into p from the underlying read. If the reader is closed io.EOF will be returned.
func (r *safeReadCloser) Read(p []byte) (n int, err error) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	if r.closed {
		return 0, io.EOF
	}

	return r.readCloser.Read(p)
}

// Close calls the underlying io.ReadCloser's Close method, removes the reference to the reader, and returns any error
// reported from Close. Subsequent calls to Close will always return a nil error.
func (r *safeReadCloser) Close() error {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	if r.closed {
		return nil
	}

	r.closed = true
	rc := r.readCloser
	r.readCloser = nil
	return rc.Close()
}