mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-27 00:55:17 +00:00
104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
|
// Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use
|
||
|
// of this source code is governed by a BSD-style license that can be found in
|
||
|
// the LICENSE file.
|
||
|
//
|
||
|
//go:build (!invariants && !tracing) || race
|
||
|
// +build !invariants,!tracing race
|
||
|
|
||
|
package cache
|
||
|
|
||
|
import (
|
||
|
"runtime"
|
||
|
"sync"
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/cockroachdb/pebble/internal/invariants"
|
||
|
"github.com/cockroachdb/pebble/internal/manual"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
entrySize = int(unsafe.Sizeof(entry{}))
|
||
|
entryAllocCacheLimit = 128
|
||
|
// Avoid using runtime.SetFinalizer in race builds as finalizers tickle a bug
|
||
|
// in the Go race detector in go1.15 and earlier versions. This requires that
|
||
|
// entries are Go allocated rather than manually allocated.
|
||
|
//
|
||
|
// If cgo is disabled we need to allocate the entries using the Go allocator
|
||
|
// and is violates the Go GC rules to put Go pointers (such as the entry
|
||
|
// pointer fields) into untyped memory (i.e. a []byte).
|
||
|
entriesGoAllocated = invariants.RaceEnabled || !cgoEnabled
|
||
|
)
|
||
|
|
||
|
var entryAllocPool = sync.Pool{
|
||
|
New: func() interface{} {
|
||
|
return newEntryAllocCache()
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func entryAllocNew() *entry {
|
||
|
a := entryAllocPool.Get().(*entryAllocCache)
|
||
|
e := a.alloc()
|
||
|
entryAllocPool.Put(a)
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
func entryAllocFree(e *entry) {
|
||
|
a := entryAllocPool.Get().(*entryAllocCache)
|
||
|
a.free(e)
|
||
|
entryAllocPool.Put(a)
|
||
|
}
|
||
|
|
||
|
type entryAllocCache struct {
|
||
|
entries []*entry
|
||
|
}
|
||
|
|
||
|
func newEntryAllocCache() *entryAllocCache {
|
||
|
c := &entryAllocCache{}
|
||
|
if !entriesGoAllocated {
|
||
|
// Note the use of a "real" finalizer here (as opposed to a build tag-gated
|
||
|
// no-op finalizer). Without the finalizer, objects released from the pool
|
||
|
// and subsequently GC'd by the Go runtime would fail to have their manually
|
||
|
// allocated memory freed, which results in a memory leak.
|
||
|
// lint:ignore SetFinalizer
|
||
|
runtime.SetFinalizer(c, freeEntryAllocCache)
|
||
|
}
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
func freeEntryAllocCache(obj interface{}) {
|
||
|
c := obj.(*entryAllocCache)
|
||
|
for i, e := range c.entries {
|
||
|
c.dealloc(e)
|
||
|
c.entries[i] = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *entryAllocCache) alloc() *entry {
|
||
|
n := len(c.entries)
|
||
|
if n == 0 {
|
||
|
if entriesGoAllocated {
|
||
|
return &entry{}
|
||
|
}
|
||
|
b := manual.New(entrySize)
|
||
|
return (*entry)(unsafe.Pointer(&b[0]))
|
||
|
}
|
||
|
e := c.entries[n-1]
|
||
|
c.entries = c.entries[:n-1]
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
func (c *entryAllocCache) dealloc(e *entry) {
|
||
|
if !entriesGoAllocated {
|
||
|
buf := (*[manual.MaxArrayLen]byte)(unsafe.Pointer(e))[:entrySize:entrySize]
|
||
|
manual.Free(buf)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *entryAllocCache) free(e *entry) {
|
||
|
if len(c.entries) == entryAllocCacheLimit {
|
||
|
c.dealloc(e)
|
||
|
return
|
||
|
}
|
||
|
c.entries = append(c.entries, e)
|
||
|
}
|