mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-22 13:00:19 +00:00
204 lines
6.4 KiB
Go
204 lines
6.4 KiB
Go
|
/*
|
||
|
Copyright 2020 The Kubernetes 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 value
|
||
|
|
||
|
// Allocator provides a value object allocation strategy.
|
||
|
// Value objects can be allocated by passing an allocator to the "Using"
|
||
|
// receiver functions on the value interfaces, e.g. Map.ZipUsing(allocator, ...).
|
||
|
// Value objects returned from "Using" functions should be given back to the allocator
|
||
|
// once longer needed by calling Allocator.Free(Value).
|
||
|
type Allocator interface {
|
||
|
// Free gives the allocator back any value objects returned by the "Using"
|
||
|
// receiver functions on the value interfaces.
|
||
|
// interface{} may be any of: Value, Map, List or Range.
|
||
|
Free(interface{})
|
||
|
|
||
|
// The unexported functions are for "Using" receiver functions of the value types
|
||
|
// to request what they need from the allocator.
|
||
|
allocValueUnstructured() *valueUnstructured
|
||
|
allocListUnstructuredRange() *listUnstructuredRange
|
||
|
allocValueReflect() *valueReflect
|
||
|
allocMapReflect() *mapReflect
|
||
|
allocStructReflect() *structReflect
|
||
|
allocListReflect() *listReflect
|
||
|
allocListReflectRange() *listReflectRange
|
||
|
}
|
||
|
|
||
|
// HeapAllocator simply allocates objects to the heap. It is the default
|
||
|
// allocator used receiver functions on the value interfaces that do not accept
|
||
|
// an allocator and should be used whenever allocating objects that will not
|
||
|
// be given back to an allocator by calling Allocator.Free(Value).
|
||
|
var HeapAllocator = &heapAllocator{}
|
||
|
|
||
|
type heapAllocator struct{}
|
||
|
|
||
|
func (p *heapAllocator) allocValueUnstructured() *valueUnstructured {
|
||
|
return &valueUnstructured{}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) allocListUnstructuredRange() *listUnstructuredRange {
|
||
|
return &listUnstructuredRange{vv: &valueUnstructured{}}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) allocValueReflect() *valueReflect {
|
||
|
return &valueReflect{}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) allocStructReflect() *structReflect {
|
||
|
return &structReflect{}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) allocMapReflect() *mapReflect {
|
||
|
return &mapReflect{}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) allocListReflect() *listReflect {
|
||
|
return &listReflect{}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) allocListReflectRange() *listReflectRange {
|
||
|
return &listReflectRange{vr: &valueReflect{}}
|
||
|
}
|
||
|
|
||
|
func (p *heapAllocator) Free(_ interface{}) {}
|
||
|
|
||
|
// NewFreelistAllocator creates freelist based allocator.
|
||
|
// This allocator provides fast allocation and freeing of short lived value objects.
|
||
|
//
|
||
|
// The freelists are bounded in size by freelistMaxSize. If more than this amount of value objects is
|
||
|
// allocated at once, the excess will be returned to the heap for garbage collection when freed.
|
||
|
//
|
||
|
// This allocator is unsafe and must not be accessed concurrently by goroutines.
|
||
|
//
|
||
|
// This allocator works well for traversal of value data trees. Typical usage is to acquire
|
||
|
// a freelist at the beginning of the traversal and use it through out
|
||
|
// for all temporary value access.
|
||
|
func NewFreelistAllocator() Allocator {
|
||
|
return &freelistAllocator{
|
||
|
valueUnstructured: &freelist{new: func() interface{} {
|
||
|
return &valueUnstructured{}
|
||
|
}},
|
||
|
listUnstructuredRange: &freelist{new: func() interface{} {
|
||
|
return &listUnstructuredRange{vv: &valueUnstructured{}}
|
||
|
}},
|
||
|
valueReflect: &freelist{new: func() interface{} {
|
||
|
return &valueReflect{}
|
||
|
}},
|
||
|
mapReflect: &freelist{new: func() interface{} {
|
||
|
return &mapReflect{}
|
||
|
}},
|
||
|
structReflect: &freelist{new: func() interface{} {
|
||
|
return &structReflect{}
|
||
|
}},
|
||
|
listReflect: &freelist{new: func() interface{} {
|
||
|
return &listReflect{}
|
||
|
}},
|
||
|
listReflectRange: &freelist{new: func() interface{} {
|
||
|
return &listReflectRange{vr: &valueReflect{}}
|
||
|
}},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bound memory usage of freelists. This prevents the processing of very large lists from leaking memory.
|
||
|
// This limit is large enough for endpoints objects containing 1000 IP address entries. Freed objects
|
||
|
// that don't fit into the freelist are orphaned on the heap to be garbage collected.
|
||
|
const freelistMaxSize = 1000
|
||
|
|
||
|
type freelistAllocator struct {
|
||
|
valueUnstructured *freelist
|
||
|
listUnstructuredRange *freelist
|
||
|
valueReflect *freelist
|
||
|
mapReflect *freelist
|
||
|
structReflect *freelist
|
||
|
listReflect *freelist
|
||
|
listReflectRange *freelist
|
||
|
}
|
||
|
|
||
|
type freelist struct {
|
||
|
list []interface{}
|
||
|
new func() interface{}
|
||
|
}
|
||
|
|
||
|
func (f *freelist) allocate() interface{} {
|
||
|
var w2 interface{}
|
||
|
if n := len(f.list); n > 0 {
|
||
|
w2, f.list = f.list[n-1], f.list[:n-1]
|
||
|
} else {
|
||
|
w2 = f.new()
|
||
|
}
|
||
|
return w2
|
||
|
}
|
||
|
|
||
|
func (f *freelist) free(v interface{}) {
|
||
|
if len(f.list) < freelistMaxSize {
|
||
|
f.list = append(f.list, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) Free(value interface{}) {
|
||
|
switch v := value.(type) {
|
||
|
case *valueUnstructured:
|
||
|
v.Value = nil // don't hold references to unstructured objects
|
||
|
w.valueUnstructured.free(v)
|
||
|
case *listUnstructuredRange:
|
||
|
v.vv.Value = nil // don't hold references to unstructured objects
|
||
|
w.listUnstructuredRange.free(v)
|
||
|
case *valueReflect:
|
||
|
v.ParentMapKey = nil
|
||
|
v.ParentMap = nil
|
||
|
w.valueReflect.free(v)
|
||
|
case *mapReflect:
|
||
|
w.mapReflect.free(v)
|
||
|
case *structReflect:
|
||
|
w.structReflect.free(v)
|
||
|
case *listReflect:
|
||
|
w.listReflect.free(v)
|
||
|
case *listReflectRange:
|
||
|
v.vr.ParentMapKey = nil
|
||
|
v.vr.ParentMap = nil
|
||
|
w.listReflectRange.free(v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocValueUnstructured() *valueUnstructured {
|
||
|
return w.valueUnstructured.allocate().(*valueUnstructured)
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocListUnstructuredRange() *listUnstructuredRange {
|
||
|
return w.listUnstructuredRange.allocate().(*listUnstructuredRange)
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocValueReflect() *valueReflect {
|
||
|
return w.valueReflect.allocate().(*valueReflect)
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocStructReflect() *structReflect {
|
||
|
return w.structReflect.allocate().(*structReflect)
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocMapReflect() *mapReflect {
|
||
|
return w.mapReflect.allocate().(*mapReflect)
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocListReflect() *listReflect {
|
||
|
return w.listReflect.allocate().(*listReflect)
|
||
|
}
|
||
|
|
||
|
func (w *freelistAllocator) allocListReflectRange() *listReflectRange {
|
||
|
return w.listReflectRange.allocate().(*listReflectRange)
|
||
|
}
|