@@ -25,9 +25,10 @@ use cell::RefCell;
2525use fmt;
2626use intrinsics;
2727use mem;
28+ use ptr;
2829use raw;
29- use sys_common:: rwlock:: RWLock ;
3030use sys:: stdio:: Stderr ;
31+ use sys_common:: rwlock:: RWLock ;
3132use sys_common:: thread_info;
3233use sys_common:: util;
3334use thread;
@@ -255,45 +256,76 @@ pub use realstd::rt::update_panic_count;
255256
256257/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
257258pub unsafe fn try < R , F : FnOnce ( ) -> R > ( f : F ) -> Result < R , Box < Any + Send > > {
258- let mut slot = None ;
259- let mut f = Some ( f) ;
260- let ret;
261-
262- {
263- let mut to_run = || {
264- slot = Some ( f. take ( ) . unwrap ( ) ( ) ) ;
265- } ;
266- let fnptr = get_call ( & mut to_run) ;
267- let dataptr = & mut to_run as * mut _ as * mut u8 ;
268- let mut any_data = 0 ;
269- let mut any_vtable = 0 ;
270- let fnptr = mem:: transmute :: < fn ( & mut _) , fn ( * mut u8 ) > ( fnptr) ;
271- let r = __rust_maybe_catch_panic ( fnptr,
272- dataptr,
273- & mut any_data,
274- & mut any_vtable) ;
275- if r == 0 {
276- ret = Ok ( ( ) ) ;
277- } else {
278- update_panic_count ( -1 ) ;
279- ret = Err ( mem:: transmute ( raw:: TraitObject {
280- data : any_data as * mut _ ,
281- vtable : any_vtable as * mut _ ,
282- } ) ) ;
283- }
259+ struct Data < F , R > {
260+ f : F ,
261+ r : R ,
284262 }
285263
286- debug_assert ! ( update_panic_count( 0 ) == 0 ) ;
287- return ret. map ( |( ) | {
288- slot. take ( ) . unwrap ( )
289- } ) ;
264+ // We do some sketchy operations with ownership here for the sake of
265+ // performance. The `Data` structure is never actually fully valid, but
266+ // instead it always contains at least one uninitialized field. We can only
267+ // pass pointers down to `__rust_maybe_catch_panic` (can't pass objects by
268+ // value), so we do all the ownership tracking here manully.
269+ //
270+ // Note that this is all invalid if any of these functions unwind, but the
271+ // whole point of this function is to prevent that! As a result we go
272+ // through a transition where:
273+ //
274+ // * First, only the closure we're going to call is initialized. The return
275+ // value is uninitialized.
276+ // * When we make the function call, the `do_call` function below, we take
277+ // ownership of the function pointer, replacing it with uninitialized
278+ // data. At this point the `Data` structure is entirely uninitialized, but
279+ // it won't drop due to an unwind because it's owned on the other side of
280+ // the catch panic.
281+ // * If the closure successfully returns, we write the return value into the
282+ // data's return slot. Note that `ptr::write` is used as it's overwriting
283+ // uninitialized data.
284+ // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
285+ // in one of two states:
286+ //
287+ // 1. The closure didn't panic, in which case the return value was
288+ // filled in. We have to be careful to `forget` the closure,
289+ // however, as ownership was passed to the `do_call` function.
290+ // 2. The closure panicked, in which case the return value wasn't
291+ // filled in. In this case the entire `data` structure is invalid,
292+ // so we forget the entire thing.
293+ //
294+ // Once we stack all that together we should have the "most efficient'
295+ // method of calling a catch panic whilst juggling ownership.
296+ let mut any_data = 0 ;
297+ let mut any_vtable = 0 ;
298+ let mut data = Data {
299+ f : f,
300+ r : mem:: uninitialized ( ) ,
301+ } ;
290302
291- fn get_call < F : FnMut ( ) > ( _: & mut F ) -> fn ( & mut F ) {
292- call
293- }
303+ let r = __rust_maybe_catch_panic ( do_call :: < F , R > ,
304+ & mut data as * mut _ as * mut u8 ,
305+ & mut any_data,
306+ & mut any_vtable) ;
307+
308+ return if r == 0 {
309+ let Data { f, r } = data;
310+ mem:: forget ( f) ;
311+ debug_assert ! ( update_panic_count( 0 ) == 0 ) ;
312+ Ok ( r)
313+ } else {
314+ mem:: forget ( data) ;
315+ update_panic_count ( -1 ) ;
316+ debug_assert ! ( update_panic_count( 0 ) == 0 ) ;
317+ Err ( mem:: transmute ( raw:: TraitObject {
318+ data : any_data as * mut _ ,
319+ vtable : any_vtable as * mut _ ,
320+ } ) )
321+ } ;
294322
295- fn call < F : FnMut ( ) > ( f : & mut F ) {
296- f ( )
323+ fn do_call < F : FnOnce ( ) -> R , R > ( data : * mut u8 ) {
324+ unsafe {
325+ let data = data as * mut Data < F , R > ;
326+ let f = ptr:: read ( & mut ( * data) . f ) ;
327+ ptr:: write ( & mut ( * data) . r , f ( ) ) ;
328+ }
297329 }
298330}
299331
0 commit comments