Ingest pty input in a different thread
At #2678 (comment 1931511) chpe wrote:
It would be lots of work probably, but offloading the whole processing into a thread would the ideal solution, I think. Have PTY I/O, parsing, putting into ring etc on the thread, and regularly (time based?) send a "snapshot" over to the UI thread (from where it would emit the necessary signals like adjustment updates, etc). Let's maybe keep that as the long-term goal?
I think it would be a great change, and could easily result in great performance boost especially in "overloaded" situations (as discussed in #2678 (closed)/#2698 (closed)), where fine-tuning any algorithm that decides how much time to spend on which operation could take up a lot of dev resources and still may not result in as good throughput and responsiveness as multi-threading would.
My vague memories tell me that GTK expects UI updates to happen from the main thread (or rather: all GTK operations to happen from one dedicated thread only). I don't know if it's (still) correct. But anyway, in accordance with chpe's comment, I think the "main" thread should do the UI updates and stuff, and the "helper" thread(s) should do the pty ingestion.
Probably a separate thread for each pty, why not. This could save a select()
-like call, each thread would just directly wait for the next data in a read()
. Also the locking (see below) would be more fine-grained, again helping to have better performance.
As for
send a "snapshot"
I don't think we should "send" any data, in the usual meaning of the word "send". After all, we'd have a common memory.
We already do a Ring -> RingView conversion before every update (if the data actually changed), and that's where the main logical split is between the data as modified by the input processing code, vs. the data that is to be displayed. The "helper" pty threads would keep updating the Ring, plus some additional variables (like adjustment values). The "main" UI thread would every once in a while perform the Ring -> RingView translation, and then paint the canvas based on RingView. This way "snapshotting" wouldn't even involve additional data copying of terminal data compared to what we already do, only a tiny amount of metadata (like insert_delta, adjustment values) would need to be copied.
Obivously a mutex would be grabbed for the beginning of the Ring -> RingView conversion (but can already be released for the BiDi step); similarly, by the pty thread for the duration of updating the Ring, released after processing each chunk.
As an optional bonus:
Right now the rewrapping code is synchronous, blocking the entire application for its duration. Which is pretty bad with giant (1M+ lines) scrollbacks. With threading, rewrapping could be the task of the helper thread that ingests the pty data. An additional variable could tell to the main thread not to wait for the mutex, as it won't be available for a while, instead it should go ahead and paint the UI according to its previous RingView snapshot. This way the overall UI would remain responsive.