Draft: Introduce an IPC framework to run modules in external processes.
This MR stems from the recent, older, and very old discussions about LUA scripts crashing VLC, the need to load the huge whisper models out of the main application and the discussion about getting the preparser in another process and its serialization.
I was convinced that there was a discussion about how serialization should probably be handled by code generation but I cannot find it anymore so I'm probably misremembering it.
Json, while a convenient serialization language, comes with overhead and isn't really suited to pass binary buffers (short of using base64 and I hate base64) and those will be needed to send audio to whisper, or true 64 bits integers since double loses integer precision at 2^53 (yes that 285 years in vlc ticks but I'm a well known pedantic guy).
In any case, as the consensus in the pointed out discussions, was that it was a lot of complex work that would be too much for GSOC I decided to try my hand at it.
The idea behind this MR is to introduce an IPC framework "easy" to re-use for different features. It relies on the fact that we can use vlc_spawn
with up to 3 file descriptors (for portability reasons) and build up from that.
It is then composed of :
- A set of IPC functions for writing and reading well known types on pipes. It is kept trivial for now as this module is left to discussion.
- A code generator based on simplified protobuf-like syntax to describe serialized entities that will generate headers with the corresponding C structs and functions to automatically send, receive and clean those structures.
- a set of client/server APIs to send and receive commands over the 3 pipes. The client uses 2 for bidirectional calls toward the main application (basically for remotely calling the libvlccore APIs), the server can send events/commands to the client using the third one. As there is a mono directional limitation they are by definition asynchronous and will need acknowledgement through the bidirectional channel if needed through custom commands.
- a runner spawned with
vlc_spawn
loading a module with an "ipc runner intf" capability and a module name passed as parameter (since it is more or less expected to implement both the vlc module and ipc runner intf in the same plugin. - an example in no way complete and probably leaky showcasing this runner loading a LUA extension (with no features).
Now I agree that this is somewhat lacking in some parts and probably cargo cultish/overengineered in others.
IMO, the biggest point of discussions right now are:
- The IPC layer is mostly a NIH version of protobuf. But protobuf is a huge C++ dependency that has failed me more often than not, and protobuf-c an unofficial port, not to mention that I'm not really comfortable about adding contrib dependencies to build and link a core-ish lib. So as a starting point, I chose a yacc/lex based parser for simplicity and flexibility but I cannot say that I'm truly happy about it and could get back to protobuf-c (or any better serialization lib) if people think it's better.
- It entirely assumes that it's run on the same machine on reliable connected pipes (and rely on a lib signature to make sure that the endianness and word size match).
- Error handling is kept at its simplest. Any error in the pipe communication will cause an
abort()
of the runner. - All the transfers are blocking since they are meant to be an "atomic" function call on a pipe.
- The libvlc_ipc is currently a big static one with poorly named headers as I don't really know where to put it. I considered making it a part of libvlccore but I don't know if we want to add that many symbols.
- I learned yacc/lex on the go so the code generator itself is ugly with globals (but since it's a host tool I didn't go the extra step).
- The various registered handlers are probably overkill (as its goal was to let the server register only the features that it wants quite similar to the feature registrations in lua).
- The runner ops split into reader and handler are ugly but it was the easiest way to make sure that server events cannot be blocking (to simplify runner kills if it's stuck) while keeping the executor thread in the lib and not on the user side.
- The runner uses the libvlc instance as its base and could probably use an interface but I don't have the use for it right now.
- stdin/stdout/stderr are unusable (and used in the other direction than the usual one to avoid printf 3rd party components derailing IPC communication).
- The doc is lacking to say the least.
- The last commit isn't meant as part of this MR but as a showcase of its possible use.
And probably more that I forgot :) (and CI is telling me that I messed up my rules when I rebased and more non linux issues...).
As such it's just a big empty skeleton but I'd like to have opinions on it, to see if I need to go further or I should abandon that.