github.com/matrixorigin/matrixone@v1.2.0/pkg/common/reuse/checker.go (about) 1 // Copyright 2023 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package reuse 16 17 import ( 18 "fmt" 19 "runtime" 20 "runtime/debug" 21 "sync" 22 "unsafe" 23 ) 24 25 var ( 26 idle = step(0) 27 inUse = step(1) 28 ) 29 30 type step int 31 32 type checker[T ReusableObject] struct { 33 enable bool 34 mu struct { 35 sync.RWMutex 36 // we use uintptr as key, to check leak free in gc triggered. 37 // We cannot hold the *T in checker. 38 m map[uintptr]step 39 createStack map[uintptr]string 40 lastFreeStack map[uintptr]string 41 } 42 } 43 44 func newChecker[T ReusableObject](enable bool) *checker[T] { 45 c := &checker[T]{ 46 enable: enable, 47 } 48 c.mu.m = make(map[uintptr]step) 49 c.mu.createStack = make(map[uintptr]string) 50 c.mu.lastFreeStack = make(map[uintptr]string) 51 return c 52 } 53 54 func (c *checker[T]) created(v *T) { 55 if !enableChecker.Load() || !c.enable { 56 return 57 } 58 59 c.mu.Lock() 60 defer c.mu.Unlock() 61 k := uintptr(unsafe.Pointer(v)) 62 c.mu.m[k] = idle 63 } 64 65 func (c *checker[T]) got(v *T) { 66 if !enableChecker.Load() || !c.enable { 67 return 68 } 69 70 c.mu.Lock() 71 defer c.mu.Unlock() 72 73 k := uintptr(unsafe.Pointer(v)) 74 s, ok := c.mu.m[k] 75 if !ok { 76 panic("missing status") 77 } 78 79 switch s { 80 case inUse: 81 panic(fmt.Sprintf("double got from pool for type: %T, %+v \n create by: <<<%s>>>\n", 82 v, v, c.mu.createStack[k])) 83 } 84 c.mu.m[k] = inUse 85 if enableVerbose.Load() { 86 c.mu.createStack[k] = string(debug.Stack()) 87 } 88 } 89 90 func (c *checker[T]) free(v *T) { 91 if !enableChecker.Load() || !c.enable { 92 return 93 } 94 95 c.mu.Lock() 96 defer c.mu.Unlock() 97 98 k := uintptr(unsafe.Pointer(v)) 99 s, ok := c.mu.m[k] 100 if !ok { 101 return 102 } 103 104 switch s { 105 // the v is marked idle, means already free 106 case idle: 107 panic(fmt.Sprintf("double free for type: %T, %+v \n create by: <<<%s>>>\n last free by: <<<%s>>> \n", 108 v, v, c.mu.createStack[k], c.mu.lastFreeStack[k])) 109 } 110 c.mu.m[k] = idle 111 if enableVerbose.Load() { 112 c.mu.lastFreeStack[k] = string(debug.Stack()) 113 } 114 } 115 116 func (c *checker[T]) gc(v *T) { 117 if !enableChecker.Load() || !c.enable { 118 return 119 } 120 121 c.mu.Lock() 122 defer c.mu.Unlock() 123 124 k := uintptr(unsafe.Pointer(v)) 125 s, ok := c.mu.m[k] 126 if !ok { 127 return 128 } 129 130 switch s { 131 // the v is marked in use, but v is release by gc 132 case inUse: 133 panic(fmt.Sprintf("missing free for type: %T, %+v \n create by: <<<%s>>>\n", 134 v, v, c.mu.createStack[k])) 135 } 136 137 delete(c.mu.m, k) 138 } 139 140 func RunReuseTests(fn func()) { 141 enableChecker.Store(true) 142 defer func() { 143 enableChecker.Store(false) 144 }() 145 fn() 146 c := make(chan struct{}) 147 func() { 148 v := &waiterGC{ 149 data: make([]byte, 1024), 150 } 151 runtime.SetFinalizer( 152 v, 153 func(v *waiterGC) { 154 close(c) 155 }) 156 }() 157 debug.FreeOSMemory() 158 <-c 159 } 160 161 type waiterGC struct { 162 data []byte 163 }