Fix potential deadlock when trying to reuse dropped RegistryKey.

If no free registry id found, we call protect_lua! macro while keeping mutex guard to the unref list.
Protected calls can trigger garbage collection and if RegistryKey is placed in userdata being collected, this can lead to deadlock.
The solution is drop mutex guard as soon as possible.
Also this commit includes optimization in creating reference in Lua registry.
This commit is contained in:
Alex Orlenko 2023-02-22 20:15:40 +00:00
parent 94f01e597c
commit 949906f9f7
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
1 changed files with 11 additions and 6 deletions

View File

@ -2065,22 +2065,27 @@ impl Lua {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let unref_list = (*self.extra.get()).registry_unref_list.clone();
self.push_value(t)?;
// Try to reuse previously allocated slot
let unref_list2 = unref_list.clone();
let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned");
if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) {
let unref_list = (*self.extra.get()).registry_unref_list.clone();
let free_registry_id = mlua_expect!(unref_list.lock(), "unref list poisoned")
.as_mut()
.and_then(|x| x.pop());
if let Some(registry_id) = free_registry_id {
// It must be safe to replace the value without triggering memory error
ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
return Ok(RegistryKey::new(registry_id, unref_list));
}
// Allocate a new RegistryKey
let registry_id = protect_lua!(state, 1, 0, |state| {
let registry_id = if self.unlikely_memory_error() {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
} else {
protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?
};
Ok(RegistryKey::new(registry_id, unref_list))
}
}