Skip to content

Commit 3f972ff

Browse files
author
toasteater
committed
Add the MapOwned trait and the Once<T> userdata type
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.
1 parent 4bc7681 commit 3f972ff

File tree

7 files changed

+272
-5
lines changed

7 files changed

+272
-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: 88 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>;
@@ -732,3 +746,76 @@ where
732746
Ok(op(&Default::default()))
733747
}
734748
}
749+
750+
/// Special user-data wrapper intended for objects that can only be used once. Only
751+
/// implements `MapOwned`.
752+
pub struct Once<T>(Arc<atomic_take::AtomicTake<T>>);
753+
754+
impl<T> Clone for Once<T> {
755+
#[inline]
756+
fn clone(&self) -> Self {
757+
Once(Arc::clone(&self.0))
758+
}
759+
}
760+
761+
unsafe impl<T> UserData for Once<T>
762+
where
763+
T: NativeClass + Send,
764+
{
765+
type Target = T;
766+
767+
#[inline]
768+
fn new(val: Self::Target) -> Self {
769+
Once(Arc::new(atomic_take::AtomicTake::new(val)))
770+
}
771+
772+
#[inline]
773+
fn into_user_data(self) -> *const libc::c_void {
774+
Arc::into_raw(self.0) as *const _
775+
}
776+
777+
#[inline]
778+
unsafe fn consume_user_data_unchecked(ptr: *const libc::c_void) -> Self {
779+
Once(Arc::from_raw(ptr as *const _))
780+
}
781+
782+
#[inline]
783+
unsafe fn clone_from_user_data_unchecked(ptr: *const libc::c_void) -> Self {
784+
let borrowed = Arc::from_raw(ptr as *const _);
785+
let arc = Arc::clone(&borrowed);
786+
mem::forget(borrowed);
787+
Once(arc)
788+
}
789+
}
790+
791+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
792+
pub struct ValueTaken;
793+
794+
impl std::error::Error for ValueTaken {}
795+
impl Display for ValueTaken {
796+
#[inline]
797+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
798+
write!(f, "this object has already been used once")
799+
}
800+
}
801+
802+
impl<T> MapOwned for Once<T>
803+
where
804+
T: NativeClass + Send,
805+
{
806+
type Err = ValueTaken;
807+
808+
/// Maps a `T` to `U`. Called for methods that take `self`. This method may fail with
809+
/// an error if it is called more than once on the same object.
810+
///
811+
/// Implementations of this method must not panic. Failures should be indicated by
812+
/// returning `Err`.
813+
#[inline]
814+
fn map_owned<F, U>(&self, op: F) -> Result<U, Self::Err>
815+
where
816+
F: FnOnce(Self::Target) -> U,
817+
{
818+
let v = self.0.take().ok_or(ValueTaken)?;
819+
Ok(op(v))
820+
}
821+
}

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)