More correct scope drop behavior

now no longer aborts if a Drop impl panics
This commit is contained in:
kyren 2018-02-07 16:42:03 -05:00
parent ab9841a02f
commit 98ee4e9492
2 changed files with 22 additions and 22 deletions

View File

@ -3,11 +3,10 @@ use std::sync::{Arc, Mutex};
use std::ops::DerefMut; use std::ops::DerefMut;
use std::cell::RefCell; use std::cell::RefCell;
use std::ffi::CString; use std::ffi::CString;
use std::any::TypeId; use std::any::{Any, TypeId};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, AssertUnwindSafe};
use libc; use libc;
@ -33,7 +32,7 @@ pub struct Lua {
/// !Send, and callbacks that are !Send and not 'static. /// !Send, and callbacks that are !Send and not 'static.
pub struct Scope<'lua> { pub struct Scope<'lua> {
lua: &'lua Lua, lua: &'lua Lua,
destructors: RefCell<Vec<Box<FnMut(*mut ffi::lua_State)>>>, destructors: RefCell<Vec<Box<FnMut(*mut ffi::lua_State) -> Box<Any>>>>,
} }
// Data associated with the main lua_State via lua_getextraspace. // Data associated with the main lua_State via lua_getextraspace.
@ -1060,12 +1059,13 @@ impl<'lua> Scope<'lua> {
registry_id as ffi::lua_Integer, registry_id as ffi::lua_Integer,
); );
ffi::lua_getupvalue(state, -1, 1); ffi::lua_getupvalue(state, -1, 1);
destruct_userdata::<RefCell<Callback>>(state); let ud = take_userdata::<RefCell<Callback>>(state);
ffi::lua_pushnil(state); ffi::lua_pushnil(state);
ffi::lua_setupvalue(state, -2, 1); ffi::lua_setupvalue(state, -2, 1);
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
Box::new(ud)
})); }));
Ok(f) Ok(f)
} }
@ -1087,7 +1087,7 @@ impl<'lua> Scope<'lua> {
ffi::LUA_REGISTRYINDEX, ffi::LUA_REGISTRYINDEX,
registry_id as ffi::lua_Integer, registry_id as ffi::lua_Integer,
); );
destruct_userdata::<RefCell<T>>(state); Box::new(take_userdata::<RefCell<T>>(state))
})); }));
Ok(u) Ok(u)
} }
@ -1096,15 +1096,14 @@ impl<'lua> Scope<'lua> {
impl<'lua> Drop for Scope<'lua> { impl<'lua> Drop for Scope<'lua> {
fn drop(&mut self) { fn drop(&mut self) {
// We separate the action of invalidating the userdata in Lua and actually dropping the
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
// can be sure that all of the userdata in Lua is actually invalidated.
let state = self.lua.state; let state = self.lua.state;
let mut drops = Vec::new();
for mut destructor in self.destructors.get_mut().drain(..) { for mut destructor in self.destructors.get_mut().drain(..) {
match catch_unwind(AssertUnwindSafe(move || destructor(state))) { drops.push(destructor(state));
Ok(_) => {}
Err(_) => {
eprintln!("Scope userdata Drop impl has panicked, aborting!");
process::abort()
}
}
} }
} }
} }

View File

@ -248,15 +248,9 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
ud ud
} }
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int { // Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
callback_error(state, || { // userdata.
destruct_userdata::<T>(state); pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
Ok(0)
})
}
// Pops the userdata off of the top of the stack and drops it
pub unsafe fn destruct_userdata<T>(state: *mut ffi::lua_State) {
// We set the metatable of userdata on __gc to a special table with no __gc method and with // We set the metatable of userdata on __gc to a special table with no __gc method and with
// metamethods that trigger an error on access. We do this so that it will not be double // metamethods that trigger an error on access. We do this so that it will not be double
// dropped, and also so that it cannot be used or identified as any particular userdata type // dropped, and also so that it cannot be used or identified as any particular userdata type
@ -265,7 +259,14 @@ pub unsafe fn destruct_userdata<T>(state: *mut ffi::lua_State) {
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
let ud = &mut *(ffi::lua_touserdata(state, -1) as *mut T); let ud = &mut *(ffi::lua_touserdata(state, -1) as *mut T);
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
mem::replace(ud, mem::uninitialized()); mem::replace(ud, mem::uninitialized())
}
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || {
take_userdata::<T>(state);
Ok(0)
})
} }
// In the context of a lua callback, this will call the given function and if the given function // In the context of a lua callback, this will call the given function and if the given function