cogo/
scoped.rs

1// modified from crossbeam
2
3use std::cell::RefCell;
4use std::fmt;
5use std::mem;
6use std::panic;
7use std::rc::Rc;
8use std::sync::Arc;
9use std::thread;
10
11use crate::coroutine_impl::{spawn, Coroutine};
12use crate::join::JoinHandle;
13use crossbeam::atomic::AtomicCell;
14
15/// Like `coroutine::spawn`, but without the closure bounds.
16pub unsafe fn spawn_unsafe<'a, F>(f: F) -> JoinHandle<()>
17    where
18        F: FnOnce() + Send + 'a,
19{
20    let closure: Box<dyn FnOnce() + 'a> = Box::new(f);
21    let closure: Box<dyn FnOnce() + Send> = mem::transmute(closure);
22    spawn(move || closure())
23}
24
25pub struct Scope<'a> {
26    dtors: RefCell<Option<DtorChain<'a>>>,
27}
28
29struct DtorChain<'a> {
30    dtor: Box<dyn FnOnce() + 'a>,
31    next: Option<Box<DtorChain<'a>>>,
32}
33
34enum JoinState {
35    Running(JoinHandle<()>),
36    Joined,
37}
38
39impl JoinState {
40    fn join(&mut self) {
41        let mut state = JoinState::Joined;
42        mem::swap(self, &mut state);
43        if let JoinState::Running(handle) = state {
44            let res = handle.join();
45
46            // TODO: when panic happened, the logic need to refine
47            if !thread::panicking() {
48                res.unwrap_or_else(|e| panic::resume_unwind(e));
49            }
50        }
51    }
52}
53
54/// A handle to a scoped coroutine
55pub struct ScopedJoinHandle<T> {
56    inner: Rc<RefCell<JoinState>>,
57    packet: Arc<AtomicCell<Option<T>>>,
58    co: Coroutine,
59}
60
61/// Create a new `scope`, for deferred destructors.
62///
63/// Scopes, in particular, support scoped coroutine spawning.
64///
65pub fn scope<'a, F, R>(f: F) -> R
66    where
67        F: FnOnce(&Scope<'a>) -> R,
68{
69    let mut scope = Scope {
70        dtors: RefCell::new(None),
71    };
72    let ret = f(&scope);
73    scope.drop_all();
74    ret
75}
76
77impl<'a> fmt::Debug for Scope<'a> {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        write!(f, "Scope {{ ... }}")
80    }
81}
82
83impl<T> fmt::Debug for ScopedJoinHandle<T> {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        write!(f, "ScopedJoinHandle {{ ... }}")
86    }
87}
88
89impl<'a> Scope<'a> {
90    // This method is carefully written in a transactional style, so
91    // that it can be called directly and, if any dtor panics, can be
92    // resumed in the unwinding this causes. By initially running the
93    // method outside of any destructor, we avoid any leakage problems
94    // due to @rust-lang/rust#14875.
95    fn drop_all(&mut self) {
96        loop {
97            // use a separate scope to ensure that the RefCell borrow
98            // is relinquished before running `dtor`
99            let dtor = {
100                let mut dtors = self.dtors.borrow_mut();
101                if let Some(mut node) = dtors.take() {
102                    *dtors = node.next.take().map(|b| *b);
103                    node.dtor
104                } else {
105                    return;
106                }
107            };
108            dtor();
109        }
110    }
111
112    /// Schedule code to be executed when exiting the scope.
113    ///
114    /// This is akin to having a destructor on the stack, except that it is
115    /// *guaranteed* to be run.
116    pub fn defer<F>(&self, f: F)
117        where
118            F: FnOnce() + 'a,
119    {
120        let mut dtors = self.dtors.borrow_mut();
121        *dtors = Some(DtorChain {
122            dtor: Box::new(f),
123            next: dtors.take().map(Box::new),
124        });
125    }
126
127    /// Create a scoped coroutine.
128    ///
129    /// `spawn` is similar to the `spawn` function in this library. The
130    /// difference is that this coroutine is scoped, meaning that it's guaranteed to terminate
131    /// before the current stack frame goes away, allowing you to reference the parent stack frame
132    /// directly. This is ensured by having the parent join on the child coroutine before the
133    /// scope exits.
134    fn spawn_impl<F, T>(&self, f: F) -> ScopedJoinHandle<T>
135        where
136            F: FnOnce() -> T + Send + 'a,
137            T: Send + 'a,
138    {
139        let their_packet = Arc::new(AtomicCell::new(None));
140        let my_packet = their_packet.clone();
141
142        let join_handle = unsafe {
143            spawn_unsafe(move || {
144                their_packet.swap(Some(f()));
145            })
146        };
147
148        let co = join_handle.coroutine().clone();
149        let deferred_handle = Rc::new(RefCell::new(JoinState::Running(join_handle)));
150        let my_handle = deferred_handle.clone();
151
152        self.defer(move || {
153            let mut state = deferred_handle.borrow_mut();
154            state.join();
155        });
156
157        ScopedJoinHandle {
158            inner: my_handle,
159            packet: my_packet,
160            co,
161        }
162    }
163
164    /// Create a scoped coroutine.
165    ///
166    /// `spawn` is similar to the `spawn` function in this library. The
167    /// difference is that this coroutine is scoped, meaning that it's guaranteed to terminate
168    /// before the current stack frame goes away, allowing you to reference the parent stack frame
169    /// directly. This is ensured by having the parent join on the child coroutine before the
170    /// scope exits.
171    pub unsafe fn spawn<F, T>(&self, f: F) -> ScopedJoinHandle<T>
172        where
173            F: FnOnce() -> T + Send + 'a,
174            T: Send + 'a,
175    {
176        self.spawn_impl(f)
177    }
178}
179
180impl<T> ScopedJoinHandle<T> {
181    /// Join the scoped coroutine, returning the result it produced.
182    pub fn join(self) -> T {
183        self.inner.borrow_mut().join();
184        self.packet.take().unwrap()
185    }
186
187    /// Get the underlying coroutine handle.
188    pub fn coroutine(&self) -> &Coroutine {
189        &self.co
190    }
191}
192
193impl<'a> Drop for Scope<'a> {
194    fn drop(&mut self) {
195        self.drop_all()
196    }
197}