@@ -90,34 +90,128 @@ static INIT_LOCK: Mutex<()> = Mutex::new(());
90
90
///
91
91
/// # Panics
92
92
/// Any panic in the handler will not be caught and will cause the signal handler thread to stop.
93
- pub fn set_handler < F > ( user_handler : F ) -> Result < ( ) , Error >
93
+ pub fn set_handler < F > ( mut user_handler : F ) -> Result < ( ) , Error >
94
94
where
95
95
F : FnMut ( ) + ' static + Send ,
96
96
{
97
- init_and_set_handler ( user_handler, true )
97
+ init_and_set_handler (
98
+ move || {
99
+ user_handler ( ) ;
100
+ false
101
+ } ,
102
+ true ,
103
+ StaticExecutor ,
104
+ )
98
105
}
99
106
100
107
/// The same as ctrlc::set_handler but errors if a handler already exists for the signal(s).
101
108
///
102
109
/// # Errors
103
110
/// Will return an error if another handler exists or if a system error occurred while setting the
104
111
/// handler.
105
- pub fn try_set_handler < F > ( user_handler : F ) -> Result < ( ) , Error >
112
+ pub fn try_set_handler < F > ( mut user_handler : F ) -> Result < ( ) , Error >
106
113
where
107
114
F : FnMut ( ) + ' static + Send ,
108
115
{
109
- init_and_set_handler ( user_handler, false )
116
+ init_and_set_handler (
117
+ move || {
118
+ user_handler ( ) ;
119
+ false
120
+ } ,
121
+ false ,
122
+ StaticExecutor ,
123
+ )
124
+ }
125
+
126
+ /// Register a scoped Ctrl-C signal handler.
127
+ ///
128
+ /// This function registers a Ctrl-C (SIGINT) signal handler using a scoped thread context,
129
+ /// allowing the use of non-`'static` closures. This is particularly useful for managing
130
+ /// state that lives within the scope of a thread, without requiring `Arc` or other
131
+ /// heap-allocated synchronization primitives.
132
+ ///
133
+ /// Unlike [`ctrlc::set_handler`] or [`ctrlc::try_set_handler`], the provided handler does not need to be `'static`,
134
+ /// as it is guaranteed not to outlive the given [`std::thread::Scope`].
135
+ ///
136
+ /// # Example
137
+ ///
138
+ /// ```no_run
139
+ /// use std::sync::atomic::{AtomicBool, Ordering};
140
+ /// use std::thread;
141
+ ///
142
+ /// let flag = AtomicBool::new(false);
143
+ /// thread::scope(|s| {
144
+ /// ctrlc::try_set_scoped_handler(s, || {
145
+ /// // Because the handler is scoped, we can use non-'static references.
146
+ /// flag.store(true, Ordering::SeqCst);
147
+ /// true // Returning `true` ensures the handler will not be invoked again.
148
+ /// }).unwrap();
149
+ ///
150
+ /// // Do some work...
151
+ /// });
152
+ /// ```
153
+ ///
154
+ /// > **Note**: Unlike `set_handler`, this function requires that the signal handler
155
+ /// > eventually terminate. If the handler returns `false`, the signal handler thread
156
+ /// > continues running, and the enclosing scope will never complete. Always ensure that
157
+ /// > the handler returns `true` at some point.
158
+ ///
159
+ /// # Semantics
160
+ ///
161
+ /// - The handler must return a `bool`, indicating whether the handler should be de-registered:
162
+ /// - `true`: the handler is removed and will not be called again.
163
+ /// - `false`: the handler remains active and will be called again on subsequent signals.
164
+ /// - This design ensures that the enclosing thread scope can only exit once the handler
165
+ /// has completed and returned `true`.
166
+ ///
167
+ /// # Limitations
168
+ ///
169
+ /// - Only one scoped handler may be registered per process.
170
+ /// - If a handler is already registered (scoped or static), this function will return an error.
171
+ /// - There is **no** `set_scoped_handler`; a scoped handler cannot be replaced once registered,
172
+ /// even if it has already finished executing.
173
+ ///
174
+ /// # Errors
175
+ ///
176
+ /// Returns an error if:
177
+ /// - A handler is already registered (scoped or static).
178
+ /// - A system-level error occurs during signal handler installation.
179
+ ///
180
+ /// # Panics
181
+ ///
182
+ /// If the handler panics, the signal handling thread will terminate and not be restarted. This
183
+ /// may leave the program in a state where no Ctrl-C handler is installed.
184
+ ///
185
+ /// # Safety
186
+ ///
187
+ /// The handler is executed in a separate thread, so ensure that shared state is synchronized
188
+ /// appropriately.
189
+ ///
190
+ /// See also: [`try_set_handler`] for a `'static` version of this API.
191
+ pub fn try_set_scoped_handler < ' scope , ' f : ' scope , ' env , F > (
192
+ scope : & ' scope thread:: Scope < ' scope , ' env > ,
193
+ user_handler : F ,
194
+ ) -> Result < ( ) , Error >
195
+ where
196
+ F : FnMut ( ) -> bool + ' f + Send ,
197
+ {
198
+ init_and_set_handler ( user_handler, false , ScopedExecutor { scope } )
110
199
}
111
200
112
- fn init_and_set_handler < F > ( user_handler : F , overwrite : bool ) -> Result < ( ) , Error >
201
+ fn init_and_set_handler < ' scope , ' f : ' scope , F , E > (
202
+ user_handler : F ,
203
+ overwrite : bool ,
204
+ executor : E ,
205
+ ) -> Result < ( ) , Error >
113
206
where
114
- F : FnMut ( ) + ' static + Send ,
207
+ F : FnMut ( ) -> bool + ' f + Send ,
208
+ E : Executor < ' scope > ,
115
209
{
116
210
if !INIT . load ( Ordering :: Acquire ) {
117
211
let _guard = INIT_LOCK . lock ( ) . unwrap ( ) ;
118
212
119
213
if !INIT . load ( Ordering :: Relaxed ) {
120
- set_handler_inner ( user_handler, overwrite) ?;
214
+ set_handler_inner ( user_handler, overwrite, executor ) ?;
121
215
INIT . store ( true , Ordering :: Release ) ;
122
216
return Ok ( ( ) ) ;
123
217
}
@@ -126,23 +220,62 @@ where
126
220
Err ( Error :: MultipleHandlers )
127
221
}
128
222
129
- fn set_handler_inner < F > ( mut user_handler : F , overwrite : bool ) -> Result < ( ) , Error >
223
+ fn set_handler_inner < ' scope , ' f : ' scope , F , E > (
224
+ mut user_handler : F ,
225
+ overwrite : bool ,
226
+ executor : E ,
227
+ ) -> Result < ( ) , Error >
130
228
where
131
- F : FnMut ( ) + ' static + Send ,
229
+ F : FnMut ( ) -> bool + ' f + Send ,
230
+ E : Executor < ' scope > ,
132
231
{
133
232
unsafe {
134
233
platform:: init_os_handler ( overwrite) ?;
135
234
}
136
235
137
- thread:: Builder :: new ( )
138
- . name ( "ctrl-c" . into ( ) )
139
- . spawn ( move || loop {
236
+ let builder = thread:: Builder :: new ( ) . name ( "ctrl-c" . into ( ) ) ;
237
+ executor
238
+ . spawn ( builder , move || loop {
140
239
unsafe {
141
240
platform:: block_ctrl_c ( ) . expect ( "Critical system error while waiting for Ctrl-C" ) ;
142
241
}
143
- user_handler ( ) ;
242
+ let finished = user_handler ( ) ;
243
+ if finished {
244
+ break ;
245
+ }
144
246
} )
145
247
. map_err ( Error :: System ) ?;
146
248
147
249
Ok ( ( ) )
148
250
}
251
+
252
+ trait Executor < ' scope > {
253
+ /// クロージャをスレッド/スコープに飛ばす
254
+ fn spawn < F > ( self , builder : thread:: Builder , f : F ) -> Result < ( ) , std:: io:: Error >
255
+ where
256
+ F : FnOnce ( ) + Send + ' scope ;
257
+ }
258
+
259
+ struct ScopedExecutor < ' scope , ' env : ' scope > {
260
+ scope : & ' scope thread:: Scope < ' scope , ' env > ,
261
+ }
262
+ impl < ' scope , ' env : ' scope > Executor < ' scope > for ScopedExecutor < ' scope , ' env > {
263
+ fn spawn < F > ( self , builder : thread:: Builder , f : F ) -> Result < ( ) , std:: io:: Error >
264
+ where
265
+ F : FnOnce ( ) + Send + ' scope ,
266
+ {
267
+ builder. spawn_scoped ( self . scope , f) ?;
268
+ Ok ( ( ) )
269
+ }
270
+ }
271
+
272
+ struct StaticExecutor ;
273
+ impl Executor < ' static > for StaticExecutor {
274
+ fn spawn < F > ( self , builder : thread:: Builder , f : F ) -> Result < ( ) , std:: io:: Error >
275
+ where
276
+ F : FnOnce ( ) + Send + ' static ,
277
+ {
278
+ builder. spawn ( f) ?;
279
+ Ok ( ( ) )
280
+ }
281
+ }
0 commit comments