Consider a library which internally uses.
It’s unknown whether the GMainLoop exists or not. Subscribing to signals works anyway, ofcourse nothing will be delivered and that’s okay, however how can the library avoid leaking memory, g_dbus_connection_signal_unsubscribe uses an idle_source for cleanup, which might never be called.
I see two solutions.
Detect if the application uses a GMainLoop or not.
I don’t know any good way to implement this synchronously, how can it be done?
“iterating the caller’s thread-default GMainContext until the unsubscribe/unwatch/unown operation is complete”
A real GMainLoop might already exist and could be running on another thread, now how could this library start processing the queued idle callbacks in an application with unknown design, yet do it in a safe way?
So your library provides a synchronous API for use by command line applications (not suitable for graphical applications)? If so, just create your own GMainContext and iterate it yourself.
If you provide an asynchronous API for use by graphical applications, then the application has to run its own main context. There’s no way around that. If the application isn’t running its own main context, asynchronous functions will never complete.
Don’t do that! You create a nested main loop situation where application code can be called at times when the application does not expect. Normally applications expect their callbacks to be executed only after they return to their main context, not inside a synchronous library function call. That’s very unexpected. See this main context tutorial for this and other best practices. See especially the section “Using GMainContext in a Library.” The rule to remember is “never iterate a main context you didn’t create yourself.”
If your API is fully synchronous, just use your own main context.
Otherwise, the application cannot have an unknown design. The application will have to create and run its own main context: there’s no good way around that. Normally you’ll use either the main context that was thread-default at the time your library object is created, or at the time your library’s function is called. You have to document which way you choose, so the application knows what to expect.
Yes, the library offers both a simple “synchronous API for use by command line applications”, and an advanced asynchronous API which requires the application to create it’s own main context. From the library there is no way to determine which case it is, it was meant to be fully transparent. glib actually knows which of the two cases it is, but there is no API to access that internal glib state during initialization of the library.
I guess I have to give up create a new major version, break existing applications by requiring all async applications to explicitly opt-in.
It sounds like your design is actually already fine? Your sync API does not require the application to run a GMainContext, and your async API does. That’s exactly how it ought to work. Sounds good. So what’s the problem?
The recommended best practice is to implement everything asynchronously internally, then let the sync APIs be a wrapper around the async APIs that create a new main context, push thread default, iterate until the operation completes, and finally pop thread default.