Skip to content

Commit cea2d7f

Browse files
committed
Refactor application data container.
Now it's allowed at the same time mutably and immutably borrow different types. Each value in the application data container is stored in it's own `RefCell` wrapper. Also added new function `Lua::try_set_app_data()`.
1 parent e0224ab commit cea2d7f

File tree

5 files changed

+219
-54
lines changed

5 files changed

+219
-54
lines changed

src/chunk.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
454454
} else {
455455
let mut cache = ChunksCache(HashMap::new());
456456
cache.0.insert(text_source, binary_source.as_ref().to_vec());
457-
self.lua.set_app_data(cache);
457+
let _ = self.lua.try_set_app_data(cache);
458458
}
459459
}
460460
}

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub use crate::stdlib::StdLib;
116116
pub use crate::string::String;
117117
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
118118
pub use crate::thread::{Thread, ThreadStatus};
119-
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
119+
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
120120
pub use crate::userdata::{
121121
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
122122
UserDataRef, UserDataRefMut,

src/lua.rs

+31-43
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use std::any::{Any, TypeId};
2-
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
1+
use std::any::TypeId;
2+
use std::cell::{RefCell, UnsafeCell};
33
use std::ffi::{CStr, CString};
44
use std::fmt;
55
use std::marker::PhantomData;
@@ -8,6 +8,7 @@ use std::ops::Deref;
88
use std::os::raw::{c_char, c_int, c_void};
99
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location};
1010
use std::ptr::NonNull;
11+
use std::result::Result as StdResult;
1112
use std::sync::atomic::{AtomicPtr, Ordering};
1213
use std::sync::{Arc, Mutex};
1314
use std::{mem, ptr, str};
@@ -25,8 +26,8 @@ use crate::string::String;
2526
use crate::table::Table;
2627
use crate::thread::Thread;
2728
use crate::types::{
28-
Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, LuaRef, MaybeSend,
29-
Number, RegistryKey,
29+
AppData, AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer,
30+
LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
3031
};
3132
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataCell};
3233
use crate::userdata_impl::{UserDataProxy, UserDataRegistrar};
@@ -85,10 +86,8 @@ pub(crate) struct ExtraData {
8586
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
8687
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
8788

88-
#[cfg(not(feature = "send"))]
89-
app_data: RefCell<FxHashMap<TypeId, Box<dyn Any>>>,
90-
#[cfg(feature = "send")]
91-
app_data: RefCell<FxHashMap<TypeId, Box<dyn Any + Send>>>,
89+
// Container to store arbitrary data (extensions)
90+
app_data: AppData,
9291

9392
safe: bool,
9493
libs: StdLib,
@@ -508,7 +507,7 @@ impl Lua {
508507
registered_userdata_mt: FxHashMap::default(),
509508
last_checked_userdata_mt: (ptr::null(), None),
510509
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
511-
app_data: RefCell::new(FxHashMap::default()),
510+
app_data: AppData::default(),
512511
safe: false,
513512
libs: StdLib::NONE,
514513
mem_state: None,
@@ -2222,51 +2221,45 @@ impl Lua {
22222221
/// }
22232222
/// ```
22242223
#[track_caller]
2225-
pub fn set_app_data<T: 'static + MaybeSend>(&self, data: T) -> Option<T> {
2224+
pub fn set_app_data<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
2225+
let extra = unsafe { &*self.extra.get() };
2226+
extra.app_data.insert(data)
2227+
}
2228+
2229+
/// Tries to set or replace an application data object of type `T`.
2230+
///
2231+
/// Returns:
2232+
/// - `Ok(Some(old_data))` if the data object of type `T` was successfully replaced.
2233+
/// - `Ok(None)` if the data object of type `T` was successfully inserted.
2234+
/// - `Err(data)` if the data object of type `T` was not inserted because the container is currently borrowed.
2235+
///
2236+
/// See [`Lua::set_app_data()`] for examples.
2237+
pub fn try_set_app_data<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
22262238
let extra = unsafe { &*self.extra.get() };
2227-
extra
2228-
.app_data
2229-
.try_borrow_mut()
2230-
.expect("cannot borrow mutably app data container")
2231-
.insert(TypeId::of::<T>(), Box::new(data))
2232-
.and_then(|data| data.downcast::<T>().ok().map(|data| *data))
2239+
extra.app_data.try_insert(data)
22332240
}
22342241

22352242
/// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
22362243
///
22372244
/// # Panics
22382245
///
2239-
/// Panics if the app data container is currently mutably borrowed. Multiple immutable reads can be
2240-
/// taken out at the same time.
2246+
/// Panics if the data object of type `T` is currently mutably borrowed. Multiple immutable reads
2247+
/// can be taken out at the same time.
22412248
#[track_caller]
2242-
pub fn app_data_ref<T: 'static>(&self) -> Option<Ref<T>> {
2249+
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
22432250
let extra = unsafe { &*self.extra.get() };
2244-
let app_data = extra
2245-
.app_data
2246-
.try_borrow()
2247-
.expect("cannot borrow app data container");
2248-
Ref::filter_map(app_data, |data| {
2249-
data.get(&TypeId::of::<T>())?.downcast_ref::<T>()
2250-
})
2251-
.ok()
2251+
extra.app_data.borrow()
22522252
}
22532253

