github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/buffer/pool.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package buffer
    16  
    17  const (
    18  	// embeddedCount is the number of buffer structures embedded in the pool. It
    19  	// is also the number for overflow allocations.
    20  	embeddedCount = 8
    21  
    22  	// defaultBufferSize is the default size for each underlying storage buffer.
    23  	//
    24  	// It is slightly less than two pages. This is done intentionally to ensure
    25  	// that the buffer object aligns with runtime internals. This two page size
    26  	// will effectively minimize internal fragmentation, but still have a large
    27  	// enough chunk to limit excessive segmentation.
    28  	defaultBufferSize = 8144
    29  )
    30  
    31  // pool allocates buffer.
    32  //
    33  // It contains an embedded buffer storage for fast path when the number of
    34  // buffers needed is small.
    35  //
    36  // +stateify savable
    37  type pool struct {
    38  	bufferSize      int
    39  	avail           []buffer              `state:"nosave"`
    40  	embeddedStorage [embeddedCount]buffer `state:"wait"`
    41  }
    42  
    43  // get gets a new buffer from p.
    44  func (p *pool) get() *buffer {
    45  	buf := p.getNoInit()
    46  	buf.init(p.bufferSize)
    47  	return buf
    48  }
    49  
    50  // get gets a new buffer from p without initializing it.
    51  func (p *pool) getNoInit() *buffer {
    52  	if p.avail == nil {
    53  		p.avail = p.embeddedStorage[:]
    54  	}
    55  	if len(p.avail) == 0 {
    56  		p.avail = make([]buffer, embeddedCount)
    57  	}
    58  	if p.bufferSize <= 0 {
    59  		p.bufferSize = defaultBufferSize
    60  	}
    61  	buf := &p.avail[0]
    62  	p.avail = p.avail[1:]
    63  	return buf
    64  }
    65  
    66  // put releases buf.
    67  func (p *pool) put(buf *buffer) {
    68  	// Remove reference to the underlying storage, allowing it to be garbage
    69  	// collected.
    70  	buf.data = nil
    71  	buf.Reset()
    72  }
    73  
    74  // setBufferSize sets the size of underlying storage buffer for future
    75  // allocations. It can be called at any time.
    76  func (p *pool) setBufferSize(size int) {
    77  	p.bufferSize = size
    78  }
    79  
    80  // afterLoad is invoked by stateify.
    81  func (p *pool) afterLoad() {
    82  	// S/R does not save subslice into embeddedStorage correctly. Restore
    83  	// available portion of embeddedStorage manually. Restore as nil if none used.
    84  	for i := len(p.embeddedStorage); i > 0; i-- {
    85  		if p.embeddedStorage[i-1].data != nil {
    86  			p.avail = p.embeddedStorage[i:]
    87  			break
    88  		}
    89  	}
    90  }