blob: 2ec41dd4ff09454071af4ebbebd909df6213c710 [file] [log] [blame]
// 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 {
buf := p.getNoInit()
buf.init(p.bufferSize)
return buf
}
// get gets a new buffer from p without initializing it.
func (p *pool) getNoInit() *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]
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
buf.Reset()
}
// 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
}
}
}