github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/internal/luagc/clonepool.go (about) 1 package luagc 2 3 import ( 4 "sort" 5 "sync" 6 ) 7 8 // 9 // Clone-based Pool implementation 10 // 11 12 // ClonePool is an implementation of Pool that makes every effort to let values 13 // be GCed when they are only reachable via WeakRefs. Unlike UnsafePool, it 14 // doesn't rely on any undocumented properties of the Go runtime so it is safe 15 // to use in any compliant Go implementation. The downside of this 16 // implementation is that it cannot provide any WeakRefs. 17 type ClonePool struct { 18 cloneRegister map[Key]cloneEntry 19 lastMarkOrder int 20 mx sync.Mutex 21 22 pendingFinalize sortablePendingClones 23 pendingRelease sortablePendingClones 24 } 25 26 // NewClonePool returns a new *ClonePool ready to be used. 27 func NewClonePool() *ClonePool { 28 return &ClonePool{ 29 cloneRegister: make(map[Key]cloneEntry), 30 } 31 } 32 33 var _ Pool = (*ClonePool)(nil) 34 35 // Get always returns nil because ClonePool doesn't support weak references. 36 func (p *ClonePool) Get(v Value) WeakRef { 37 return nil 38 } 39 40 // Mark marks v for finalizing, i.e. when v is garbage collected, its finalizer 41 // should be run. 42 func (p *ClonePool) Mark(v Value, flags MarkFlags) { 43 k := v.Key() 44 p.mx.Lock() 45 defer p.mx.Unlock() 46 c, ok := p.cloneRegister[k] 47 if flags == 0 { 48 if ok { 49 setFinalizer(v, nil) 50 delete(p.cloneRegister, k) 51 } 52 return 53 } 54 if !ok { 55 setFinalizer(v, p.goFinalizer) 56 } 57 c.value = v.Clone() 58 p.lastMarkOrder++ 59 c.markOrder = p.lastMarkOrder 60 if flags&Finalize == 0 { 61 c.setFlag(wrFinalized) 62 } else { 63 c.clearFlag(wrFinalized) 64 } 65 if flags&Release == 0 { 66 c.setFlag(wrReleased) 67 } else { 68 c.clearFlag(wrReleased) 69 } 70 p.cloneRegister[k] = c 71 } 72 73 // ExtractPendingFinalize returns the set of values which are being garbage 74 // collected and need their finalizer running, in the order that they should be 75 // run. The caller of this function has the responsibility to run all the 76 // finalizers. The values returned are removed from the pool and their weak refs 77 // are invalidated. 78 func (p *ClonePool) ExtractPendingFinalize() []Value { 79 p.mx.Lock() 80 pending := p.pendingFinalize 81 if pending == nil { 82 p.mx.Unlock() 83 return nil 84 } 85 p.pendingFinalize = nil 86 p.mx.Unlock() 87 88 for _, c := range pending { 89 // The finalizer code might resurrect the value, or there may still be 90 // release code to run, so we need the clone to have a finalizer. 91 setFinalizer(c.value, p.goFinalizer) 92 } 93 94 // Lua wants to run finalizers in reverse order 95 sort.Sort(pending) 96 return pending.vals() 97 } 98 99 func (p *ClonePool) ExtractPendingRelease() []Value { 100 p.mx.Lock() 101 pending := p.pendingRelease 102 if pending == nil { 103 p.mx.Unlock() 104 return nil 105 } 106 p.pendingRelease = nil 107 p.mx.Unlock() 108 109 sort.Sort(pending) 110 return pending.vals() 111 } 112 113 // ExtractAllMarkedFinalized returns all the values that have been marked for 114 // finalizing, even if their go finalizer hasn't run yet. This is useful e.g. 115 // when closing a runtime, to run all pending finalizers. 116 func (p *ClonePool) ExtractAllMarkedFinalize() []Value { 117 p.mx.Lock() 118 119 // Disregard the pendingFinalize list as all values are still present in the 120 // weakrefs map. 121 p.pendingFinalize = nil 122 var marked sortablePendingClones 123 for k, c := range p.cloneRegister { 124 if !c.hasFlag(wrFinalized) { 125 c.setFlag(wrFinalized) 126 p.cloneRegister[k] = c 127 marked = append(marked, c) 128 } 129 } 130 p.mx.Unlock() 131 132 // Sort in reverse order 133 sort.Sort(marked) 134 return marked.vals() 135 } 136 137 // ExtractAllMarkedRelease returns all the values that have been marked for 138 // release, even if their go finalizer hasn't run yet. This is useful e.g. when 139 // closing a runtime, to release all resources. 140 func (p *ClonePool) ExtractAllMarkedRelease() []Value { 141 p.mx.Lock() 142 143 // Start from values whose go finalizer has already run and are awaiting 144 // release, then add all values in the weakrefs map not yet released. 145 marked := p.pendingRelease 146 for _, c := range p.cloneRegister { 147 if !c.hasFlag(wrReleased) { 148 marked = append(marked, c) 149 } 150 } 151 p.pendingRelease = nil 152 p.cloneRegister = nil 153 p.mx.Unlock() 154 155 // Sort in reverse order 156 sort.Sort(marked) 157 return marked.vals() 158 } 159 160 func (p *ClonePool) goFinalizer(v Value) { 161 k := v.Key() 162 p.mx.Lock() 163 defer p.mx.Unlock() 164 c, ok := p.cloneRegister[k] 165 if !ok { 166 // We are too late - ExtractAllMarkedRelease() has been run 167 return 168 } 169 170 if !c.hasFlag(wrFinalized) { 171 p.pendingFinalize = append(p.pendingFinalize, c) 172 c.setFlag(wrFinalized) 173 p.cloneRegister[k] = c 174 return 175 } 176 177 if !c.hasFlag(wrReleased) { 178 p.pendingRelease = append(p.pendingRelease, c) 179 } 180 181 delete(p.cloneRegister, k) 182 } 183 184 type cloneEntry struct { 185 value Value 186 markOrder int 187 flags wrStatusFlags 188 } 189 190 func (c *cloneEntry) hasFlag(flag wrStatusFlags) bool { 191 return c.flags&flag != 0 192 } 193 194 func (c *cloneEntry) setFlag(flag wrStatusFlags) { 195 c.flags |= flag 196 } 197 198 func (c *cloneEntry) clearFlag(flag wrStatusFlags) { 199 c.flags &= ^flag 200 } 201 202 type sortablePendingClones []cloneEntry 203 204 var _ sort.Interface = sortableVals(nil) 205 206 func (vs sortablePendingClones) Len() int { 207 return len(vs) 208 } 209 210 func (vs sortablePendingClones) Less(i, j int) bool { 211 return vs[i].markOrder > vs[j].markOrder 212 } 213 214 func (vs sortablePendingClones) Swap(i, j int) { 215 vs[i], vs[j] = vs[j], vs[i] 216 } 217 218 // Extract the values. 219 func (vs sortablePendingClones) vals() []Value { 220 vals := make([]Value, len(vs)) 221 for i, v := range vs { 222 vals[i] = v.value 223 } 224 return vals 225 }