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