/rust/registry/src/index.crates.io-1949cf8c6b5b557f/crossbeam-utils-0.8.21/src/thread.rs
Line | Count | Source |
1 | | //! Threads that can borrow variables from the stack. |
2 | | //! |
3 | | //! Create a scope when spawned threads need to access variables on the stack: |
4 | | //! |
5 | | //! ``` |
6 | | //! use crossbeam_utils::thread; |
7 | | //! |
8 | | //! let people = vec![ |
9 | | //! "Alice".to_string(), |
10 | | //! "Bob".to_string(), |
11 | | //! "Carol".to_string(), |
12 | | //! ]; |
13 | | //! |
14 | | //! thread::scope(|s| { |
15 | | //! for person in &people { |
16 | | //! s.spawn(move |_| { |
17 | | //! println!("Hello, {}!", person); |
18 | | //! }); |
19 | | //! } |
20 | | //! }).unwrap(); |
21 | | //! ``` |
22 | | //! |
23 | | //! # Why scoped threads? |
24 | | //! |
25 | | //! Suppose we wanted to re-write the previous example using plain threads: |
26 | | //! |
27 | | //! ```compile_fail,E0597 |
28 | | //! use std::thread; |
29 | | //! |
30 | | //! let people = vec![ |
31 | | //! "Alice".to_string(), |
32 | | //! "Bob".to_string(), |
33 | | //! "Carol".to_string(), |
34 | | //! ]; |
35 | | //! |
36 | | //! let mut threads = Vec::new(); |
37 | | //! |
38 | | //! for person in &people { |
39 | | //! threads.push(thread::spawn(move || { |
40 | | //! println!("Hello, {}!", person); |
41 | | //! })); |
42 | | //! } |
43 | | //! |
44 | | //! for thread in threads { |
45 | | //! thread.join().unwrap(); |
46 | | //! } |
47 | | //! ``` |
48 | | //! |
49 | | //! This doesn't work because the borrow checker complains about `people` not living long enough: |
50 | | //! |
51 | | //! ```text |
52 | | //! error[E0597]: `people` does not live long enough |
53 | | //! --> src/main.rs:12:20 |
54 | | //! | |
55 | | //! 12 | for person in &people { |
56 | | //! | ^^^^^^ borrowed value does not live long enough |
57 | | //! ... |
58 | | //! 21 | } |
59 | | //! | - borrowed value only lives until here |
60 | | //! | |
61 | | //! = note: borrowed value must be valid for the static lifetime... |
62 | | //! ``` |
63 | | //! |
64 | | //! The problem here is that spawned threads are not allowed to borrow variables on stack because |
65 | | //! the compiler cannot prove they will be joined before `people` is destroyed. |
66 | | //! |
67 | | //! Scoped threads are a mechanism to guarantee to the compiler that spawned threads will be joined |
68 | | //! before the scope ends. |
69 | | //! |
70 | | //! # How scoped threads work |
71 | | //! |
72 | | //! If a variable is borrowed by a thread, the thread must complete before the variable is |
73 | | //! destroyed. Threads spawned using [`std::thread::spawn`] can only borrow variables with the |
74 | | //! `'static` lifetime because the borrow checker cannot be sure when the thread will complete. |
75 | | //! |
76 | | //! A scope creates a clear boundary between variables outside the scope and threads inside the |
77 | | //! scope. Whenever a scope spawns a thread, it promises to join the thread before the scope ends. |
78 | | //! This way we guarantee to the borrow checker that scoped threads only live within the scope and |
79 | | //! can safely access variables outside it. |
80 | | //! |
81 | | //! # Nesting scoped threads |
82 | | //! |
83 | | //! Sometimes scoped threads need to spawn more threads within the same scope. This is a little |
84 | | //! tricky because argument `s` lives *inside* the invocation of `thread::scope()` and as such |
85 | | //! cannot be borrowed by scoped threads: |
86 | | //! |
87 | | //! ```compile_fail,E0521 |
88 | | //! use crossbeam_utils::thread; |
89 | | //! |
90 | | //! thread::scope(|s| { |
91 | | //! s.spawn(|_| { |
92 | | //! // Not going to compile because we're trying to borrow `s`, |
93 | | //! // which lives *inside* the scope! :( |
94 | | //! s.spawn(|_| println!("nested thread")); |
95 | | //! }); |
96 | | //! }); |
97 | | //! ``` |
98 | | //! |
99 | | //! Fortunately, there is a solution. Every scoped thread is passed a reference to its scope as an |
100 | | //! argument, which can be used for spawning nested threads: |
101 | | //! |
102 | | //! ``` |
103 | | //! use crossbeam_utils::thread; |
104 | | //! |
105 | | //! thread::scope(|s| { |
106 | | //! // Note the `|s|` here. |
107 | | //! s.spawn(|s| { |
108 | | //! // Yay, this works because we're using a fresh argument `s`! :) |
109 | | //! s.spawn(|_| println!("nested thread")); |
110 | | //! }); |
111 | | //! }).unwrap(); |
112 | | //! ``` |
113 | | |
114 | | use std::boxed::Box; |
115 | | use std::fmt; |
116 | | use std::io; |
117 | | use std::marker::PhantomData; |
118 | | use std::mem; |
119 | | use std::panic; |
120 | | use std::string::String; |
121 | | use std::sync::{Arc, Mutex}; |
122 | | use std::thread; |
123 | | use std::vec::Vec; |
124 | | |
125 | | use crate::sync::WaitGroup; |
126 | | |
127 | | type SharedVec<T> = Arc<Mutex<Vec<T>>>; |
128 | | type SharedOption<T> = Arc<Mutex<Option<T>>>; |
129 | | |
130 | | /// Creates a new scope for spawning threads. |
131 | | /// |
132 | | /// All child threads that haven't been manually joined will be automatically joined just before |
133 | | /// this function invocation ends. If all joined threads have successfully completed, `Ok` is |
134 | | /// returned with the return value of `f`. If any of the joined threads has panicked, an `Err` is |
135 | | /// returned containing errors from panicked threads. Note that if panics are implemented by |
136 | | /// aborting the process, no error is returned; see the notes of [std::panic::catch_unwind]. |
137 | | /// |
138 | | /// **Note:** Since Rust 1.63, this function is soft-deprecated in favor of the more efficient [`std::thread::scope`]. |
139 | | /// |
140 | | /// # Examples |
141 | | /// |
142 | | /// ``` |
143 | | /// use crossbeam_utils::thread; |
144 | | /// |
145 | | /// let var = vec![1, 2, 3]; |
146 | | /// |
147 | | /// thread::scope(|s| { |
148 | | /// s.spawn(|_| { |
149 | | /// println!("A child thread borrowing `var`: {:?}", var); |
150 | | /// }); |
151 | | /// }).unwrap(); |
152 | | /// ``` |
153 | 0 | pub fn scope<'env, F, R>(f: F) -> thread::Result<R> |
154 | 0 | where |
155 | 0 | F: FnOnce(&Scope<'env>) -> R, |
156 | | { |
157 | | struct AbortOnPanic; |
158 | | impl Drop for AbortOnPanic { |
159 | 0 | fn drop(&mut self) { |
160 | 0 | if thread::panicking() { |
161 | 0 | std::process::abort(); |
162 | 0 | } |
163 | 0 | } |
164 | | } |
165 | | |
166 | 0 | let wg = WaitGroup::new(); |
167 | 0 | let scope = Scope::<'env> { |
168 | 0 | handles: SharedVec::default(), |
169 | 0 | wait_group: wg.clone(), |
170 | 0 | _marker: PhantomData, |
171 | 0 | }; |
172 | | |
173 | | // Execute the scoped function, but catch any panics. |
174 | 0 | let result = panic::catch_unwind(panic::AssertUnwindSafe(|| f(&scope))); |
175 | | |
176 | | // If an unwinding panic occurs before all threads are joined |
177 | | // promote it to an aborting panic to prevent any threads from escaping the scope. |
178 | 0 | let guard = AbortOnPanic; |
179 | | |
180 | | // Wait until all nested scopes are dropped. |
181 | 0 | drop(scope.wait_group); |
182 | 0 | wg.wait(); |
183 | | |
184 | | // Join all remaining spawned threads. |
185 | 0 | let panics: Vec<_> = scope |
186 | 0 | .handles |
187 | 0 | .lock() |
188 | 0 | .unwrap() |
189 | 0 | // Filter handles that haven't been joined, join them, and collect errors. |
190 | 0 | .drain(..) |
191 | 0 | .filter_map(|handle| handle.lock().unwrap().take()) |
192 | 0 | .filter_map(|handle| handle.join().err()) |
193 | 0 | .collect(); |
194 | | |
195 | 0 | mem::forget(guard); |
196 | | |
197 | | // If `f` has panicked, resume unwinding. |
198 | | // If any of the child threads have panicked, return the panic errors. |
199 | | // Otherwise, everything is OK and return the result of `f`. |
200 | 0 | match result { |
201 | 0 | Err(err) => panic::resume_unwind(err), |
202 | 0 | Ok(res) => { |
203 | 0 | if panics.is_empty() { |
204 | 0 | Ok(res) |
205 | | } else { |
206 | 0 | Err(Box::new(panics)) |
207 | | } |
208 | | } |
209 | | } |
210 | 0 | } |
211 | | |
212 | | /// A scope for spawning threads. |
213 | | pub struct Scope<'env> { |
214 | | /// The list of the thread join handles. |
215 | | handles: SharedVec<SharedOption<thread::JoinHandle<()>>>, |
216 | | |
217 | | /// Used to wait until all subscopes all dropped. |
218 | | wait_group: WaitGroup, |
219 | | |
220 | | /// Borrows data with invariant lifetime `'env`. |
221 | | _marker: PhantomData<&'env mut &'env ()>, |
222 | | } |
223 | | |
224 | | unsafe impl Sync for Scope<'_> {} |
225 | | |
226 | | impl<'env> Scope<'env> { |
227 | | /// Spawns a scoped thread. |
228 | | /// |
229 | | /// This method is similar to the [`spawn`] function in Rust's standard library. The difference |
230 | | /// is that this thread is scoped, meaning it's guaranteed to terminate before the scope exits, |
231 | | /// allowing it to reference variables outside the scope. |
232 | | /// |
233 | | /// The scoped thread is passed a reference to this scope as an argument, which can be used for |
234 | | /// spawning nested threads. |
235 | | /// |
236 | | /// The returned [handle](ScopedJoinHandle) can be used to manually |
237 | | /// [join](ScopedJoinHandle::join) the thread before the scope exits. |
238 | | /// |
239 | | /// This will create a thread using default parameters of [`ScopedThreadBuilder`], if you want to specify the |
240 | | /// stack size or the name of the thread, use this API instead. |
241 | | /// |
242 | | /// [`spawn`]: std::thread::spawn |
243 | | /// |
244 | | /// # Panics |
245 | | /// |
246 | | /// Panics if the OS fails to create a thread; use [`ScopedThreadBuilder::spawn`] |
247 | | /// to recover from such errors. |
248 | | /// |
249 | | /// # Examples |
250 | | /// |
251 | | /// ``` |
252 | | /// use crossbeam_utils::thread; |
253 | | /// |
254 | | /// thread::scope(|s| { |
255 | | /// let handle = s.spawn(|_| { |
256 | | /// println!("A child thread is running"); |
257 | | /// 42 |
258 | | /// }); |
259 | | /// |
260 | | /// // Join the thread and retrieve its result. |
261 | | /// let res = handle.join().unwrap(); |
262 | | /// assert_eq!(res, 42); |
263 | | /// }).unwrap(); |
264 | | /// ``` |
265 | 0 | pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> |
266 | 0 | where |
267 | 0 | F: FnOnce(&Scope<'env>) -> T, |
268 | 0 | F: Send + 'env, |
269 | 0 | T: Send + 'env, |
270 | | { |
271 | 0 | self.builder() |
272 | 0 | .spawn(f) |
273 | 0 | .expect("failed to spawn scoped thread") |
274 | 0 | } |
275 | | |
276 | | /// Creates a builder that can configure a thread before spawning. |
277 | | /// |
278 | | /// # Examples |
279 | | /// |
280 | | /// ``` |
281 | | /// use crossbeam_utils::thread; |
282 | | /// |
283 | | /// thread::scope(|s| { |
284 | | /// s.builder() |
285 | | /// .spawn(|_| println!("A child thread is running")) |
286 | | /// .unwrap(); |
287 | | /// }).unwrap(); |
288 | | /// ``` |
289 | 0 | pub fn builder<'scope>(&'scope self) -> ScopedThreadBuilder<'scope, 'env> { |
290 | 0 | ScopedThreadBuilder { |
291 | 0 | scope: self, |
292 | 0 | builder: thread::Builder::new(), |
293 | 0 | } |
294 | 0 | } |
295 | | } |
296 | | |
297 | | impl fmt::Debug for Scope<'_> { |
298 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
299 | 0 | f.pad("Scope { .. }") |
300 | 0 | } |
301 | | } |
302 | | |
303 | | /// Configures the properties of a new thread. |
304 | | /// |
305 | | /// The two configurable properties are: |
306 | | /// |
307 | | /// - [`name`]: Specifies an [associated name for the thread][naming-threads]. |
308 | | /// - [`stack_size`]: Specifies the [desired stack size for the thread][stack-size]. |
309 | | /// |
310 | | /// The [`spawn`] method will take ownership of the builder and return an [`io::Result`] of the |
311 | | /// thread handle with the given configuration. |
312 | | /// |
313 | | /// The [`Scope::spawn`] method uses a builder with default configuration and unwraps its return |
314 | | /// value. You may want to use this builder when you want to recover from a failure to launch a |
315 | | /// thread. |
316 | | /// |
317 | | /// # Examples |
318 | | /// |
319 | | /// ``` |
320 | | /// use crossbeam_utils::thread; |
321 | | /// |
322 | | /// thread::scope(|s| { |
323 | | /// s.builder() |
324 | | /// .spawn(|_| println!("Running a child thread")) |
325 | | /// .unwrap(); |
326 | | /// }).unwrap(); |
327 | | /// ``` |
328 | | /// |
329 | | /// [`name`]: ScopedThreadBuilder::name |
330 | | /// [`stack_size`]: ScopedThreadBuilder::stack_size |
331 | | /// [`spawn`]: ScopedThreadBuilder::spawn |
332 | | /// [`io::Result`]: std::io::Result |
333 | | /// [naming-threads]: std::thread#naming-threads |
334 | | /// [stack-size]: std::thread#stack-size |
335 | | #[derive(Debug)] |
336 | | pub struct ScopedThreadBuilder<'scope, 'env> { |
337 | | scope: &'scope Scope<'env>, |
338 | | builder: thread::Builder, |
339 | | } |
340 | | |
341 | | impl<'scope, 'env> ScopedThreadBuilder<'scope, 'env> { |
342 | | /// Sets the name for the new thread. |
343 | | /// |
344 | | /// The name must not contain null bytes (`\0`). |
345 | | /// |
346 | | /// For more information about named threads, see [here][naming-threads]. |
347 | | /// |
348 | | /// # Examples |
349 | | /// |
350 | | /// ``` |
351 | | /// use crossbeam_utils::thread; |
352 | | /// use std::thread::current; |
353 | | /// |
354 | | /// thread::scope(|s| { |
355 | | /// s.builder() |
356 | | /// .name("my thread".to_string()) |
357 | | /// .spawn(|_| assert_eq!(current().name(), Some("my thread"))) |
358 | | /// .unwrap(); |
359 | | /// }).unwrap(); |
360 | | /// ``` |
361 | | /// |
362 | | /// [naming-threads]: std::thread#naming-threads |
363 | 0 | pub fn name(mut self, name: String) -> ScopedThreadBuilder<'scope, 'env> { |
364 | 0 | self.builder = self.builder.name(name); |
365 | 0 | self |
366 | 0 | } |
367 | | |
368 | | /// Sets the size of the stack for the new thread. |
369 | | /// |
370 | | /// The stack size is measured in bytes. |
371 | | /// |
372 | | /// For more information about the stack size for threads, see [here][stack-size]. |
373 | | /// |
374 | | /// # Examples |
375 | | /// |
376 | | /// ``` |
377 | | /// use crossbeam_utils::thread; |
378 | | /// |
379 | | /// thread::scope(|s| { |
380 | | /// s.builder() |
381 | | /// .stack_size(32 * 1024) |
382 | | /// .spawn(|_| println!("Running a child thread")) |
383 | | /// .unwrap(); |
384 | | /// }).unwrap(); |
385 | | /// ``` |
386 | | /// |
387 | | /// [stack-size]: std::thread#stack-size |
388 | 0 | pub fn stack_size(mut self, size: usize) -> ScopedThreadBuilder<'scope, 'env> { |
389 | 0 | self.builder = self.builder.stack_size(size); |
390 | 0 | self |
391 | 0 | } |
392 | | |
393 | | /// Spawns a scoped thread with this configuration. |
394 | | /// |
395 | | /// The scoped thread is passed a reference to this scope as an argument, which can be used for |
396 | | /// spawning nested threads. |
397 | | /// |
398 | | /// The returned handle can be used to manually join the thread before the scope exits. |
399 | | /// |
400 | | /// # Errors |
401 | | /// |
402 | | /// Unlike the [`Scope::spawn`] method, this method yields an |
403 | | /// [`io::Result`] to capture any failure to create the thread at |
404 | | /// the OS level. |
405 | | /// |
406 | | /// [`io::Result`]: std::io::Result |
407 | | /// |
408 | | /// # Panics |
409 | | /// |
410 | | /// Panics if a thread name was set and it contained null bytes. |
411 | | /// |
412 | | /// # Examples |
413 | | /// |
414 | | /// ``` |
415 | | /// use crossbeam_utils::thread; |
416 | | /// |
417 | | /// thread::scope(|s| { |
418 | | /// let handle = s.builder() |
419 | | /// .spawn(|_| { |
420 | | /// println!("A child thread is running"); |
421 | | /// 42 |
422 | | /// }) |
423 | | /// .unwrap(); |
424 | | /// |
425 | | /// // Join the thread and retrieve its result. |
426 | | /// let res = handle.join().unwrap(); |
427 | | /// assert_eq!(res, 42); |
428 | | /// }).unwrap(); |
429 | | /// ``` |
430 | 0 | pub fn spawn<F, T>(self, f: F) -> io::Result<ScopedJoinHandle<'scope, T>> |
431 | 0 | where |
432 | 0 | F: FnOnce(&Scope<'env>) -> T, |
433 | 0 | F: Send + 'env, |
434 | 0 | T: Send + 'env, |
435 | | { |
436 | | // The result of `f` will be stored here. |
437 | 0 | let result = SharedOption::default(); |
438 | | |
439 | | // Spawn the thread and grab its join handle and thread handle. |
440 | 0 | let (handle, thread) = { |
441 | 0 | let result = Arc::clone(&result); |
442 | | |
443 | | // A clone of the scope that will be moved into the new thread. |
444 | 0 | let scope = Scope::<'env> { |
445 | 0 | handles: Arc::clone(&self.scope.handles), |
446 | 0 | wait_group: self.scope.wait_group.clone(), |
447 | 0 | _marker: PhantomData, |
448 | 0 | }; |
449 | | |
450 | | // Spawn the thread. |
451 | 0 | let handle = { |
452 | 0 | let closure = move || { |
453 | | // Make sure the scope is inside the closure with the proper `'env` lifetime. |
454 | 0 | let scope: Scope<'env> = scope; |
455 | | |
456 | | // Run the closure. |
457 | 0 | let res = f(&scope); |
458 | | |
459 | | // Store the result if the closure didn't panic. |
460 | 0 | *result.lock().unwrap() = Some(res); |
461 | 0 | }; |
462 | | |
463 | | // Allocate `closure` on the heap and erase the `'env` bound. |
464 | 0 | let closure: Box<dyn FnOnce() + Send + 'env> = Box::new(closure); |
465 | 0 | let closure: Box<dyn FnOnce() + Send + 'static> = |
466 | 0 | unsafe { mem::transmute(closure) }; |
467 | | |
468 | | // Finally, spawn the closure. |
469 | 0 | self.builder.spawn(closure)? |
470 | | }; |
471 | | |
472 | 0 | let thread = handle.thread().clone(); |
473 | 0 | let handle = Arc::new(Mutex::new(Some(handle))); |
474 | 0 | (handle, thread) |
475 | | }; |
476 | | |
477 | | // Add the handle to the shared list of join handles. |
478 | 0 | self.scope.handles.lock().unwrap().push(Arc::clone(&handle)); |
479 | | |
480 | 0 | Ok(ScopedJoinHandle { |
481 | 0 | handle, |
482 | 0 | result, |
483 | 0 | thread, |
484 | 0 | _marker: PhantomData, |
485 | 0 | }) |
486 | 0 | } |
487 | | } |
488 | | |
489 | | unsafe impl<T> Send for ScopedJoinHandle<'_, T> {} |
490 | | unsafe impl<T> Sync for ScopedJoinHandle<'_, T> {} |
491 | | |
492 | | /// A handle that can be used to join its scoped thread. |
493 | | /// |
494 | | /// This struct is created by the [`Scope::spawn`] method and the |
495 | | /// [`ScopedThreadBuilder::spawn`] method. |
496 | | pub struct ScopedJoinHandle<'scope, T> { |
497 | | /// A join handle to the spawned thread. |
498 | | handle: SharedOption<thread::JoinHandle<()>>, |
499 | | |
500 | | /// Holds the result of the inner closure. |
501 | | result: SharedOption<T>, |
502 | | |
503 | | /// A handle to the spawned thread. |
504 | | thread: thread::Thread, |
505 | | |
506 | | /// Borrows the parent scope with lifetime `'scope`. |
507 | | _marker: PhantomData<&'scope ()>, |
508 | | } |
509 | | |
510 | | impl<T> ScopedJoinHandle<'_, T> { |
511 | | /// Waits for the thread to finish and returns its result. |
512 | | /// |
513 | | /// If the child thread panics, an error is returned. Note that if panics are implemented by |
514 | | /// aborting the process, no error is returned; see the notes of [std::panic::catch_unwind]. |
515 | | /// |
516 | | /// # Panics |
517 | | /// |
518 | | /// This function may panic on some platforms if a thread attempts to join itself or otherwise |
519 | | /// may create a deadlock with joining threads. |
520 | | /// |
521 | | /// # Examples |
522 | | /// |
523 | | /// ``` |
524 | | /// use crossbeam_utils::thread; |
525 | | /// |
526 | | /// thread::scope(|s| { |
527 | | /// let handle1 = s.spawn(|_| println!("I'm a happy thread :)")); |
528 | | /// let handle2 = s.spawn(|_| panic!("I'm a sad thread :(")); |
529 | | /// |
530 | | /// // Join the first thread and verify that it succeeded. |
531 | | /// let res = handle1.join(); |
532 | | /// assert!(res.is_ok()); |
533 | | /// |
534 | | /// // Join the second thread and verify that it panicked. |
535 | | /// let res = handle2.join(); |
536 | | /// assert!(res.is_err()); |
537 | | /// }).unwrap(); |
538 | | /// ``` |
539 | 0 | pub fn join(self) -> thread::Result<T> { |
540 | | // Take out the handle. The handle will surely be available because the root scope waits |
541 | | // for nested scopes before joining remaining threads. |
542 | 0 | let handle = self.handle.lock().unwrap().take().unwrap(); |
543 | | |
544 | | // Join the thread and then take the result out of its inner closure. |
545 | 0 | handle |
546 | 0 | .join() |
547 | 0 | .map(|()| self.result.lock().unwrap().take().unwrap()) |
548 | 0 | } |
549 | | |
550 | | /// Returns a handle to the underlying thread. |
551 | | /// |
552 | | /// # Examples |
553 | | /// |
554 | | /// ``` |
555 | | /// use crossbeam_utils::thread; |
556 | | /// |
557 | | /// thread::scope(|s| { |
558 | | /// let handle = s.spawn(|_| println!("A child thread is running")); |
559 | | /// println!("The child thread ID: {:?}", handle.thread().id()); |
560 | | /// }).unwrap(); |
561 | | /// ``` |
562 | 0 | pub fn thread(&self) -> &thread::Thread { |
563 | 0 | &self.thread |
564 | 0 | } |
565 | | } |
566 | | |
567 | | /// Unix-specific extensions. |
568 | | #[cfg(unix)] |
569 | | mod unix { |
570 | | use super::ScopedJoinHandle; |
571 | | use std::os::unix::thread::{JoinHandleExt, RawPthread}; |
572 | | |
573 | | impl<T> JoinHandleExt for ScopedJoinHandle<'_, T> { |
574 | 0 | fn as_pthread_t(&self) -> RawPthread { |
575 | | // Borrow the handle. The handle will surely be available because the root scope waits |
576 | | // for nested scopes before joining remaining threads. |
577 | 0 | let handle = self.handle.lock().unwrap(); |
578 | 0 | handle.as_ref().unwrap().as_pthread_t() |
579 | 0 | } |
580 | 0 | fn into_pthread_t(self) -> RawPthread { |
581 | 0 | self.as_pthread_t() |
582 | 0 | } |
583 | | } |
584 | | } |
585 | | /// Windows-specific extensions. |
586 | | #[cfg(windows)] |
587 | | mod windows { |
588 | | use super::ScopedJoinHandle; |
589 | | use std::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle}; |
590 | | |
591 | | impl<T> AsRawHandle for ScopedJoinHandle<'_, T> { |
592 | | fn as_raw_handle(&self) -> RawHandle { |
593 | | // Borrow the handle. The handle will surely be available because the root scope waits |
594 | | // for nested scopes before joining remaining threads. |
595 | | let handle = self.handle.lock().unwrap(); |
596 | | handle.as_ref().unwrap().as_raw_handle() |
597 | | } |
598 | | } |
599 | | |
600 | | impl<T> IntoRawHandle for ScopedJoinHandle<'_, T> { |
601 | | fn into_raw_handle(self) -> RawHandle { |
602 | | self.as_raw_handle() |
603 | | } |
604 | | } |
605 | | } |
606 | | |
607 | | impl<T> fmt::Debug for ScopedJoinHandle<'_, T> { |
608 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
609 | 0 | f.pad("ScopedJoinHandle { .. }") |
610 | 0 | } |
611 | | } |