github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/internal/luagc/unsafepool.go (about) 1 package luagc 2 3 import ( 4 "runtime" 5 "sort" 6 "sync" 7 "unsafe" 8 ) 9 10 // 11 // Unsafe Pool implementation 12 // 13 14 // So that runtime.SetFinalizer can be mocked for testing. 15 var setFinalizer = runtime.SetFinalizer 16 17 // UnsafePool is an implementation of Pool that makes every effort to let 18 // values be GCed when they are only reachable via WeakRefs. It relies on 19 // casting interface{} to unsafe pointers and back again, which would break if 20 // Go were to have a moving GC. 21 type UnsafePool struct { 22 mx sync.Mutex // Used to synchronize access to weakrefs, pendingVals, pendingOrders. 23 weakrefs map[uintptr]*weakRef // 24 pendingFinalize sortableVals // Values pending Lua finalization 25 pendingRelease sortableVals 26 lastMarkOrder int // this is to sort values by reverse order of mark for finalize 27 } 28 29 var _ Pool = &UnsafePool{} 30 31 // NewUnsafePool returns a new *UnsafeWeakRefPool ready to be used. 32 func NewUnsafePool() *UnsafePool { 33 return &UnsafePool{weakrefs: make(map[uintptr]*weakRef)} 34 } 35 36 // Get returns a WeakRef for v if possible. 37 func (p *UnsafePool) Get(v Value) WeakRef { 38 p.mx.Lock() 39 defer p.mx.Unlock() 40 return p.get(v) 41 } 42 43 // Returns a *WeakRef for iface, not thread safe, only call when you have the 44 // pool lock. 45 func (p *UnsafePool) get(v Value) *weakRef { 46 w := getwiface(v) 47 id := w.id() 48 r := p.weakrefs[id] 49 if r == nil { 50 setFinalizer(v, p.goFinalizer) 51 r = &weakRef{ 52 w: w, 53 pool: p, 54 } 55 p.weakrefs[id] = r 56 } 57 return r 58 } 59 60 // Mark marks v for finalizing, i.e. when v is garbage collected, its finalizer 61 // should be run. It only takes effect if v can have a weak ref. 62 func (p *UnsafePool) Mark(v Value, flags MarkFlags) { 63 if flags == 0 { 64 return 65 } 66 p.mx.Lock() 67 defer p.mx.Unlock() 68 p.lastMarkOrder++ 69 r := p.get(v) 70 r.markOrder = p.lastMarkOrder 71 if flags&Finalize == 0 { 72 r.setFlag(wrFinalized) 73 } else { 74 r.clearFlag(wrFinalized) 75 } 76 if flags&Release == 0 { 77 r.setFlag(wrReleased) 78 } else { 79 r.clearFlag(wrReleased) 80 } 81 } 82 83 // ExtractPendingFinalize returns the set of values which are being garbage 84 // collected and need their finalizer running, in the order that they should be 85 // run. The caller of this function has the responsibility to run all the 86 // finalizers. The values returned are removed from the pool and their weak refs 87 // are invalidated. 88 func (p *UnsafePool) ExtractPendingFinalize() []Value { 89 90 // It may be that since a value pending finalizing has been added to the 91 // list, it was resurrected by a weak ref, so we need to go through the list 92 // and filter these out first. 93 p.mx.Lock() 94 if p.pendingFinalize == nil { 95 p.mx.Unlock() 96 return nil 97 } 98 var pending sortableVals 99 for _, rval := range p.pendingFinalize { 100 if rval.r.hasFlag(wrResurrected) { 101 rval.r.clearFlag(wrResurrected) 102 } else { 103 rval.r.setFlag(wrFinalized) 104 pending = append(pending, rval) 105 } 106 } 107 p.pendingFinalize = nil 108 p.mx.Unlock() 109 110 // Lua wants to run finalizers in reverse order 111 sort.Sort(pending) 112 return pending.vals() 113 } 114 115 func (p *UnsafePool) ExtractPendingRelease() []Value { 116 p.mx.Lock() 117 pending := p.pendingRelease 118 if pending == nil { 119 p.mx.Unlock() 120 return nil 121 } 122 p.pendingRelease = nil 123 124 for _, rval := range pending { 125 rval.r.setFlag(wrReleased) 126 } 127 p.mx.Unlock() 128 129 sort.Sort(pending) 130 return pending.vals() 131 } 132 133 // ExtractAllMarkedFinalized returns all the values that have been marked for 134 // finalizing, even if their go finalizer hasn't run yet. This is useful e.g. 135 // when closing a runtime, to run all pending finalizers. 136 func (p *UnsafePool) ExtractAllMarkedFinalize() []Value { 137 p.mx.Lock() 138 139 // Disregard the pendingFinalize list as all values are still present in the 140 // weakrefs map. 141 p.pendingFinalize = nil 142 var marked sortableVals 143 for _, r := range p.weakrefs { 144 if !r.hasFlag(wrFinalized) { 145 iface := r.w.iface() 146 marked = append(marked, refVal{ 147 v: iface, 148 r: r, 149 }) 150 r.setFlag(wrFinalized) 151 // We don't want the finalizer to be triggered anymore, but more 152 // important the finalizer is holding a reference to the pool 153 // (although that may not affect its reachability?) 154 setFinalizer(iface, nil) 155 } 156 } 157 p.mx.Unlock() 158 159 // Sort in reverse order 160 sort.Sort(marked) 161 return marked.vals() 162 } 163 164 // ExtractAllMarkedRelease returns all the values that have been marked for 165 // release, even if their go finalizer hasn't run yet. This is useful e.g. when 166 // closing a runtime, to release all resources. 167 func (p *UnsafePool) ExtractAllMarkedRelease() []Value { 168 p.mx.Lock() 169 170 // Start from values whose go finalizer has already run and are awaiting 171 // release, then add all values in the weakrefs map not yet released. 172 marked := p.pendingRelease 173 for _, r := range p.weakrefs { 174 if !r.hasFlag(wrReleased) { 175 iface := r.w.iface() 176 marked = append(marked, refVal{ 177 v: iface, 178 r: r, 179 }) 180 r.flags |= wrReleased 181 // We don't want the finalizer to be triggered anymore, but more 182 // important the finalizer is holding a reference to the pool 183 // (although that may not affect its reachability?) 184 setFinalizer(iface, nil) 185 } 186 } 187 p.pendingRelease = nil 188 p.weakrefs = nil 189 p.mx.Unlock() 190 191 // Sort in reverse order 192 sort.Sort(marked) 193 return marked.vals() 194 } 195 196 // This is the finalizer that Go runs on values added to the pool when they 197 // become unreachable. 198 func (p *UnsafePool) goFinalizer(v Value) { 199 p.mx.Lock() 200 defer p.mx.Unlock() 201 id := getwiface(v).id() 202 r := p.weakrefs[id] 203 if r == nil { 204 return 205 } 206 207 // A resurrected value has its go finalizer reinstated. 208 if r.hasFlag(wrResurrected) { 209 r.clearFlag(wrResurrected) 210 setFinalizer(v, p.goFinalizer) 211 return 212 } 213 214 rval := refVal{ 215 v: v, 216 r: r, 217 } 218 219 // A not yet finalized value is added to the pendingFinalize list. As it 220 // may get resurrected in the finalizer, we reinstate its go finalizer. 221 // When it is extracted to be processed, its finalized flag will be set. 222 if !r.hasFlag(wrFinalized) { 223 p.pendingFinalize = append(p.pendingFinalize, rval) 224 setFinalizer(v, p.goFinalizer) 225 return 226 } 227 228 // This is a point of no return, this value is now dead to the Lua runtime. 229 r.setFlag(wrDead) 230 231 // A not yet released value is added to the pendingRelease list. 232 if !r.hasFlag(wrReleased) { 233 p.pendingRelease = append(p.pendingRelease, rval) 234 } 235 236 // It is now safe to remove this value from the weakref pool. 237 delete(p.weakrefs, id) 238 } 239 240 // 241 // WeakRef implementation for UnsafePool 242 // 243 244 type weakRef struct { 245 w wiface // encodes the value the weak ref refers to 246 markOrder int // positive if the value was marked with UnsafePool.Mark() 247 flags wrStatusFlags 248 249 // Needed to sync with the Go finalizers which run in their own goroutine. 250 pool *UnsafePool 251 } 252 253 var _ WeakRef = &weakRef{} 254 255 // Value returns the value this weak ref refers to if it is still alive, else 256 // returns NilValue. 257 func (r *weakRef) Value() Value { 258 r.pool.mx.Lock() 259 defer r.pool.mx.Unlock() 260 if r.hasFlag(wrDead) { 261 return nil 262 } 263 r.setFlag(wrResurrected) 264 return r.w.iface() 265 } 266 267 func (r *weakRef) hasFlag(flag wrStatusFlags) bool { 268 return r.flags&flag != 0 269 } 270 271 func (r *weakRef) setFlag(flag wrStatusFlags) { 272 r.flags |= flag 273 } 274 275 func (r *weakRef) clearFlag(flag wrStatusFlags) { 276 r.flags &= ^flag 277 } 278 279 // 280 // Statuses of a weak ref 281 // 282 283 type wrStatusFlags uint8 284 285 // A weakRef has 4 status flags: "dead" , "resurrected", "finalized", 286 // "released". 287 const ( 288 wrDead wrStatusFlags = 1 << iota // The value is dead to the Lua runtime 289 wrResurrected // The weakRef's value has been obtained 290 wrFinalized // The Lua finalizer no longer needs to run 291 wrReleased // The value's resources no longer need to be released (in this case it should be dead) 292 ) 293 294 // 295 // Non-retaining reference to an interface value 296 // 297 298 // wiface is an unsafe copy of an interface. It remembers the type and data of 299 // a Go interface value, but does not keep it alive. 300 type wiface [2]uintptr 301 302 func getwiface(r Value) wiface { 303 return *(*[2]uintptr)(unsafe.Pointer(&r)) 304 } 305 306 func (w wiface) id() uintptr { 307 // This is the address containing the interface data. 308 return w[1] 309 } 310 311 func (w wiface) iface() Value { 312 return *(*Value)(unsafe.Pointer(&w)) 313 } 314 315 // 316 // Values need to be sorted by reverse mark order. The data structures below help with that. 317 // 318 319 type refVal struct { 320 v Value 321 r *weakRef 322 } 323 324 type sortableVals []refVal 325 326 var _ sort.Interface = sortableVals(nil) 327 328 func (vs sortableVals) Len() int { 329 return len(vs) 330 } 331 332 func (vs sortableVals) Less(i, j int) bool { 333 return vs[i].r.markOrder > vs[j].r.markOrder 334 } 335 336 func (vs sortableVals) Swap(i, j int) { 337 vs[i], vs[j] = vs[j], vs[i] 338 } 339 340 // Extract the values. 341 func (vs sortableVals) vals() []Value { 342 vals := make([]Value, len(vs)) 343 for i, v := range vs { 344 vals[i] = v.v 345 } 346 return vals 347 }