Created attachment 33799 [details]
desrt's pseudo-code example of the g_main_context_push_thread_default solution.
The Gibber library is obsolete now that GLib has Gio. Mainly, the Gibber code is used for handling Gabble bytestreams. In addition to porting to Gio, it would be good to move the new code into Wocky.
It seems a good solution for this would be to ultimately change GabbleBytestreamIface implementations to be derived from a GabbleIOBytestream class, which in turn would be derived from GIOStream, with custom GInputStream's and GOutputStream's for the implementations that would need them (namely the MUC and IBB bytestreams). The GabbleBytestreamIface can eventually be dissolved into this new derived class. One of the big benefits of using GIOStream's is that users of the objects can splice external input and output streams directly to the bytestream's streams via g_output_stream_splice[_async]. The _async method even allows them to be spliced and virtually forgotten about.
Currently there are 4 bytestream implementations: SOCKS5, MUC, IBB, and multiple. SOCKS5 would be able to pass a GSocketStream's streams directly. MUC and IBB would need to have custom input and output streams implemented. Multiple is just a wrapper around the rest so it just needs to return the streams of the active bytestream.
For tube-streams and ftchannels, the sockets and filestreams can be spliced directly into the bytestream's streams. dbus-tubes can use the interfaces to write data similar to how it currently does.
For MUC and IBB bytestreams it gets a little tricky. The async methods would be fine, but blocking ones are troublesome, mostly because they block the main thread while async operations on the main thread are needing to happen. I talked to desrt on IRC and he suggested to use g_main_context_push_thread_default to block, but still allow the async methods to complete (See attached). This would work great for writing to input streams, but reading from output streams is still a problem.
Wocky calls registered handlers when data is received. If this handler buffered the data, when it received it, until read() was called, that would be fine. The exception being that if it's the blocking version and the buffer is/becomes empty, then you're stuck without any data left in a blocking function and the handler function will never call. Additionally, since it's a blocking function, if there were any stanzas received that weren't for the bytestream, the handlers shouldn't be called, but the stanzas shouldn't be dropped either.
The default implementation of the splice and splice_async functions use the blocking read() implementation of the input stream. For the async form, it runs it in a separate thread, which can be made to work fine with a little use of mutexes, but with the blocking form, this has the same empty buffer problem.