I have found no way to reload the configuration without restarting the whole process (which is only possible through restarting i3). However, this is inconvenient and from time to time causes weird display bugs in my system.
What issues do you experience exactly when restarting i3 in-place (i.e. without restarting the X server)?
For example sometimes i3 does not come up again, for unknown reasons. Or the UI hangs after i3 restart until I kill the compositor.
This does not happen very often, only sometimes. Like every 10 restarts or so.
I use geany, which crashes on about every 5th i3 restart. But i think since the config isn't changed that often this is not very important.
I didn't realize people had such unstable systems... Given the current structure of the code, adding async configuration updates would be a pretty big undertaking, even if this is a desirable feature.
Wouldn't this be easily achieveable by just calling exec() and restarting the program? If that is not a preferred solution, I would agree with @GladOSkar and close this issue since it's not so important.
Also, another use for a signal would be updating all blocks like the classic i3status does on SIGURS1.
Wouldn't this be easily achieveable by just calling exec() and restarting the program?
I hadn't thought of that. Sneaky. Any possible side effects?
Maybe. Some connections/file descriptors could stay open when the close-on-exec flag isn't set there. However this can easily be checked by just trying it and then looking at /proc.
Another, probably more idiomatic solution would be to wrap the main function inside a loop and each time SIGUSR1 occours, exit from the main function and restart it. So, in other words, implementing restarting manually.
Another, probably more idiomatic solution would be to wrap the main function inside a loop and each time SIGUSR1 occours, exit from the main function and restart it.
I do not believe that this is possible, due to the way we currently use per-block threads.
If someone whips up a proof-of-concept using the exec() approach we'd happily review it, but lacking a champion this feature is unlikely to be implemented. I'm marking this closed in the meantime.
I think it is worth reopening this, considering one might like to use SIGUSR1 to change just the theme. Isn't that easier to implement? At the moment I'm reloading the window manager to mach the new config.toml symlinked to config_dark.toml or config_light.toml. The only changes are idle_bg and idle_fg in [theme.overrides].
No one disagrees that this would be nice to have -- it was closed because we did not see a path forward on implementing it.
If someone stumbles upon this and wants a temporary solution:
I created a small, unelegant python script which makes reloading the bar possible through sending a SIGUSR1 signal without having to restart i3.
import subprocess
import sys
import signal
import time
r = None
args = sys.argv
args[0] = "i3status-rs"
def restart(snr,stack):
global r
r.terminate()
r = subprocess.Popen(args,stdout=subprocess.PIPE)
r.stdout.readline()
k = r.stdout.readline()
sys.stdout.buffer.write(k[1:])
sys.stdout.flush()
signal.signal(signal.SIGUSR1, restart)
r = subprocess.Popen(args,stdout=subprocess.PIPE)
while True:
sys.stdout.buffer.write(r.stdout.readline())
sys.stdout.flush()
@atheriel , can you explain to a non-Rust developer what the issue is? Is this a limitation in Rust itself? Does it not support interrupts? I'm wondering mainly because the functionality is common both among i3status replacements and outside of that space, and is not difficult in the languages I'm current on. I am curious whether this is something to do with the design of i3status-rs, or is a limitation in the language.
@xxxserxxx Sure!
We currently use MPMC channels to send "update" messages from blocks back to the main thread. But these channels are only one-way (at least at present, maybe this is possible to change). So if we did receive a signal to update the config, we currently have no way to communicate these changes across all the threads.
Rust is not particularly special here, although it does make it harder. Signal handlers run in an arbitrary thread on Linux, so in general to use them in a multithreaded application you need a way of synchronizing state across threads. Common hacky ways of doing this in other languages (e.g. some volatile global variable) are generally disallowed by default in Rust, since they're horribly thread unsafe. We would have to do something more thread safe, or expand the way we currently pass messages.
The alternative is basically to kill all the threads and start from scratch, which is equivalent to restarting the process. This is how @boi4's clever workaround works, as does the exec() approach discussed above.
Hm. That'd be a fair bit of ABI to change, to be sure, but setting up channels in the other direction so the configuration management process -- whatever that is -- could read and spam out new configuration information in an immutable message doesn't seem like it'd be hard -- it's just a lot of little changes to function definitions, right? And some code in the functions to listen on a channel (set up when the process is forked) and update the process-local configuration.
I'm not trying to suggest that it isn't work; I interpreted when you said
we did not see a path forward on implementing it.
as meaning you weren't sure you could see a solution, which is different (in my mind) from "it's a heck of a lot of work."
I'm going to re-open this as I have found the need for this while developing i3status-rs on sway(-git). Reloads sometimes crash sway which isn't nice to deal with, and testing with sway within sway has it's own limitations.
@faulesocke By exec() do you mean using C's exec* functions?
Looks like the nix crate we already use has what we would need: https://docs.rs/nix/0.8.1/nix/unistd/fn.execvp.html
However they are used to run binaries, how are we to use it to restart i3status-rs within the code itself?