diff options
Diffstat (limited to 'pkg/buffer')
-rw-r--r-- | pkg/buffer/buffer.go | 35 | ||||
-rw-r--r-- | pkg/buffer/buffer_state_autogen.go | 29 | ||||
-rw-r--r-- | pkg/buffer/pool.go | 83 | ||||
-rw-r--r-- | pkg/buffer/safemem.go | 4 | ||||
-rw-r--r-- | pkg/buffer/view.go | 13 |
5 files changed, 130 insertions, 34 deletions
diff --git a/pkg/buffer/buffer.go b/pkg/buffer/buffer.go index c6d089fd9..311808ae9 100644 --- a/pkg/buffer/buffer.go +++ b/pkg/buffer/buffer.go @@ -14,36 +14,26 @@ // Package buffer provides the implementation of a buffer view. // -// A view is an flexible buffer, backed by a pool, supporting the safecopy -// operations natively as well as the ability to grow via either prepend or -// append, as well as shrink. +// A view is an flexible buffer, supporting the safecopy operations natively as +// well as the ability to grow via either prepend or append, as well as shrink. package buffer -import ( - "sync" -) - -const bufferSize = 8144 // See below. - // buffer encapsulates a queueable byte buffer. // -// Note that the total size is slightly less than two pages. This is done -// intentionally to ensure that the buffer object aligns with runtime -// internals. We have no hard size or alignment requirements. This two page -// size will effectively minimize internal fragmentation, but still have a -// large enough chunk to limit excessive segmentation. -// // +stateify savable type buffer struct { - data [bufferSize]byte + data []byte read int write int bufferEntry } -// reset resets internal data. -// -// This must be called before returning the buffer to the pool. +// init performs in-place initialization for zero value. +func (b *buffer) init(size int) { + b.data = make([]byte, size) +} + +// Reset resets read and write locations, effectively emptying the buffer. func (b *buffer) Reset() { b.read = 0 b.write = 0 @@ -85,10 +75,3 @@ func (b *buffer) WriteMove(n int) { func (b *buffer) WriteSlice() []byte { return b.data[b.write:] } - -// bufferPool is a pool for buffers. -var bufferPool = sync.Pool{ - New: func() interface{} { - return new(buffer) - }, -} diff --git a/pkg/buffer/buffer_state_autogen.go b/pkg/buffer/buffer_state_autogen.go index 33887cb66..8ba19e5f9 100644 --- a/pkg/buffer/buffer_state_autogen.go +++ b/pkg/buffer/buffer_state_autogen.go @@ -90,6 +90,31 @@ func (x *bufferEntry) StateLoad(m state.Source) { m.Load(1, &x.prev) } +func (x *pool) StateTypeName() string { + return "pkg/buffer.pool" +} + +func (x *pool) StateFields() []string { + return []string{ + "bufferSize", + "embeddedStorage", + } +} + +func (x *pool) beforeSave() {} + +func (x *pool) StateSave(m state.Sink) { + x.beforeSave() + m.Save(0, &x.bufferSize) + m.Save(1, &x.embeddedStorage) +} + +func (x *pool) StateLoad(m state.Source) { + m.Load(0, &x.bufferSize) + m.LoadWait(1, &x.embeddedStorage) + m.AfterLoad(x.afterLoad) +} + func (x *View) StateTypeName() string { return "pkg/buffer.View" } @@ -98,6 +123,7 @@ func (x *View) StateFields() []string { return []string{ "data", "size", + "pool", } } @@ -107,6 +133,7 @@ func (x *View) StateSave(m state.Sink) { x.beforeSave() m.Save(0, &x.data) m.Save(1, &x.size) + m.Save(2, &x.pool) } func (x *View) afterLoad() {} @@ -114,11 +141,13 @@ func (x *View) afterLoad() {} func (x *View) StateLoad(m state.Source) { m.Load(0, &x.data) m.Load(1, &x.size) + m.Load(2, &x.pool) } func init() { state.Register((*buffer)(nil)) state.Register((*bufferList)(nil)) state.Register((*bufferEntry)(nil)) + state.Register((*pool)(nil)) state.Register((*View)(nil)) } diff --git a/pkg/buffer/pool.go b/pkg/buffer/pool.go new file mode 100644 index 000000000..7ad6132ab --- /dev/null +++ b/pkg/buffer/pool.go @@ -0,0 +1,83 @@ +// Copyright 2020 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package buffer + +const ( + // embeddedCount is the number of buffer structures embedded in the pool. It + // is also the number for overflow allocations. + embeddedCount = 8 + + // defaultBufferSize is the default size for each underlying storage buffer. + // + // It is slightly less than two pages. This is done intentionally to ensure + // that the buffer object aligns with runtime internals. This two page size + // will effectively minimize internal fragmentation, but still have a large + // enough chunk to limit excessive segmentation. + defaultBufferSize = 8144 +) + +// pool allocates buffer. +// +// It contains an embedded buffer storage for fast path when the number of +// buffers needed is small. +// +// +stateify savable +type pool struct { + bufferSize int + avail []buffer `state:"nosave"` + embeddedStorage [embeddedCount]buffer `state:"wait"` +} + +// get gets a new buffer from p. +func (p *pool) get() *buffer { + if p.avail == nil { + p.avail = p.embeddedStorage[:] + } + if len(p.avail) == 0 { + p.avail = make([]buffer, embeddedCount) + } + if p.bufferSize <= 0 { + p.bufferSize = defaultBufferSize + } + buf := &p.avail[0] + buf.init(p.bufferSize) + p.avail = p.avail[1:] + return buf +} + +// put releases buf. +func (p *pool) put(buf *buffer) { + // Remove reference to the underlying storage, allowing it to be garbage + // collected. + buf.data = nil +} + +// setBufferSize sets the size of underlying storage buffer for future +// allocations. It can be called at any time. +func (p *pool) setBufferSize(size int) { + p.bufferSize = size +} + +// afterLoad is invoked by stateify. +func (p *pool) afterLoad() { + // S/R does not save subslice into embeddedStorage correctly. Restore + // available portion of embeddedStorage manually. Restore as nil if none used. + for i := len(p.embeddedStorage); i > 0; i-- { + if p.embeddedStorage[i-1].data != nil { + p.avail = p.embeddedStorage[i:] + break + } + } +} diff --git a/pkg/buffer/safemem.go b/pkg/buffer/safemem.go index b789e56e9..8b42575b4 100644 --- a/pkg/buffer/safemem.go +++ b/pkg/buffer/safemem.go @@ -44,7 +44,7 @@ func (v *View) WriteFromSafememReader(r safemem.Reader, count uint64) (uint64, e // Need at least one buffer. firstBuf := v.data.Back() if firstBuf == nil { - firstBuf = bufferPool.Get().(*buffer) + firstBuf = v.pool.get() v.data.PushBack(firstBuf) } @@ -56,7 +56,7 @@ func (v *View) WriteFromSafememReader(r safemem.Reader, count uint64) (uint64, e count -= l blocks = append(blocks, firstBuf.WriteBlock()) for count > 0 { - emptyBuf := bufferPool.Get().(*buffer) + emptyBuf := v.pool.get() v.data.PushBack(emptyBuf) block := emptyBuf.WriteBlock().TakeFirst64(count) count -= uint64(block.Len()) diff --git a/pkg/buffer/view.go b/pkg/buffer/view.go index e6901eadb..00652d675 100644 --- a/pkg/buffer/view.go +++ b/pkg/buffer/view.go @@ -27,6 +27,7 @@ import ( type View struct { data bufferList size int64 + pool pool } // TrimFront removes the first count bytes from the buffer. @@ -81,7 +82,7 @@ func (v *View) advanceRead(count int64) { buf = buf.Next() // Iterate. v.data.Remove(oldBuf) oldBuf.Reset() - bufferPool.Put(oldBuf) + v.pool.put(oldBuf) // Update counts. count -= sz @@ -118,7 +119,7 @@ func (v *View) Truncate(length int64) { // Drop the buffer completely; see above. v.data.Remove(buf) buf.Reset() - bufferPool.Put(buf) + v.pool.put(buf) v.size -= sz } } @@ -137,7 +138,7 @@ func (v *View) Grow(length int64, zero bool) { // Is there some space in the last buffer? if buf == nil || buf.Full() { - buf = bufferPool.Get().(*buffer) + buf = v.pool.get() v.data.PushBack(buf) } @@ -181,7 +182,7 @@ func (v *View) Prepend(data []byte) { for len(data) > 0 { // Do we need an empty buffer? - buf := bufferPool.Get().(*buffer) + buf := v.pool.get() v.data.PushFront(buf) // The buffer is empty; copy last chunk. @@ -211,7 +212,7 @@ func (v *View) Append(data []byte) { // Ensure there's a buffer with space. if buf == nil || buf.Full() { - buf = bufferPool.Get().(*buffer) + buf = v.pool.get() v.data.PushBack(buf) } @@ -297,7 +298,7 @@ func (v *View) WriteFromReader(r io.Reader, count int64) (int64, error) { // Ensure we have an empty buffer. if buf == nil || buf.Full() { - buf = bufferPool.Get().(*buffer) + buf = v.pool.get() v.data.PushBack(buf) } |