@@ -26,7 +26,7 @@ import (
26
26
27
27
const maxTypesAllowed = 3
28
28
29
- var knownTypes uint32
29
+ var knownTypes uint32 = 0
30
30
31
31
type BoxedType [T any ] struct {
32
32
ctor func (* Box ) * T
@@ -81,7 +81,8 @@ func (t *BoxedType[T]) Delete(box *Box) {
81
81
82
82
// Box is an opaque type holding extra data.
83
83
type Box struct {
84
- data [maxTypesAllowed ]unsafe.Pointer
84
+ dummy * unsafe.Pointer
85
+ data [maxTypesAllowed ]unsafe.Pointer
85
86
}
86
87
87
88
// Object returns Box's C GObject pointer.
@@ -114,6 +115,11 @@ func newBox(obj unsafe.Pointer) *Box {
114
115
box := & Box {}
115
116
box .data [0 ] = obj
116
117
118
+ // Cheat Go's GC by adding a finalizer to a dummy pointer that is inside Box
119
+ // but is not Box itself.
120
+ box .dummy = & obj
121
+ runtime .SetFinalizer (box .dummy , finalizeBox )
122
+
117
123
if objectProfile != nil {
118
124
objectProfile .Add (obj , 3 )
119
125
}
@@ -189,7 +195,6 @@ func Get(gobject unsafe.Pointer, take bool) *Box {
189
195
// reverse link is always strong.
190
196
//
191
197
shared .strong [gobject ] = box
192
- runtime .SetFinalizer (box , finalizeBox )
193
198
194
199
shared .mu .Unlock ()
195
200
@@ -259,38 +264,60 @@ func Free(box *Box) {
259
264
}
260
265
}
261
266
267
+ var finalizing atomic.Bool
268
+
262
269
// finalizeBox only delays its finalization until GLib notifies us a toggle. It
263
270
// does so for as long as an object is stored only in the Go heap. Once the
264
271
// object is also shared, the toggle notifier will strongly reference the Box.
265
- func finalizeBox (box * Box ) {
266
- obj := box .GObject ()
267
- if obj == nil {
268
- return
272
+ func finalizeBox (dummy * unsafe.Pointer ) {
273
+ // obj := box.GObject()
274
+ // if obj == nil {
275
+ // return
276
+ // }
277
+
278
+ // var objInfoRes string
279
+ // if toggleRefs != nil {
280
+ // objInfoRes = objInfo(obj)
281
+ // toggleRefs.Println(objInfoRes, "finalizeBox: acquiring lock...")
282
+ // }
283
+
284
+ shared .mu .Lock ()
285
+
286
+ if ! finalizing .CompareAndSwap (false , true ) {
287
+ panic ("impossible: finalizeBox called while finalizing" )
288
+ }
289
+ defer finalizing .Store (false )
290
+
291
+ box , _ := gets (* dummy )
292
+ if box == nil {
293
+ panic ("bug: finalizeBox called on already freed object" )
269
294
}
270
295
296
+ obj := box .GObject ()
297
+
271
298
var objInfoRes string
272
299
if toggleRefs != nil {
273
300
objInfoRes = objInfo (obj )
274
301
toggleRefs .Println (objInfoRes , "finalizeBox: acquiring lock..." )
275
302
}
276
303
277
- shared .mu .Lock ()
278
-
279
304
if ! freeBox (box ) {
280
305
// Delegate finalizing to the next cycle.
281
- runtime .SetFinalizer (box , finalizeBox )
306
+ runtime .SetFinalizer (box . dummy , finalizeBox )
282
307
shared .mu .Unlock ()
308
+
283
309
if toggleRefs != nil {
284
310
toggleRefs .Println (objInfoRes , "finalizeBox: moving finalize to next GC cycle" )
285
311
}
286
312
} else {
287
- shared .mu .Unlock ()
288
313
// Unreference the object. This will potentially free the object as
289
314
// well. The closures are definitely gone at this point.
290
315
C .g_object_remove_toggle_ref (
291
316
(* C .GObject )(unsafe .Pointer (obj )),
292
317
(* [0 ]byte )(C .goToggleNotify ), nil ,
293
318
)
319
+ shared .mu .Unlock ()
320
+
294
321
if toggleRefs != nil {
295
322
toggleRefs .Println (objInfoRes , "finalizeBox: removed toggle ref during GC" )
296
323
}
@@ -346,15 +373,18 @@ func goToggleNotify(_ C.gpointer, obj *C.GObject, isLastInt C.gboolean) {
346
373
gobject := unsafe .Pointer (obj )
347
374
isLast := isLastInt != C .FALSE
348
375
349
- shared .mu .Lock ()
376
+ if ! finalizing .Load () {
377
+ shared .mu .Lock ()
378
+ defer shared .mu .Unlock ()
379
+ }
380
+
350
381
if isLast {
351
382
// delete(shared.sharing, gobject)
352
383
makeWeak (gobject )
353
384
} else {
354
385
// shared.sharing[gobject] = struct{}{}
355
386
makeStrong (gobject )
356
387
}
357
- shared .mu .Unlock ()
358
388
359
389
if toggleRefs != nil {
360
390
toggleRefs .Println (objInfo (unsafe .Pointer (obj )), "goToggleNotify: is last =" , isLast )
@@ -388,6 +418,9 @@ func makeStrong(gobject unsafe.Pointer) {
388
418
// TODO: double mutex check, similar to ShouldFree.
389
419
390
420
box , strong := gets (gobject )
421
+ if toggleRefs != nil {
422
+ toggleRefs .Println (objInfo (gobject ), "makeStrong: obtained box" , box , "strong =" , strong )
423
+ }
391
424
if box == nil {
392
425
return
393
426
}
@@ -402,6 +435,9 @@ func makeStrong(gobject unsafe.Pointer) {
402
435
// weakly referenced.
403
436
func makeWeak (gobject unsafe.Pointer ) {
404
437
box , strong := gets (gobject )
438
+ if toggleRefs != nil {
439
+ toggleRefs .Println (objInfo (gobject ), "makeWeak: obtained box" , box , "strong =" , strong )
440
+ }
405
441
if box == nil {
406
442
return
407
443
}
0 commit comments