Skip to content

Commit f2b4080

Browse files
bors[bot]toasteaterWaridley
authored
Merge #693 #700
693: Add the MapOwned trait and the `Once<T>` userdata type r=toasteater a=toasteater This adds support for exporting methods that take ownership of `self`. `Once<T>` is a userdata wrapper that uses atomic compare-exchange to allow taking ownership safely once. 700: Add into_inner method to ArcData r=toasteater a=Waridley As discussed in Discord, this method is especially useful for coercing the `script` of an `Instance` into a trait object, thus being able to benefit from more Rust conveniences within Godot scripts. This *could* be written as an `impl<T> Into<Arc<T>> for ArcData<T>`, but I feel like this makes it more clear that it's not expected for the user to be able to freely treat `ArcData` as an `Arc` whenever they want to, but rather must be explicit about accessing the internals of this type -- even if it should theoretically be as memory-safe as using the `ArcData` anyway. I decided to have this method take ownership of `self` because if you have reference to an `ArcData`, you should still be able to call `clone()` on it to get an owned copy, but the opposite would not be as easy if the user didn't want to increment the ref count. Co-authored-by: toasteater <48371905+toasteater@users.noreply.github.com> Co-authored-by: Waridley <Waridley64@gmail.com>
3 parents 46514c7 + 3f972ff + 36199b0 commit f2b4080

File tree

7 files changed

+287
-5
lines changed

7 files changed

+287
-5
lines changed

gdnative-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ gdnative-impl-proc-macros = { path = "../impl/proc_macros", version = "=0.9.3" }
2828

2929
bitflags = { version = "1.2", optional = true }
3030
parking_lot = { version = "0.11.0", optional = true }
31+
atomic-take = "1.0.0"

gdnative-core/src/nativescript/class.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ use crate::core_types::{
44
FromVariant, FromVariantError, GodotString, OwnedToVariant, ToVariant, Variant,
55
};
66
use crate::nativescript::init::ClassBuilder;
7-
use crate::nativescript::Map;
8-
use crate::nativescript::MapMut;
9-
use crate::nativescript::UserData;
7+
use crate::nativescript::{Map, MapMut, MapOwned, UserData};
108
use crate::object::{
119
AssumeSafeLifetime, LifetimeConstraint, QueueFree, RawObject, Ref, RefImplBound, SafeAsRaw,
1210
SafeDeref, TRef,
@@ -441,6 +439,18 @@ where
441439
self.script
442440
.map_mut(|script| op(script, self.owner.as_ref()))
443441
}
442+
443+
/// Calls a function with a NativeClass instance and its owner, and returns its return
444+
/// value. Can be used on reference counted types for multiple times.
445+
#[inline]
446+
pub fn map_owned<F, U>(&self, op: F) -> Result<U, <T::UserData as MapOwned>::Err>
447+
where
448+
T::UserData: MapOwned,
449+
F: FnOnce(T, TRef<'_, T::Base, Access>) -> U,
450+
{
451+
self.script
452+
.map_owned(|script| op(script, self.owner.as_ref()))
453+
}
444454
}
445455

446456
/// Methods for instances with manually-managed base classes.
@@ -648,6 +658,17 @@ where
648658
{
649659
self.script.map_mut(|script| op(script, self.owner))
650660
}
661+
662+
/// Calls a function with a NativeClass instance and its owner, and returns its return
663+
/// value.
664+
#[inline]
665+
pub fn map_owned<F, U>(&self, op: F) -> Result<U, <T::UserData as MapOwned>::Err>
666+
where
667+
T::UserData: MapOwned,
668+
F: FnOnce(T, TRef<'_, T::Base, Access>) -> U,
669+
{
670+
self.script.map_owned(|script| op(script, self.owner))
671+
}
651672
}
652673

653674
impl<T, Access: ThreadAccess> Clone for Instance<T, Access>