22542254
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
22552255
///
22562256
/// # Panics
22572257
///
2258-
/// Panics if the app data container is currently borrowed.
2258+
/// Panics if the data object of type `T` is currently borrowed.
22592259
#[track_caller]
2260-
pub fn app_data_mut<T: 'static>(&self) -> Option<RefMut<T>> {
2260+
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
22612261
let extra = unsafe { &*self.extra.get() };
2262-
let app_data = extra
2263-
.app_data
2264-
.try_borrow_mut()
2265-
.expect("cannot mutably borrow app data container");
2266-
RefMut::filter_map(app_data, |data| {
2267-
data.get_mut(&TypeId::of::<T>())?.downcast_mut::<T>()
2268-
})
2269-
.ok()
2262+
extra.app_data.borrow_mut()
22702263
}
22712264

22722265
/// Removes an application data of type `T`.
@@ -2277,12 +2270,7 @@ impl Lua {
22772270
#[track_caller]
22782271
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
22792272
let extra = unsafe { &*self.extra.get() };
2280-
extra
2281-
.app_data
2282-
.try_borrow_mut()
2283-
.expect("cannot mutably borrow app data container")
2284-
.remove(&TypeId::of::<T>())
2285-
.and_then(|data| data.downcast::<T>().ok().map(|data| *data))
2273+
extra.app_data.remove()
22862274
}
22872275

22882276
// Uses 2 stack spaces, does not call checkstack

src/types.rs

+150-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
use std::cell::UnsafeCell;
1+
use std::any::{Any, TypeId};
2+
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
23
use std::hash::{Hash, Hasher};
4+
use std::ops::{Deref, DerefMut};
35
use std::os::raw::{c_int, c_void};
6+
use std::result::Result as StdResult;
47
use std::sync::atomic::{AtomicBool, Ordering};
58
use std::sync::{Arc, Mutex};
69
use std::{fmt, mem, ptr};
710

811
#[cfg(feature = "lua54")]
912
use std::ffi::CStr;
1013

14+
use rustc_hash::FxHashMap;
15+
1116
#[cfg(feature = "async")]
1217
use futures_util::future::LocalBoxFuture;
1318

@@ -286,6 +291,150 @@ impl LuaOwnedRef {
286291
}
287292
}
288293

