How should a library create a temporary signal handler?

I made a library that's used by a larger application. The application calls some function of my library, which is blocking. Currently, you cannot interrupt the application while my library's code is running. I want to be able to terminate my library with Ctrl+C, which should:

  1. Run some cleanup actions, including killing a process, starting a process, and writing to files. This does not need to be interruptible; e.g. a second Ctrl+C does not need to do anything.
  2. Return an Err(Error::Interrupted) to the application, letting it handle the error, and continue running if it wants.

However, I don't want any trace of my signal handling to be left after my library's function finishes executing. What is the best way to do this?

Windows support is appreciated, but not required.

Signal handlers are a process global resource. As a library you probably shouldn't register signal handlers in case they will conflict with those set by the main program. You could try asking your users to register a signal handler instead and expose a function that should be called on SIGINT (but probably not from within the signal handler as some of the things you do are not async-signal-safe).

2 Likes

Signal-hook — Rust API for Unix // Lib.rs is also relevant for same signal handling in Rust. But indeed, it should be left to the application where possible.

1 Like

I will clear up what I mean a little bit:
My "library" (jetbrains-toolbox-updater) is just an application that exposes a single function to run the entire thing as a library, to allow the big application to run it. Its library has exactly one user, the big application (topgrade). I could've made topgrade parse the output of jetbrains-toolbox-updater, but I didn't want to distribute them separately. That is why it's a library at all.

I have control of topgrade. Topgrade sets a signal handler, but the signal handler only sets an AtomicBool that is unset afterwards, to check whether a subprocess was killed by Ctrl+C, or by exiting by itself. I do not care about any other applications using jetbrains-toolbox-updater as a library.

I want to keep jetbrains-toolbox-updater as decoupled from topgrade as possible. It should be usable as an application by itself.

Do you think this justifies a temporary signal handler? Or is there a better solution I'm missing?

I see. Would it make sense to have topgrade spawn std::env::current_exe() with an argument to tell it to only invoke jetbrains-toolbox-updater and then exit? If so you can permanently register the signal handler for jetbrains-toolbox-updater in the child process.

That's possible, but hacky. And (although I said I have control over topgrade) I would rather not add coupling like that to topgrade.

ChatGPT came up with two different solutions:

  1. Using signal_hook::iterator::Signals and then handle.close()
  2. Using signal_hook::low_level::{register,unregister}

I couldn't find enough information on them in the docs to really understand them deeply, but how are those?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.