Language bindings and the async callbacks

I wonder which of all the GTK language bindings supports the GTK async callbacks really well?

We recently got an issue by someone who wants to use the GTK4 GdkClipport using gdk_clipboard_read_value_async(), which is not yet supported by the gintro Nim bindings.

I think I had a discussion about GTK async callbacks already some years ago, and the conclusion was that at least for glib we can often just ignore that callbacks, as the host language supports most of glib functionality.

Of course it is possible to support all the GTK related async callbacks by use of Nim macros, but doing that fully automatically is not that easy. And creating for each use case a macro manually and testing it is some work.

My motivation is not that high, as all the async stuff makes things more complicated, without a actual benefit. For example gedit and gtksourceview reads files async. But the actual reading process takes about one millisecond or less on modern hardware with 10 GB/s memory bandwidth. I think people using still floppy disk or a 300 baud modem are a minority now.

But well, when most other language bindings supports this, then we should provide it for Nim and its few GTK users too :slight_smile:

All other language bindings support this and it is always important. E.g. if you do a D-Bus call that doesn’t immediately return, do you want your application to hang for 30 seconds waiting for the response? What if you are reading a file mounted on NFS and the network is slow, or down? What if you are reading 2 GB of clipboard data?

There is no way a programming language can emulate async function calls. If your language is able to wrap a synchronous call in an async-style callback, it might look async to the programmer, but the underlying code is still synchronous and your application will block. The only way for the language to avoid this would be to perform the synchronous operation on a separate thread, which is almost never allowed (this only works if the API is threadsafe, and few are). You can wrap an async operation inside a sync operation, but you cannot wrap a sync operation inside an async operation if you want it to really be async.


Not that it helps in this specific case, but languages like Go and GHC/Haskell kind of do that: each “synchronous” operation in the code you write is under the hood a suspension point for your current “coroutine”, and on “blocking” the language runtime can decide to do other things on the OS thread. Other languages/language runtimes with N:M green threads probably have similar mechanisms.

I don’t know if Nim also does such things (but isn’t it about there being no implicit control flow?), but if it does then this brings special problems for writing language bindings and you’d ideally would like to avoid exposing any synchronous, blocking operations that can’t be integrated into the IO system of the language runtime.

That motivation seems a bit misguided. Especially in (for example) UI applications you really don’t want to do any blocking operations on your main thread (it’s always fun if your UI freezes for a second or so because for some reason your DNS takes a while to resolve a hostname…), and async operations together with corresponding integration into the target language provide a convenient way out of this dilemma. It’s more complex than sync/blocking code but that’s probably a good price to pay for providing a snappy UI to your users.

Of course you can also work with helper threads, but that introduces complexity as well (and often more complexity than using async operations in a language with something like async/await constructs) and has different tradeoffs.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.