Line data Source code
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 : package cache 6 : 7 : import ( 8 : "fmt" 9 : "os" 10 : "unsafe" 11 : 12 : "github.com/cockroachdb/pebble/internal/buildtags" 13 : "github.com/cockroachdb/pebble/internal/invariants" 14 : "github.com/cockroachdb/pebble/internal/manual" 15 : ) 16 : 17 : // ValueMetadataSize denotes the number of bytes of metadata allocated for a 18 : // cache entry. Note that builds with cgo disabled allocate no metadata, and 19 : // 32-bit builds allocate less for a cache.Value. However, we keep the value 20 : // constant to reduce friction for writing tests. 21 : const ValueMetadataSize = 32 22 : 23 : // Assert that the size of a Value{} is less than or equal to the 24 : // ValueMetadataSize. 25 : var _ uint = ValueMetadataSize - uint(unsafe.Sizeof(Value{})) 26 : 27 : // Value holds a reference counted immutable value. 28 : type Value struct { 29 : // buf is allocated using the manual package. 30 : buf []byte 31 : // Reference count for the value. The value is freed when the reference count 32 : // drops to zero. 33 : ref refcnt 34 : } 35 : 36 : // The Value struct is normally allocated together with the buffer using manual 37 : // memory. 38 : // 39 : // If cgo is not available, the Value must be a normal Go object to keep 40 : // the buffer reference visible to the GC. We also use the Go allocator if we 41 : // want to add finalizer assertions. 42 : const valueEntryGoAllocated = !buildtags.Cgo || invariants.UseFinalizers 43 : 44 1 : func newValue(n int) *Value { 45 1 : if n == 0 { 46 0 : return nil 47 0 : } 48 : 49 1 : if valueEntryGoAllocated { 50 1 : // Note: if cgo is not enabled, manual.New will do a regular Go allocation. 51 1 : b := manual.New(manual.BlockCacheData, n) 52 1 : v := &Value{buf: b} 53 1 : v.ref.init(1) 54 1 : // Note: this is a no-op if invariants and tracing are disabled or race is 55 1 : // enabled. 56 1 : invariants.SetFinalizer(v, func(obj interface{}) { 57 1 : v := obj.(*Value) 58 1 : if v.buf != nil { 59 0 : fmt.Fprintf(os.Stderr, "%p: cache value was not freed: refs=%d\n%s", 60 0 : v, v.refs(), v.ref.traces()) 61 0 : os.Exit(1) 62 0 : } 63 : }) 64 1 : return v 65 : } 66 : // When we're not performing leak detection, the lifetime of the returned 67 : // Value is exactly the lifetime of the backing buffer and we can manually 68 : // allocate both. 69 0 : b := manual.New(manual.BlockCacheData, ValueMetadataSize+n) 70 0 : v := (*Value)(unsafe.Pointer(&b[0])) 71 0 : v.buf = b[ValueMetadataSize:] 72 0 : v.ref.init(1) 73 0 : return v 74 : } 75 : 76 1 : func (v *Value) free() { 77 1 : if invariants.Enabled { 78 1 : // Poison the contents to help catch use-after-free bugs. 79 1 : for i := range v.buf { 80 1 : v.buf[i] = 0xff 81 1 : } 82 : } 83 1 : if valueEntryGoAllocated { 84 1 : manual.Free(manual.BlockCacheData, v.buf) 85 1 : v.buf = nil 86 1 : return 87 1 : } 88 0 : n := ValueMetadataSize + cap(v.buf) 89 0 : buf := unsafe.Slice((*byte)(unsafe.Pointer(v)), n) 90 0 : v.buf = nil 91 0 : manual.Free(manual.BlockCacheData, buf) 92 : } 93 : 94 : // Buf returns the buffer associated with the value. The contents of the buffer 95 : // should not be changed once the value has been added to the cache. Instead, a 96 : // new Value should be created and added to the cache to replace the existing 97 : // value. 98 1 : func (v *Value) Buf() []byte { 99 1 : if v == nil { 100 0 : return nil 101 0 : } 102 1 : return v.buf 103 : } 104 : 105 : // Truncate the buffer to the specified length. The buffer length should not be 106 : // changed once the value has been added to the cache as there may be 107 : // concurrent readers of the Value. Instead, a new Value should be created and 108 : // added to the cache to replace the existing value. 109 1 : func (v *Value) Truncate(n int) { 110 1 : v.buf = v.buf[:n] 111 1 : } 112 : 113 1 : func (v *Value) refs() int32 { 114 1 : return v.ref.refs() 115 1 : } 116 : 117 1 : func (v *Value) acquire() { 118 1 : v.ref.acquire() 119 1 : } 120 : 121 1 : func (v *Value) release() { 122 1 : if v != nil && v.ref.release() { 123 1 : v.free() 124 1 : } 125 : }