github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/internal/cache/entry_normal.go (about) 1 // Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 // 5 //go:build (!invariants && !tracing) || race 6 // +build !invariants,!tracing race 7 8 package cache 9 10 import ( 11 "runtime" 12 "sync" 13 "unsafe" 14 15 "github.com/cockroachdb/pebble/internal/invariants" 16 "github.com/cockroachdb/pebble/internal/manual" 17 ) 18 19 const ( 20 entrySize = int(unsafe.Sizeof(entry{})) 21 entryAllocCacheLimit = 128 22 // Avoid using runtime.SetFinalizer in race builds as finalizers tickle a bug 23 // in the Go race detector in go1.15 and earlier versions. This requires that 24 // entries are Go allocated rather than manually allocated. 25 // 26 // If cgo is disabled we need to allocate the entries using the Go allocator 27 // and is violates the Go GC rules to put Go pointers (such as the entry 28 // pointer fields) into untyped memory (i.e. a []byte). 29 entriesGoAllocated = invariants.RaceEnabled || !cgoEnabled 30 ) 31 32 var entryAllocPool = sync.Pool{ 33 New: func() interface{} { 34 return newEntryAllocCache() 35 }, 36 } 37 38 func entryAllocNew() *entry { 39 a := entryAllocPool.Get().(*entryAllocCache) 40 e := a.alloc() 41 entryAllocPool.Put(a) 42 return e 43 } 44 45 func entryAllocFree(e *entry) { 46 a := entryAllocPool.Get().(*entryAllocCache) 47 a.free(e) 48 entryAllocPool.Put(a) 49 } 50 51 type entryAllocCache struct { 52 entries []*entry 53 } 54 55 func newEntryAllocCache() *entryAllocCache { 56 c := &entryAllocCache{} 57 if !entriesGoAllocated { 58 // Note the use of a "real" finalizer here (as opposed to a build tag-gated 59 // no-op finalizer). Without the finalizer, objects released from the pool 60 // and subsequently GC'd by the Go runtime would fail to have their manually 61 // allocated memory freed, which results in a memory leak. 62 // lint:ignore SetFinalizer 63 runtime.SetFinalizer(c, freeEntryAllocCache) 64 } 65 return c 66 } 67 68 func freeEntryAllocCache(obj interface{}) { 69 c := obj.(*entryAllocCache) 70 for i, e := range c.entries { 71 c.dealloc(e) 72 c.entries[i] = nil 73 } 74 } 75 76 func (c *entryAllocCache) alloc() *entry { 77 n := len(c.entries) 78 if n == 0 { 79 if entriesGoAllocated { 80 return &entry{} 81 } 82 b := manual.New(entrySize) 83 return (*entry)(unsafe.Pointer(&b[0])) 84 } 85 e := c.entries[n-1] 86 c.entries = c.entries[:n-1] 87 return e 88 } 89 90 func (c *entryAllocCache) dealloc(e *entry) { 91 if !entriesGoAllocated { 92 buf := (*[manual.MaxArrayLen]byte)(unsafe.Pointer(e))[:entrySize:entrySize] 93 manual.Free(buf) 94 } 95 } 96 97 func (c *entryAllocCache) free(e *entry) { 98 if len(c.entries) == entryAllocCacheLimit { 99 c.dealloc(e) 100 return 101 } 102 c.entries = append(c.entries, e) 103 }