Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signal handling fixes #3669

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft

Conversation

alebastr
Copy link
Contributor

@alebastr alebastr commented Oct 7, 2024

First draft. Tested on Linux with glibc only.

See individual commit descriptions for details.

TODOs:

  • It should not be necessary to recreate the outputs. But the whole thing has so many races and weird things happen if I keep using the existing output list.
    Time to dig out my old work on reducing the number of wl_display_roundtrips in the code.
  • CssReloadHelper does not handle appearance changes and .reset() on reloads can be optimized.

Fixes: #3344

The previous implementation was not async-signal-safe and could cause
lots of hard to debug issues.

The common ways to handle that include
 * Self-pipe and non-allocating lock-free set of pending signals in the
   main thread (see `g_unix_signal_source_new` for example).
 * A dedicated signal handling thread calling `sigtimedwait`.
 * Linux-specific solution with `signalfd(2)`.
 * FreeBSD-specific solution with `EVFILT_SIGNAL` filter for `kqueue`.

`GUnixSignalSource` intentionally supports a very limited set of
signals, making it a bad fit for us. There's also no benefit in
maintaining a set of platform-dependent solutions. Therefore, extending
an existing `signalThread` code seems to be the best option.

All the threads and child processes inherit the signal mask we set in
`UnixSignalHandler::start()`, and now we block a few more signals.
That requires an extra care when forking and starting new processes.
Thankfully, `waybar::util::command` already does everything necessary
and appears to be used consistently across the source.
Repeated calls to `Client::main()` result in unnecessary work and
duplication of several event handlers. A common consequence of that is
the bug with multiple bars being created (Alexays#3344).

This change ensures that the `main` is called only once and makes reload
logic much simpler.
@ShellCode33
Copy link

Hi, I'm coming from #3344. Thanks for your work on this!

I see that you are spawning a new thread in the signal handler:

void UnixSignalHandler::start() {
// Block signals so they can be handled by the signal thread
// Any threads created by this one (the main thread) should not
// modify their signal mask to unblock the handled signals
if (int err = pthread_sigmask(SIG_BLOCK, &mask_, nullptr); err != 0) {
spdlog::error("pthread_sigmask failed in UnixSignalHandler::start: {}", strerror(err));
exit(1);
}
running_ = true;
thread_ = std::thread([this]() { run(); });
}

Even though that will probably work most of the time, you unfortunately cannot do that, pthread_create is not async-signal-safe. You might end up with users reporting bugs that are extremely complex to diagnose.

You can have a look at man 7 signal-safety for a list of functions you can call in a signal handler.
Some pthread functions are there:

pthread_kill(3)        Added in POSIX.1-2008 TC1
pthread_self(3)        Added in POSIX.1-2008 TC1
pthread_sigmask(3)     Added in POSIX.1-2008 TC1

But not pthread_create. What people usually do in signal handlers is that they set an atomic boolean (let's say sigusr2_needs_to_be_processed) to true, and handle it in a separate thread that is already running. Once the thread is done handling the signal, it would set sigusr2_needs_to_be_processed back to false.

@alebastr
Copy link
Contributor Author

I see that you are spawning a new thread in the signal handler:

That's not what is happening here.

The signal handling thread is started from the main thread in normal context, then we mask all the signals that are interesting for us for all the application threads and listen for signals with sigtimedwait(2). Not a single line of the new code runs in a signal handler context.

@ShellCode33
Copy link

Oh my bad, I misunderstood what's going on then, I'm sorry for wasting your time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multiple waybars open after resume from dpms off.
2 participants