Skip to content

Commit d449f2e

Browse files
committed
improve buffer pool usage
This commit improves the internal buffer pool usage. Now, recycling a buffer pool **guarantees** that the buffer is returned to the pool only once. This allows readers and writers to return buffers in more scenarios and as soon as possible. Signed-off-by: Andreas Auernhammer <[email protected]>
1 parent 3cd3734 commit d449f2e

File tree

7 files changed

+153
-154
lines changed

7 files changed

+153
-154
lines changed

.github/workflows/codeql.yml

Lines changed: 0 additions & 47 deletions
This file was deleted.

.github/workflows/go.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
go-version: [1.22.x]
17+
go-version: [1.24.0]
1818
steps:
1919
- name: Set up Go ${{ matrix.go-version }}
2020
uses: actions/setup-go@v2
@@ -41,7 +41,7 @@ jobs:
4141
runs-on: ${{ matrix.os }}
4242
strategy:
4343
matrix:
44-
go-version: [1.21.x, 1.22.x]
44+
go-version: 1.24.0
4545
os: [ubuntu-latest, windows-latest, macos-latest]
4646
steps:
4747
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
module github.com/minio/sio
22

3+
go 1.24
4+
35
require (
46
golang.org/x/crypto v0.23.0
57
golang.org/x/sys v0.20.0
68
)
79

810
require golang.org/x/term v0.20.0
9-
10-
go 1.18

