-
Notifications
You must be signed in to change notification settings - Fork 86
Add try_set_scoped_handler: Scoped Ctrl-C Handler for Non-'static Closures #132
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
base: master
Are you sure you want to change the base?
Conversation
Hi. Thanks for the PR, it is well documented and the code looks good by first glance. However, like with other PRs that want to add extra functionality I'm questioning whether the benefits outweigh the added complexity. Do you have some concrete use cases of signal handling where this kind of scoped handling would be needed? |
Hi, thank you for your thoughtful feedback. You’re absolutely right to ask whether the added functionality justifies the increased complexity—this is a fair and important question when maintaining a crate like ctrlc. To be honest, I don’t have a strong performance-based argument for this addition. The difference in real-world applications is likely negligible in most cases. However, from the perspective of idiomatic Rust usage and ergonomics, I believe this functionality fills a meaningful gap. Specifically, when writing concurrent code with thread::scope, I find it frustrating that I need to reach for Arc or other synchronization mechanisms even when they are not conceptually necessary, just to satisfy 'static bounds imposed by the current API. This leads to code that:
I understand this might sound like a “purist” or stylistic argument, but for those of us who care deeply about expressing correctness and lifetime boundaries precisely, being forced into 'static when we could do better is genuinely disappointing. I hope this helps clarify the motivation. I’d be happy to further refine the design or documentation if needed. Thanks again for your consideration. |
I get the idea, but let's stop here for a minute. The very idea of handling signals differently for different threads is very advanced and I doubt it's very rarely used in practice. If you have multiple threads, then you'd want to set the signal masks appropriately because it's not defined to which thread of an application the OS sends the signal to, unless you specify it yourself or use signal masks to define the behavior. Such advanced use is out of the scope of this crate. This crate is just for handling the simple case of SIGINT and SIGTERM and I want to keep it like that. As the readme states, there are better crates available for more advanced use cases. |
Thanks for the clarification — I see where you're coming from. Just to be clear, I’m not trying to introduce advanced per-thread signal handling or mess with signal masks. I’m simply hoping to make it easier to write scoped, idiomatic Rust without reaching for To illustrate this more concretely, here’s a comparison of what I’m talking about: Current (with
|
Thanks for the example, this is what I was looking for. I misunderstood the benefits this would provide, sorry about that. The example indeed seems nice in comparison. I will take a look at this a bit deeper level once I have the time. Thank you for the patience. |
Glad to hear that — and thanks again for maintaining such a solid crate! |
First of all, thank you for providing such an excellent crate. It has been incredibly helpful in the development of my own CLI tools!
This PR introduces a new function, try_set_scoped_handler, that enables setting a scoped Ctrl-C (SIGINT) signal handler using a std::thread::Scope. Unlike the existing set_handler and try_set_handler APIs, this version allows the use of non-'static closures, making it possible to reference stack-local state without requiring heap allocations or synchronization primitives like Arc.
Motivation
The existing signal handler APIs require 'static closures, which limits their flexibility—especially in multi-threaded code where state is short-lived and scoped. This new function is particularly useful in structured concurrency scenarios (e.g., with thread::scope), where signal handling logic only needs to live within a temporary thread scope.
Highlights
Example
Safety and Semantics
Limitations