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 part of the slice 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, uintptr(n)) 52 1 : v := &Value{buf: b.Slice()} 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+uintptr(n)) 70 0 : v := (*Value)(b.Data()) 71 0 : v.buf = b.Slice()[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 : buf := manual.MakeBufUnsafe(unsafe.Pointer(unsafe.SliceData(v.buf)), uintptr(cap(v.buf))) 85 1 : manual.Free(manual.BlockCacheData, buf) 86 1 : v.buf = nil 87 1 : return 88 1 : } 89 0 : n := ValueMetadataSize + uintptr(cap(v.buf)) 90 0 : buf := manual.MakeBufUnsafe(unsafe.Pointer(v), n) 91 0 : v.buf = nil 92 0 : manual.Free(manual.BlockCacheData, buf) 93 : } 94 : 95 : // RawBuffer returns the buffer associated with the value. The contents of the buffer 96 : // should not be changed once the value has been added to the cache. Instead, a 97 : // new Value should be created and added to the cache to replace the existing 98 : // value. 99 1 : func (v *Value) RawBuffer() []byte { 100 1 : if v == nil { 101 0 : return nil 102 0 : } 103 1 : return v.buf 104 : } 105 : 106 : // Truncate the buffer to the specified length. The buffer length should not be 107 : // changed once the value has been added to the cache as there may be 108 : // concurrent readers of the Value. Instead, a new Value should be created and 109 : // added to the cache to replace the existing value. 110 1 : func (v *Value) Truncate(n int) { 111 1 : v.buf = v.buf[:n] 112 1 : } 113 : 114 1 : func (v *Value) refs() int32 { 115 1 : return v.ref.refs() 116 1 : } 117 : 118 1 : func (v *Value) acquire() { 119 1 : v.ref.acquire() 120 1 : } 121 : 122 : // Release a ref count on the buffer. It is a no-op to call Release on a nil 123 : // Value. 124 1 : func (v *Value) Release() { 125 1 : if v != nil && v.ref.release() { 126 1 : v.free() 127 1 : } 128 : }