1
+ use std:: ops:: Deref ;
1
2
use std:: sync:: Arc ;
2
3
3
4
use bevy:: prelude:: World ;
@@ -6,37 +7,153 @@ use parking_lot::{
6
7
} ;
7
8
8
9
/// Pointer to a bevy world, safely allows multiple access via RwLock
9
- /// # Safety
10
- /// This pointer does not prevent dangling pointers, i.e. you must ensure the world is not dropped while any world pointers still exist,
11
- /// the world must also not change, from the moment a world pointer is created it must always point to the same world.
10
+ ///
11
+ /// If the original [WorldPointerGuard] that created this pointer is dropped,
12
+ /// the `read` and `write` methods will panic, and the "try" variants will
13
+ /// return `None`.
12
14
#[ derive( Debug , Clone ) ]
13
- pub struct WorldPointer ( Arc < RwLock < * mut World > > ) ;
15
+ pub struct WorldPointer ( Arc < RwLock < Option < * mut World > > > ) ;
16
+
17
+ /// Guarded pointer to a bevy world, can be used to `clone` additional
18
+ /// [WorldPointer]s for safe access.
19
+ ///
20
+ /// # Safety
21
+ /// The original `&mut World` used to create this guard _must not_ be used after
22
+ /// the guard is created in order for the cloned [WorldPointer]s to be safe.
23
+ ///
24
+ /// On [Drop], it will "take back" access to the `&mut World`, preventing the
25
+ /// `WorldPointer`s from invoking UB.
26
+ #[ derive( Debug ) ]
27
+ pub struct WorldPointerGuard ( WorldPointer ) ;
28
+
29
+ impl Deref for WorldPointerGuard {
30
+ type Target = WorldPointer ;
31
+ fn deref ( & self ) -> & Self :: Target {
32
+ & self . 0
33
+ }
34
+ }
14
35
15
36
unsafe impl Send for WorldPointer { }
16
37
unsafe impl Sync for WorldPointer { }
17
38
18
- impl WorldPointer {
39
+ impl WorldPointerGuard {
19
40
/// Creates a new world pointer.
20
41
/// # Safety
21
- /// satisfies world constancy, since it's impossible to change the underlying pointer
22
- /// However you must ensure that the world does not go out of scope while this pointer is live
42
+ /// The original `&mut World` must not be used while this guard is in scope.
43
+ /// The [World] may only be accessed through this guard or one of its cloned
44
+ /// [WorldPointer]s.
23
45
#[ allow( clippy:: arc_with_non_send_sync) ]
24
46
pub unsafe fn new ( world : & mut World ) -> Self {
25
- WorldPointer ( Arc :: new ( RwLock :: new ( world) ) )
47
+ WorldPointerGuard ( WorldPointer ( Arc :: new ( RwLock :: new ( Some ( world) ) ) ) )
48
+ }
49
+ }
50
+
51
+ impl Drop for WorldPointerGuard {
52
+ fn drop ( & mut self ) {
53
+ // Being explicit about the types here to make sure we're getting things
54
+ // correct.
55
+ let world_ptr: & WorldPointer = & self . 0 ;
56
+ let _: Option < * mut World > = RwLock :: write ( & world_ptr. 0 ) . take ( ) ;
26
57
}
58
+ }
27
59
60
+ impl WorldPointer {
28
61
/// Returns a read guard which can be used for immutable world access.
62
+ ///
63
+ /// Panics if the pointer is already locked or has gone out of scope.
29
64
pub fn read ( & self ) -> MappedRwLockReadGuard < World > {
30
- RwLockReadGuard :: map ( self . 0 . try_read ( ) . expect ( "" ) , |ptr : & * mut World | unsafe {
31
- & * * ptr
32
- } )
65
+ self . try_read ( ) . expect ( "concurrent read/write world access" )
33
66
}
34
67
35
68
/// Returns a write guard which can be used for mutable world access.
69
+ ///
70
+ /// Panics if the pointer is already locked or has gone out of scope.
36
71
pub fn write ( & self ) -> MappedRwLockWriteGuard < World > {
37
- RwLockWriteGuard :: map (
38
- self . 0 . try_write ( ) . expect ( "" ) ,
39
- |ptr : & mut * mut World | unsafe { & mut * * ptr } ,
40
- )
72
+ self . try_write ( )
73
+ . expect ( "concurrent read/write world access" )
74
+ }
75
+
76
+ /// Returns a read guard which can be used for immutable world access.
77
+ ///
78
+ /// Returns `None` if the pointer is already locked or has gone out of
79
+ /// scope.
80
+ pub fn try_read ( & self ) -> Option < MappedRwLockReadGuard < World > > {
81
+ self . try_read_inner ( false )
82
+ }
83
+
84
+ /// Returns a write guard which can be used for mutable world access.
85
+ ///
86
+ /// Returns `None` if the pointer is already locked or has gone out of
87
+ /// scope.
88
+ pub fn try_write ( & self ) -> Option < MappedRwLockWriteGuard < World > > {
89
+ self . try_write_inner ( false )
90
+ }
91
+
92
+ /// Returns a read guard which can be used for immutable world access.
93
+ ///
94
+ /// Panics if the pointer has gone out of scope. May block if another thread
95
+ /// holds the lock.
96
+ pub fn read_blocking ( & self ) -> MappedRwLockReadGuard < World > {
97
+ self . try_read_blocking ( )
98
+ . expect ( "the world pointer is out of scope" )
99
+ }
100
+
101
+ /// Returns a write guard which can be used for mutable world access.
102
+ ///
103
+ /// Panics if the pointer has gone out of scope. May block if another thread
104
+ /// holds the lock.
105
+ pub fn write_blocking ( & self ) -> MappedRwLockWriteGuard < World > {
106
+ self . try_write_blocking ( )
107
+ . expect ( "the world pointer is out of scope" )
108
+ }
109
+
110
+ /// Returns a read guard which can be used for immutable world access.
111
+ ///
112
+ /// Returns `None` if has gone out of scope. May block if another thread
113
+ /// holds the lock.
114
+ pub fn try_read_blocking ( & self ) -> Option < MappedRwLockReadGuard < World > > {
115
+ self . try_read_inner ( true )
116
+ }
117
+
118
+ /// Returns a write guard which can be used for mutable world access.
119
+ ///
120
+ /// Returns `None` if has gone out of scope. May block if another thread
121
+ /// holds the lock.
122
+ pub fn try_write_blocking ( & self ) -> Option < MappedRwLockWriteGuard < World > > {
123
+ self . try_write_inner ( true )
124
+ }
125
+
126
+ fn try_read_inner ( & self , blocking : bool ) -> Option < MappedRwLockReadGuard < World > > {
127
+ let guard = if blocking {
128
+ self . 0 . read ( )
129
+ } else {
130
+ self . 0 . try_read ( ) ?
131
+ } ;
132
+ // Check if the inner pointer is there so we can invert the `Option`.
133
+ if guard. is_none ( ) {
134
+ return None ;
135
+ }
136
+
137
+ Some ( RwLockReadGuard :: map (
138
+ guard,
139
+ |ptr : & Option < * mut World > | unsafe { & * ptr. unwrap ( ) } ,
140
+ ) )
141
+ }
142
+
143
+ fn try_write_inner ( & self , blocking : bool ) -> Option < MappedRwLockWriteGuard < World > > {
144
+ let guard = if blocking {
145
+ self . 0 . write ( )
146
+ } else {
147
+ self . 0 . try_write ( ) ?
148
+ } ;
149
+ // Check if the inner pointer is there so we can invert the `Option`.
150
+ if guard. is_none ( ) {
151
+ return None ;
152
+ }
153
+
154
+ Some ( RwLockWriteGuard :: map (
155
+ guard,
156
+ |ptr : & mut Option < * mut World > | unsafe { & mut * ptr. unwrap ( ) } ,
157
+ ) )
41
158
}
42
159
}
0 commit comments