294+
#[derive(Debug, Default)]
295+
pub(crate) struct AppData {
296+
#[cfg(not(feature = "send"))]
297+
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>,
298+
#[cfg(feature = "send")]
299+
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>,
300+
borrow: Cell<usize>,
301+
}
302+
303+
impl AppData {
304+
#[track_caller]
305+
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
306+
match self.try_insert(data) {
307+
Ok(data) => data,
308+
Err(_) => panic!("cannot mutably borrow app data container"),
309+
}
310+
}
311+
312+
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
313+
if self.borrow.get() != 0 {
314+
return Err(data);
315+
}
316+
// SAFETY: we checked that there are no other references to the container
317+
Ok(unsafe { &mut *self.container.get() }
318+
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
319+
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
320+
}
321+
322+
#[track_caller]
323+
pub(crate) fn borrow<T: 'static>(&self) -> Option<AppDataRef<T>> {
324+
let data = unsafe { &*self.container.get() }
325+
.get(&TypeId::of::<T>())?
326+
.borrow();
327+
self.borrow.set(self.borrow.get() + 1);
328+
Some(AppDataRef {
329+
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
330+
borrow: &self.borrow,
331+
})
332+
}
333+
334+
#[track_caller]
335+
pub(crate) fn borrow_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
336+
let data = unsafe { &*self.container.get() }
337+
.get(&TypeId::of::<T>())?
338+
.borrow_mut();
339+
self.borrow.set(self.borrow.get() + 1);
340+
Some(AppDataRefMut {
341+
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
342+
borrow: &self.borrow,
343+
})
344+
}
345+
346+
#[track_caller]
347+
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
348+
if self.borrow.get() != 0 {
349+
panic!("cannot mutably borrow app data container");
350+
}
351+
// SAFETY: we checked that there are no other references to the container
352+
unsafe { &mut *self.container.get() }
353+
.remove(&TypeId::of::<T>())?
354+
.into_inner()
355+
.downcast::<T>()
356+
.ok()
357+
.map(|data| *data)
358+
}
359+
}
360+
361+
/// A wrapper type for an immutably borrowed value from an app data container.
362+
///
363+
/// This type is similar to [`Ref`].
364+
pub struct AppDataRef<'a, T: ?Sized + 'a> {
365+
data: Ref<'a, T>,
366+
borrow: &'a Cell<usize>,
367+
}
368+
369+
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
370+
fn drop(&mut self) {
371+
self.borrow.set(self.borrow.get() - 1);
372+
}
373+
}
374+
375+
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
376+
type Target = T;
377+
378+
#[inline]
379+
fn deref(&self) -> &Self::Target {
380+
&self.data
381+
}
382+
}
383+
384+
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
385+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386+
(**self).fmt(f)
387+
}
388+
}
389+
390+
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
391+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392+
(**self).fmt(f)
393+
}
394+
}
395+
396+
/// A wrapper type for a mutably borrowed value from an app data container.
397+
///
398+
/// This type is similar to [`RefMut`].
399+
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
400+
data: RefMut<'a, T>,
401+
borrow: &'a Cell<usize>,
402+
}
403+
404+
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
405+
fn drop(&mut self) {
406+
self.borrow.set(self.borrow.get() - 1);
407+
}
408+
}
409+
410+
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
411+
type Target = T;
412+
413+
#[inline]
414+
fn deref(&self) -> &Self::Target {
415+
&self.data
416+
}
417+
}
418+
419+
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
420+
#[inline]
421+
fn deref_mut(&mut self) -> &mut Self::Target {
422+
&mut self.data
423+
}
424+
}
425+
426+
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
427+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428+
(**self).fmt(f)
429+
}
430+
}
431+
432+
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
433+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434+
(**self).fmt(f)
435+
}
436+
}
437+
289438
#[cfg(test)]
290439
mod assertions {
291440
use super::*;

tests/tests.rs

+36-8
Original file line numberDiff line numberDiff line change
@@ -887,19 +887,47 @@ fn test_application_data() -> Result<()> {
887887
lua.set_app_data("test1");
888888
lua.set_app_data(vec!["test2"]);
889889

890+
// Borrow &str immutably and Vec<&str> mutably
891+
let s = lua.app_data_ref::<&str>().unwrap();
892+
let mut v = lua.app_data_mut::<Vec<&str>>().unwrap();
893+
v.push("test3");
894+
895+
// Insert of new data or removal should fail now
896+
assert!(lua.try_set_app_data::<i32>(123).is_err());
897+
match catch_unwind(AssertUnwindSafe(|| lua.set_app_data::<i32>(123))) {
898+
Ok(_) => panic!("expected panic"),
899+
Err(_) => {}
900+
}
901+
match catch_unwind(AssertUnwindSafe(|| lua.remove_app_data::<i32>())) {
902+
Ok(_) => panic!("expected panic"),
903+
Err(_) => {}
904+
}
905+
906+
// Check display and debug impls
907+
assert_eq!(format!("{s}"), "test1");
908+
assert_eq!(format!("{s:?}"), "\"test1\"");
909+
910+
// Borrowing immutably and mutably of the same type is not allowed
911+
match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) {
912+
Ok(_) => panic!("expected panic"),
913+
Err(_) => {}
914+
}
915+
drop((s, v));
916+
917+
// Test that application data is accessible from anywhere
890918
let f = lua.create_function(|lua, ()| {
891-
{
892-
let data1 = lua.app_data_ref::<&str>().unwrap();
893-
assert_eq!(*data1, "test1");
894-
}
895-
let mut data2 = lua.app_data_mut::<Vec<&str>>().unwrap();
896-
assert_eq!(*data2, vec!["test2"]);
897-
data2.push("test3");
919+
let mut data1 = lua.app_data_mut::<&str>().unwrap();
920+
assert_eq!(*data1, "test1");
921+
*data1 = "test4";
922+
923+
let data2 = lua.app_data_ref::<Vec<&str>>().unwrap();
924+
assert_eq!(*data2, vec!["test2", "test3"]);
925+
898926
Ok(())
899927
})?;
900928
f.call(())?;
901929

902-
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test1");
930+
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4");
903931
assert_eq!(
904932
*lua.app_data_ref::<Vec<&str>>().unwrap(),
905933
vec!["test2", "test3"]

0 commit comments

Comments
 (0)