| // 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 |
| } |
| } |
| } |