# Tldr: Based on discussion that happened on IRC, there seems to be a vague agreement towards the approach (originally suggested by Tristan in [0]) of having the UI run in the main `bst` process, with `Stream` running in a subprocess, assuming we can make signal handling and the interactive shell both work together nicely. I've attempted to articulate in a bit more detail what I think this approach will look like, and a couple of possible approaches to handle the interactive shell. # The process model The front-end would remain in the parent process. Each of the main `Stream` entry points would be spawned into a separate process. This subprocess would call `setsid` to ensure that signals are received and handled only by the front-end process. I'll add some more detail on this bellow. # Message handling The message handler in `Context` will send messages over a queue to the front-end to handle. Currently, the front-end uses global state to gain some of the information it renders. After the split, all state needed by the front-end will be passed as explicit parameters of each `Message` object (with the exception of any BuildStream configuration, which will be still loaded and available before the `Stream` process is spawned and therefore available to the front-end without any changes needed). # Signal handling All signals will be caught by the front-end process. The font-end will be responsible for terminating/suspending/resuming the `Stream` process as appropriate. Although I'm still a bit hazy on the implementation details here, I imagine this will work in a very similar way to how we currently interact with scheduler jobs and will reuse much of the same code. # Interactive UI prompts The front-end will provide a service which can called to display interactive prompts. I think this would work something like: 0) `Stream` process sends a message to the front-end saying "please display this interactive prompt" 1) Front-end stops displaying new messages, queuing them up for later 2) Front-end displays prompt 3) Front-end waits for input which it then passes back to the `Stream` process 4) `Stream` process does any necessary input validation 5) If the input is valid: * the `Stream` sends message back to accept the provided value * The front-end resumes displaying the queued messages 6) If the input is invalid: * The `Stream` sends a message back to the front-end rejecting the returned value, with an optional helpful error message. * The front-end will display the error message if provided * Go back to 3 I would imagine this service would be accessed via the context # The Interactive shell As pointed out by Tristan, calling `setsid` in the `Stream` process means the `Stream` is not in the process group which owns the terminal. As a result, it wouldn't be easy (or possible?) to have a shell created in the `Stream` process take over the terminal. One approach I think would circumvent this limitation would be for the front-end to provide an API along the lines of execute_in_foreground, this would do the specified work in the front-end process, taking over the terminal while it it running. This would: * pause the displaying of messages, allowing any new messages to queue up. * execute the given work in the front-end process * pass any return value back to the calling process * Continue printing queued messages Another approach would be to simply special case the `Stream.shell` method, so that it does not run in a subprocess. The shell only displays loading and staging messages and as so far as I can see will never be rendering a large number of messages. While I'm not really a fan of special casing one of the `Stream` methods, this would have the advantage of reducing the complexity of the implementation. What do people think? Cheers, Phil [0] https://mail.gnome.org/archives/buildstream-list/2019-May/msg00015.html -- Phil Dawson, Software Engineer Codethink Ltd https://www.codethink.co.uk/ We respect your privacy. See https://www.codethink.co.uk/privacy.html |