summaryrefslogtreecommitdiffhomepage
path: root/pkg/buffer
diff options
context:
space:
mode:
authorgVisor bot <gvisor-bot@google.com>2020-09-23 00:59:55 +0000
committergVisor bot <gvisor-bot@google.com>2020-09-23 00:59:55 +0000
commit43ee43819bf4f2e433065def7908aa57f493f5a7 (patch)
tree87659ee64420e96ff3789ffb9d8ee71b8587df1d /pkg/buffer
parent3b2f12d245c6973479d5286e87355f33bada6c0f (diff)
parentc0f21bb19a0ff0fd4bc3bd1f0bed9171e43faf68 (diff)
Merge release-20200914.0-135-gc0f21bb19 (automated)
Diffstat (limited to 'pkg/buffer')
-rw-r--r--pkg/buffer/buffer.go35
-rw-r--r--pkg/buffer/buffer_state_autogen.go29
-rw-r--r--pkg/buffer/pool.go83
-rw-r--r--pkg/buffer/safemem.go4
-rw-r--r--pkg/buffer/view.go13
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)
}