gdnative-core/src/nativescript/macros.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,50 @@ macro_rules! godot_wrap_method {
188188
) -> $retty
189189
)
190190
};
191+
// owned
192+
(
193+
$type_name:ty,
194+
fn $method_name:ident(
195+
mut $self:ident,
196+
$owner:ident : $owner_ty:ty
197+
$(,$pname:ident : $pty:ty)*
198+
$(,#[opt] $opt_pname:ident : $opt_pty:ty)*
199+
$(,)?
200+
) -> $retty:ty
201+
) => {
202+
$crate::godot_wrap_method_inner!(
203+
$type_name,
204+
map_owned,
205+
fn $method_name(
206+
$self,
207+
$owner: $owner_ty
208+
$(,$pname : $pty)*
209+
$(,#[opt] $opt_pname : $opt_pty)*
210+
) -> $retty
211+
)
212+
};
213+
// owned
214+
(
215+
$type_name:ty,
216+
fn $method_name:ident(
217+
$self:ident,
218+
$owner:ident : $owner_ty:ty
219+
$(,$pname:ident : $pty:ty)*
220+
$(,#[opt] $opt_pname:ident : $opt_pty:ty)*
221+
$(,)?
222+
) -> $retty:ty
223+
) => {
224+
$crate::godot_wrap_method_inner!(
225+
$type_name,
226+
map_owned,
227+
fn $method_name(
228+
$self,
229+
$owner: $owner_ty
230+
$(,$pname : $pty)*
231+
$(,#[opt] $opt_pname : $opt_pty)*
232+
) -> $retty
233+
)
234+
};
191235
// mutable without return type
192236
(
193237
$type_name:ty,
@@ -230,6 +274,48 @@ macro_rules! godot_wrap_method {
230274
) -> ()
231275
)
232276
};
277+
// owned without return type
278+
(
279+
$type_name:ty,
280+
fn $method_name:ident(
281+
mut $self:ident,
282+
$owner:ident : $owner_ty:ty
283+
$(,$pname:ident : $pty:ty)*
284+
$(,#[opt] $opt_pname:ident : $opt_pty:ty)*
285+
$(,)?
286+
)
287+
) => {
288+
$crate::godot_wrap_method!(
289+
$type_name,
290+
fn $method_name(
291+
$self,
292+
$owner: $owner_ty
293+
$(,$pname : $pty)*
294+
$(,#[opt] $opt_pname : $opt_pty)*
295+
) -> ()
296+
)
297+
};
298+
// owned without return type
299+
(
300+
$type_name:ty,
301+
fn $method_name:ident(
302+
$self:ident,
303+
$owner:ident : $owner_ty:ty
304+
$(,$pname:ident : $pty:ty)*
305+
$(,#[opt] $opt_pname:ident : $opt_pty:ty)*
306+
$(,)?
307+
)
308+
) => {
309+
$crate::godot_wrap_method!(
310+
$type_name,
311+
fn $method_name(
312+
$self,
313+
$owner: $owner_ty
314+
$(,$pname : $pty)*
315+
$(,#[opt] $opt_pname : $opt_pty)*
316+
) -> ()
317+
)
318+
};
233319
}
234320

235321
/// Convenience macro to create a profiling signature with a given tag.

gdnative-core/src/nativescript/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ pub mod user_data;
1111

1212
pub use class::*;
1313
pub use init::*;
14-
pub use user_data::{Map, MapMut, UserData};
14+
pub use user_data::{Map, MapMut, MapOwned, UserData};

gdnative-core/src/nativescript/user_data.rs

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
//! - You don't need to do anything special in `Drop`.
6464
6565
use parking_lot::{Mutex, RwLock};
66-
use std::fmt::Debug;
66+
use std::fmt::{self, Debug, Display};
6767
use std::marker::PhantomData;
6868
use std::mem;
6969
use std::sync::Arc;
@@ -145,6 +145,20 @@ pub trait MapMut: UserData {
145145
F: FnOnce(&mut Self::Target) -> U;
146146
}
147147

148+
/// Trait for wrappers that can be mapped once.
149+
pub trait MapOwned: UserData {
150+
type Err: Debug;
151+
152+
/// Maps a `T` to `U`. Called for methods that take `self`. This method may fail with
153+
/// an error if it is called more than once on the same object.
154+
///
155+
/// Implementations of this method must not panic. Failures should be indicated by
156+
/// returning `Err`.
157+
fn map_owned<F, U>(&self, op: F) -> Result<U, Self::Err>
158+
where
159+
F: FnOnce(Self::Target) -> U;
160+
}
161+
148162
/// The default user data wrapper used by derive macro, when no `user_data` attribute is present.
149163
/// This may change in the future.
150164
pub type DefaultUserData<T> = LocalCellData<T>;
@@ -440,6 +454,21 @@ impl<T, OPT> Clone for RwLockData<T, OPT> {
440454
#[derive(Debug)]
441455
pub struct ArcData<T>(Arc<T>);
442456

457+
impl<T> ArcData<T> {
458+
/// Returns the internal `Arc<T>`. Useful for API's that require an `Arc`
459+
/// directly, or for coercing it into a trait object.
460+
///
461+
/// Note that this removes
462+
/// the restriction of only being able to access the `NativeClass` instance
463+
/// temporarily through the `Map` trait; however, it should be exactly as safe
464+
/// as permanently storing an owned `ArcData` and then calling `.map()` on
465+
/// it later.
466+
#[inline]
467+
pub fn into_inner(self) -> Arc<T> {
468+
self.0
469+
}
470+
}
471+
443472
unsafe impl<T> UserData for ArcData<T>
444473
where
445474
T: NativeClass + Send + Sync,
@@ -732,3 +761,76 @@ where
732761
Ok(op(&Default::default()))
733762
}
734763
}
764+
765+
/// Special user-data wrapper intended for objects that can only be used once. Only
766+
/// implements `MapOwned`.
767+
pub struct Once<T>(Arc<atomic_take::AtomicTake<T>>);
768+
769+
impl<T> Clone for Once<T> {
770+
#[inline]
771+
fn clone(&self) -> Self {
772+
Once(Arc::clone(&self.0))
773+
}
774+
}
775+
776+
unsafe impl<T> UserData for Once<T>
777+
where
778+
T: NativeClass + Send,
779+
{
780+
type Target = T;
781+
782+
#[inline]
783+
fn new(val: Self::Target) -> Self {
784+
Once(Arc::new(atomic_take::AtomicTake::new(val)))
785+
}
786+
787+
#[inline]
788+
fn into_user_data(self) -> *const libc::c_void {
789+
Arc::into_raw(self.0) as *const _
790+
}
791+
792+
#[inline]
793+
unsafe fn consume_user_data_unchecked(ptr: *const libc::c_void) -> Self {
794+
Once(Arc::from_raw(ptr as *const _))
795+
}
796+
797+
#[inline]
798+
unsafe fn clone_from_user_data_unchecked(ptr: *const libc::c_void) -> Self {
799+
let borrowed = Arc::from_raw(ptr as *const _);
800+
let arc = Arc::clone(&borrowed);
801+
mem::forget(borrowed);
802+
Once(arc)
803+
}
804+
}
805+
806+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
807+
pub struct ValueTaken;
808+
809+
impl std::error::Error for ValueTaken {}
810+
impl Display for ValueTaken {
811+
#[inline]
812+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813+
write!(f, "this object has already been used once")
814+
}
815+
}
816+
817+
impl<T> MapOwned for Once<T>
818+
where
819+
T: NativeClass + Send,
820+
{
821+
type Err = ValueTaken;
822+
823+
/// Maps a `T` to `U`. Called for methods that take `self`. This method may fail with
824+
/// an error if it is called more than once on the same object.
825+
///
826+
/// Implementations of this method must not panic. Failures should be indicated by
827+
/// returning `Err`.
828+
#[inline]
829+
fn map_owned<F, U>(&self, op: F) -> Result<U, Self::Err>
830+
where
831+
F: FnOnce(Self::Target) -> U,
832+
{
833+
let v = self.0.take().ok_or(ValueTaken)?;
834+
Ok(op(v))
835+
}
836+
}

test/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use gdnative::prelude::*;
55
mod test_constructor;
66
mod test_derive;
77
mod test_free_ub;
8+
mod test_map_owned;
89
mod test_register;
910
mod test_return_leak;
1011
mod test_vararray_return;
@@ -61,6 +62,7 @@ pub extern "C" fn run_tests(
6162
status &= test_derive::run_tests();
6263
status &= test_free_ub::run_tests();
6364
status &= test_constructor::run_tests();
65+
status &= test_map_owned::run_tests();
6466
status &= test_register::run_tests();
6567
status &= test_return_leak::run_tests();
6668
status &= test_variant_call_args::run_tests();
@@ -255,6 +257,7 @@ fn init(handle: InitHandle) {
255257
test_derive::register(handle);
256258
test_free_ub::register(handle);
257259
test_constructor::register(handle);
260+
test_map_owned::register(handle);
258261
test_register::register(handle);
259262
test_return_leak::register(handle);
260263
test_variant_call_args::register(handle);

test/src/test_map_owned.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use gdnative::nativescript::user_data::Once;
2+
use gdnative::prelude::*;
3+
4+
pub(crate) fn run_tests() -> bool {
5+
let mut status = true;
6+
7+
status &= test_map_owned();
8+
9+
status
10+
}
11+
12+
pub(crate) fn register(handle: InitHandle) {
13+
handle.add_class::<VecBuilder>();
14+
}
15+
16+
#[derive(NativeClass)]
17+
#[no_constructor]
18+
#[inherit(Reference)]
19+
#[user_data(Once<Self>)]
20+
struct VecBuilder {
21+
v: Vec<i32>,
22+
}
23+
24+
#[methods]
25+
impl VecBuilder {
26+
#[export]
27+
fn append(mut self, _owner: TRef<Reference>, mut numbers: Vec<i32>) -> Instance<Self, Shared> {
28+
self.v.append(&mut numbers);
29+
Instance::emplace(Self { v: self.v }).into_shared()
30+
}
31+
}
32+
33+
fn test_map_owned() -> bool {
34+
println!(" -- test_map_owned");
35+
36+
let ok = std::panic::catch_unwind(|| {
37+
let v1 = Instance::emplace(VecBuilder { v: Vec::new() }).into_shared();
38+
let v1 = unsafe { v1.assume_safe() };
39+
40+
let v2 = v1
41+
.map_owned(|s, owner| s.append(owner, vec![1, 2, 3]))
42+
.unwrap();
43+
let v2 = unsafe { v2.assume_safe() };
44+
assert!(v1
45+
.map_owned(|_, _| panic!("should never be called"))
46+
.is_err());
47+
48+
let v3 = v2
49+
.map_owned(|s, owner| s.append(owner, vec![4, 5, 6]))
50+
.unwrap();
51+
let v3 = unsafe { v3.assume_safe() };
52+
assert!(v2
53+
.map_owned(|_, _| panic!("should never be called"))
54+
.is_err());
55+
56+
let v = v3.map_owned(|s, _| s.v).unwrap();
57+
assert_eq!(&v, &[1, 2, 3, 4, 5, 6]);
58+
assert!(v3
59+
.map_owned(|_, _| panic!("should never be called"))
60+
.is_err());
61+
})
62+
.is_ok();
63+
64+
if !ok {
65+
gdnative::godot_error!(" !! Test test_map_owned failed");
66+
}
67+
68+
ok
69+
}

0 commit comments

Comments
 (0)