Hmm.

Well, I suppose the way that clients are using it at the moment is through the GObject lifetime semantics, as it is the easiest way to get reference/unreference semantics around a particular behaviour.

There's three requirements here:
 - It needs to have shared ownership semantics, or perhaps unique ownership semantics.
 - Clients who are passed this object need a way to get rid of it (sensibly).
 - An arbitrary callback needs to be executed just before the object is freed. Ideally, clients should not know (or need to care) about what that callback is.

Doing it through a normal object seems to fufill all three of these correctly. The only case I can think of it not working the way you'd expect is in a managed language, because there's no deterministic destruction there.

`GClosure` could kind of work here, but it is more awkward. We have a `GClosureNotify` for when the closure goes away, but then you end up exposing a bunch of API surface (`g_closure_invoke`) that isn't really relevant. The client calling the debugging function doesn't want to call their own callback, they just want to get rid of their callback registration when it makes sense to.

Another alternative is to wrap the entire callback inside of `GClosure` for usage in both the interrupt register and on the client side. But then you end up having to invoke it by specifying everything as a `GValue` and through a list of `GParamSpec`. Not exactly desired if we can avoid it.

I can't think of any other system available in GLib that would allow us to satisfy those three requirements better than a custom object or a boxed type. Clients are still using the object through its interface, although they're using the `GObject` API and nothing else. Just the object lifecycle API and the `GjsDebugConnection` constructor.

(Part of my thinking here is coming from C++ where these RAII-type handles in either `shared_ptr` or `unique_ptr` containers are the way that you usually solve these problems)