reader-v1.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type encReaderV10 struct {
2424
src io.Reader
2525

2626
buffer packageV10
27+
recycle func() // Returns the buffer to the pool
2728
offset int
2829
payloadSize int
2930
stateErr error
@@ -36,19 +37,16 @@ func encryptReaderV10(src io.Reader, config *Config) (*encReaderV10, error) {
3637
if err != nil {
3738
return nil, err
3839
}
40+
buffer, recycle := getBuffer()
3941
return &encReaderV10{
4042
authEncV10: ae,
4143
src: src,
42-
buffer: packageBufferPool.Get().([]byte)[:maxPackageSize],
44+
buffer: buffer,
45+
recycle: recycle,
4346
payloadSize: config.PayloadSize,
4447
}, nil
4548
}
4649

47-
func (r *encReaderV10) recycle() {
48-
recyclePackageBufferPool(r.buffer)
49-
r.buffer = nil
50-
}
51-
5250
func (r *encReaderV10) Read(p []byte) (int, error) {
5351
if r.stateErr != nil {
5452
return 0, r.stateErr
@@ -99,6 +97,7 @@ type decReaderV10 struct {
9997
src io.Reader
10098

10199
buffer packageV10
100+
recycle func() // Returns the buffer to the pool
102101
offset int
103102
stateErr error
104103
}
@@ -110,18 +109,15 @@ func decryptReaderV10(src io.Reader, config *Config) (*decReaderV10, error) {
110109
if err != nil {
111110
return nil, err
112111
}
112+
buffer, recycle := getBuffer()
113113
return &decReaderV10{
114114
authDecV10: ad,
115115
src: src,
116-
buffer: packageBufferPool.Get().([]byte)[:maxPackageSize],
116+
buffer: buffer,
117+
recycle: recycle,
117118
}, nil
118119
}
119120

120-
func (r *decReaderV10) recycle() {
121-
recyclePackageBufferPool(r.buffer)
122-
r.buffer = nil
123-
}
124-
125121
func (r *decReaderV10) Read(p []byte) (n int, err error) {
126122
if r.stateErr != nil {
127123
return 0, r.stateErr
@@ -226,12 +222,14 @@ func (r *decReaderAtV10) ReadAt(p []byte, offset int64) (n int, err error) {
226222
return 0, errUnexpectedSize
227223
}
228224

229-
buffer := packageBufferPool.Get().([]byte)[:maxPackageSize]
230-
defer recyclePackageBufferPool(buffer)
225+
buffer, recycle := getBuffer()
226+
defer recycle()
227+
231228
decReader := decReaderV10{
232229
authDecV10: r.ad,
233230
src: &sectionReader{r.src, t * maxPackageSize},
234231
buffer: packageV10(buffer),
232+
recycle: recycle,
235233
offset: 0,
236234
}
237235
decReader.SeqNum = uint32(t)

reader-v2.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,27 @@ type encReaderV20 struct {
2525
src io.Reader
2626

2727
buffer packageV20
28+
recycle func() // Returns the buffer to the pool
2829
offset int
2930
lastByte byte
3031
stateErr error
3132

3233
firstRead bool
3334
}
3435

35-
var packageBufferPool = sync.Pool{New: func() interface{} { return make([]byte, maxPackageSize) }}
36+
var packageBufferPool = sync.Pool{
37+
New: func() any {
38+
b := make([]byte, maxPackageSize)
39+
return &b
40+
},
41+
}
3642

37-
func recyclePackageBufferPool(b []byte) {
38-
if cap(b) < maxPackageSize {
39-
return
40-
}
41-
// Clear so we don't potentially leak info between callers
42-
for i := range b {
43-
b[i] = 0
44-
}
45-
packageBufferPool.Put(b)
43+
func getBuffer() ([]byte, func()) {
44+
p := packageBufferPool.Get().(*[]byte)
45+
return *p, sync.OnceFunc(func() {
46+
clear(*p) // Clear to avoid leaking data between callers
47+
packageBufferPool.Put(p)
48+
})
4649
}
4750

4851
// encryptReaderV20 returns an io.Reader wrapping the given io.Reader.
@@ -52,19 +55,17 @@ func encryptReaderV20(src io.Reader, config *Config) (*encReaderV20, error) {
5255
if err != nil {
5356
return nil, err
5457
}
58+
59+
buffer, recycle := getBuffer()
5560
return &encReaderV20{
5661
authEncV20: ae,
5762
src: src,
58-
buffer: packageBufferPool.Get().([]byte)[:maxPackageSize],
63+
buffer: buffer,
64+
recycle: recycle,
5965
firstRead: true,
6066
}, nil
6167
}
6268

63-
func (r *encReaderV20) recycle() {
64-
recyclePackageBufferPool(r.buffer)
65-
r.buffer = nil
66-
}
67-
6869
func (r *encReaderV20) Read(p []byte) (n int, err error) {
6970
if r.stateErr != nil {
7071
return 0, r.stateErr
@@ -146,6 +147,7 @@ type decReaderV20 struct {
146147

147148
stateErr error
148149
buffer packageV20
150+
recycle func()
149151
offset int
150152
}
151153

@@ -156,18 +158,15 @@ func decryptReaderV20(src io.Reader, config *Config) (*decReaderV20, error) {
156158
if err != nil {
157159
return nil, err
158160
}
161+
buffer, recycle := getBuffer()
159162
return &decReaderV20{
160163
authDecV20: ad,
161164
src: src,
162-
buffer: packageBufferPool.Get().([]byte)[:maxPackageSize],
165+
buffer: buffer,
166+
recycle: recycle,
163167
}, nil
164168
}
165169

166-
func (r *decReaderV20) recycle() {
167-
recyclePackageBufferPool(r.buffer)
168-
r.buffer = nil
169-
}
170-
171170
func (r *decReaderV20) Read(p []byte) (n int, err error) {
172171
if r.stateErr != nil {
173172
return 0, r.stateErr
@@ -296,13 +295,16 @@ func (r *decReaderAtV20) ReadAt(p []byte, offset int64) (n int, err error) {
296295
t--
297296
}
298297

298+
buffer, recycle := getBuffer()
299+
defer recycle()
300+
299301
decReader := decReaderV20{
300302
authDecV20: r.ad,
301303
src: &sectionReader{r.src, t * maxPackageSize},
302-
buffer: packageBufferPool.Get().([]byte)[:maxPackageSize],
304+
buffer: buffer,
305+
recycle: recycle,
303306
offset: 0,
304307
}
305-
defer decReader.recycle()
306308
decReader.SeqNum = uint32(t)
307309
if k > 0 {
308310
if _, err := io.CopyN(io.Discard, &decReader, k); err != nil {
@@ -312,7 +314,7 @@ func (r *decReaderAtV20) ReadAt(p []byte, offset int64) (n int, err error) {
312314

313315
for n < len(p) && err == nil {
314316
var nn int
315-
nn, err = (&decReader).Read(p[n:])
317+
nn, err = decReader.Read(p[n:])
316318
n += nn
317319
}
318320
if err == io.EOF && n == len(p) {

0 commit comments

Comments
 (0)