|
| 1 | +# Threads |
| 2 | + |
| 3 | +Under the hood, Moulti uses multiple [threads](https://en.wikipedia.org/wiki/Thread_(computing)). |
| 4 | +This document intends to describe them. |
| 5 | + |
| 6 | +## Main thread |
| 7 | + |
| 8 | +Name: `moulti:main` |
| 9 | + |
| 10 | +Moulti's main thread runs [Textual](https://textual.textualize.io/)'s |
| 11 | +[asyncio](https://docs.python.org/3/library/asyncio.html) |
| 12 | +[event loop](https://en.wikipedia.org/wiki/Event_loop). |
| 13 | +Otherly put, it manages most of the TUI part. |
| 14 | + |
| 15 | +## Textual threads |
| 16 | + |
| 17 | +All Textual applications spawn two unnamed threads and Moulti is no exception: |
| 18 | + |
| 19 | +- a first thread renders the user interface to stderr; |
| 20 | +- a second thread reads events (e.g. keystrokes and mouse events) from stdin. |
| 21 | + |
| 22 | +## Network loop |
| 23 | + |
| 24 | +Name: `network-loop` |
| 25 | + |
| 26 | +Once the TUI part is ready, Moulti spawns a third thread that runs the network |
| 27 | +loop, i.e. the infinite loop that handles network events: new connections, |
| 28 | +incoming messages, outgoing responses, etc. |
| 29 | +This loop relies on non-blocking Unix sockets. Unlike the main thread, it does |
| 30 | +not leverage asyncio. |
| 31 | +Incoming messages are analyzed within this thread but actual TUI changes (e.g. |
| 32 | +adding or removing steps) are delegated to the main thread. |
| 33 | + |
| 34 | +## Execute command |
| 35 | + |
| 36 | +Name: `exec-command` |
| 37 | + |
| 38 | +When Moulti is launched through [moulti run](shell-scripting.md#moulti-run), and |
| 39 | +once the network loop is ready, Moulti spawns a fourth thread in charge of |
| 40 | +executing and monitoring the given command. |
| 41 | + |
| 42 | +## moulti pass |
| 43 | + |
| 44 | +Each `moulti pass` command results in Moulti spawning two extra threads. |
| 45 | + |
| 46 | +### Data ingestion |
| 47 | + |
| 48 | +Name: `|step_name` |
| 49 | + |
| 50 | +This thread reads data from the file descriptor provided by `moulti pass`. |
| 51 | +This file descriptor is typically a pipe, hence the `|` prefix. |
| 52 | + |
| 53 | +### Data processing |
| 54 | + |
| 55 | +Name: `>step_name` |
| 56 | + |
| 57 | +This thread converts data read by the ingestion thread so they can be |
| 58 | +added to the TUI. |
| 59 | +The `>` prefix represents the idea that lines get written to the target step. |
| 60 | +Unlike the `>` redirection in shell languages, this symbol does not imply creating or writing to files. |
| 61 | + |
| 62 | +### Why do step names look weird? |
| 63 | + |
| 64 | +Thread names cannot exceed: |
| 65 | + |
| 66 | +- 15 characters on Linux; |
| 67 | +- 63 characters on macOS; |
| 68 | +- 19 characters on FreeBSD; |
| 69 | +- 23 characters on OpenBSD; |
| 70 | +- 15 characters on NetBSD. |
| 71 | + |
| 72 | +Here, the first character is either `|` or `>`, leaving only: |
| 73 | + |
| 74 | +- 14 characters on Linux; |
| 75 | +- 62 characters on macOS; |
| 76 | +- 18 characters on FreeBSD; |
| 77 | +- 22 characters on OpenBSD; |
| 78 | +- 14 characters on NetBSD. |
| 79 | + |
| 80 | +This is why longer step names must be abridged in thread names. |
| 81 | +This is particularly true on Linux and NetBSD where abridged names keep: |
| 82 | + |
| 83 | +- the first 4 characters; |
| 84 | +- the middle 5 characters; |
| 85 | +- the last 5 characters. |
| 86 | + |
| 87 | +!!! example "Examples" |
| 88 | + - `moulti pass abcdefghijklmnopqrstuvwxyz` results in threads |
| 89 | + `|abcdklmnovwxyz` and `>abcdklmnovwxyz`: |
| 90 | + |
| 91 | + ``` |
| 92 | + abcdefghijklmnopqrstuvwxyz |
| 93 | + ^^^^ ^^^^^ ^^^^^ |
| 94 | + first 4 middle 5 last 5 |
| 95 | + ``` |
| 96 | + |
| 97 | + - The `moulti_run_output` special step results in threads `|mouli_runutput` |
| 98 | + and `>mouli_runutput`. |
| 99 | + |
| 100 | + ``` |
| 101 | + moulti_run_output |
| 102 | + ^^^^ ^^^^^ ^^^^^ |
| 103 | + | | `---- last 5 |
| 104 | + | `----------- middle 5 |
| 105 | + `---------------- first 4 |
| 106 | + ``` |
| 107 | + |
| 108 | +## Save / export contents |
| 109 | + |
| 110 | +Name: `export-to-dir` |
| 111 | + |
| 112 | +Through its "Save" action, Moulti is able to [export all shown contents to text |
| 113 | +files](saving-and-loading.md/#saving-a-complete-moulti-instance) stored inside |
| 114 | +a timestamped directory, hence the name of the thread dedicated to this operation. |
| 115 | + |
| 116 | +## Inactive threads |
| 117 | + |
| 118 | +Name: `thread-pool` |
| 119 | + |
| 120 | +Threads that have finished executing are not deleted. |
| 121 | +Instead, they remain inactive until the [thread pool](https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor) decides to reuse them. |
| 122 | + |
| 123 | +## How to inspect thread names? |
| 124 | + |
| 125 | +The previous sections mention thread names. |
| 126 | +But how to inpsect threads and their names in the first place? |
| 127 | + |
| 128 | +### How to inspect thread names on Linux? |
| 129 | + |
| 130 | +Combining `pgrep` and `pstree` (typically found in the `psmisc` package) does wonders: |
| 131 | + |
| 132 | +``` |
| 133 | +pgrep moulti:main | xargs -n 1 pstree -napt |
| 134 | +``` |
| 135 | + |
| 136 | +Alternatively, use `htop`: hit `H` to toggle threads. |
| 137 | + |
| 138 | +### How to inspect thread names on FreeBSD? |
| 139 | + |
| 140 | +``` |
| 141 | +ps Ho pid,tid,tdname,command |
| 142 | +``` |
| 143 | + |
| 144 | +Thread names are shown in the `TDNAME` column. |
| 145 | + |
| 146 | +### How to inspect thread names on OpenBSD? |
| 147 | + |
| 148 | +``` |
| 149 | +ps Ho pid,tid,command |
| 150 | +``` |
| 151 | + |
| 152 | +Thread names are shown at the end of the `COMMAND` column, between parens, after |
| 153 | +the `/` character. |
| 154 | + |
| 155 | +### How to inspect thread names on NetBSD? |
| 156 | + |
| 157 | +``` |
| 158 | +top -t -c -U $(whoami) |
| 159 | +``` |
| 160 | + |
| 161 | +Thread names are shown in the `NAME` column, which is unfortunately truncated |
| 162 | +to 9 characters. |
| 163 | + |
| 164 | +### How to inspect thread names on macOS? |
| 165 | + |
| 166 | +On macOS, listing threads requires root privileges: |
| 167 | + |
| 168 | +``` |
| 169 | +sudo htop -t -u $(whoami) |
| 170 | +``` |
| 171 | + |
| 172 | +## How to turn off thread names? |
| 173 | + |
| 174 | +If you suspect naming threads triggers issues (e.g. crash on some niche |
| 175 | +operating system), you can disable it by setting the |
| 176 | +[`MOULTI_NAME_THREADS`](environment-variables.md/#moulti_name_threads) |
| 177 | +environment variable to `no`: |
| 178 | + |
| 179 | +```bash |
| 180 | +export MOULTI_NAME_THREADS=no |
| 181 | +``` |
0 commit comments