@@ -92,6 +92,149 @@ use alloc::boxed::Box;
92
92
93
93
use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
94
94
95
+ /// ## Init/move safe objects
96
+ ///
97
+ /// In Rust code, many language features are designed around Rust's "move semantics". Because of
98
+ /// the borrow checker, the Rust compiler has full knowledge of when it is safe to move an object in
99
+ /// memory, as it will know that there are no references to it.
100
+ ///
101
+ /// However, most kernel objects in Zephyr contain self-referential pointers to those objects. The
102
+ /// traditional way to handle this in Rust is to use `Pin`. However, besides Pin being awkward to
103
+ /// use, it is generally assumed that the underlying objects will be dynamically allocated. It is
104
+ /// desirable to allow as much functionality of Zephyr to be used without explicitly requiring
105
+ /// alloc.
106
+ ///
107
+ /// The original solution (Wrapped), used a type `Fixed` that either referenced a static, or
108
+ /// contained a `Pin<Box<T>>` of the Zephyr kernel object. This introduces overhead for both the
109
+ /// enum as well as the actual reference itself.
110
+ ///
111
+ /// Some simple benchmarking has determined that it is just as efficient, or even slightly more so,
112
+ /// to represent each object instead as a `UnsafeCell` contaning the Zephyr object, and an atomic
113
+ /// pointer that can be used to determine the state of the object.
114
+ ///
115
+ /// This type is not intended for general use, but for the implementation of wrappers for Zephyr
116
+ /// types that require non-move semantics.
117
+ ///
118
+ /// # Safety
119
+ ///
120
+ /// The Zephyr APIs require that once objects have been initialized, they are not moved in memory.
121
+ /// To avoid the inconvenience of managing 'Pin' for most of these, we rely on a run-time detection
122
+ /// both of initialization and non-movement. It is fairly easy, as a user of an object in Rust to
123
+ /// avoid moving it. Generally, in an embedded system, objects will either live on the stack of a
124
+ /// single persistent thread, or will be statically allocated. Both of these cases will result in
125
+ /// objects that don't move. However, we want initialization to be able to happen on first _use_
126
+ /// not when the constructor runs, because the semantics of most constructors invovles a move (even
127
+ /// if that is often optimized away).
128
+ ///
129
+ /// Note that this does not solve the case of objects that must not be moved even after the object
130
+ /// has a single Rust reference (threads, and work queues, notably, or timers with active
131
+ /// callbacks).
132
+ ///
133
+ /// To do this, each object is paired with an Atomic pointer. The pointer can exist in three state:
134
+ /// - null: The object has not been initialized. It is safe to move the object at this time.
135
+ /// - pointer that equals the addres of the object itself. Object has been initialized, and can be
136
+ /// used. It must not be moved.
137
+ /// - pointer that doesn't match the object. This indicates that the object was moved, and is
138
+ /// invalid. We treat this as a panic condition.
139
+ pub struct ZephyrObject < T > {
140
+ state : AtomicUsize ,
141
+ object : UnsafeCell < T > ,
142
+ }
143
+
144
+ impl < T > ZephyrObject < T >
145
+ where
146
+ ZephyrObject < T > : ObjectInit < T > ,
147
+ {
148
+ /// Construct a new Zephyr Object.
149
+ ///
150
+ /// The 'init' function will be given a reference to the object. For objects that have
151
+ /// initialization parameters (specifically Semaphores), this can be used to hold those
152
+ /// parameters until the real init is called upon first use.
153
+ ///
154
+ /// The 'setup' function must not assume the address given to it will persist. The object can
155
+ /// be freely moved by Rust until the 'init' call has been called, which happens on first use.
156
+ pub const fn new_raw ( ) -> Self {
157
+ Self {
158
+ state : AtomicUsize :: new ( 0 ) ,
159
+ // SAFETY: It is safe to assume Zephyr objects can be zero initialized before calling
160
+ // their init. The atomic above will ensure that this is not used by any API other than
161
+ // the init call until it has been initialized.
162
+ object : UnsafeCell :: new ( unsafe { mem:: zeroed ( ) } ) ,
163
+ }
164
+ }
165
+
166
+ /// Get a reference, _without initializing_ the item.
167
+ ///
168
+ /// This is useful during a const constructor to be able to stash values in the item.
169
+ pub const fn get_uninit ( & self ) -> * mut T {
170
+ self . object . get ( )
171
+ }
172
+
173
+ /// Get a reference to the underlying zephyr object, ensuring it has been initialized properly.
174
+ /// The method is unsafe, because the caller must ensure that the lifetime of `&self` is long
175
+ /// enough for the use of the raw pointer.
176
+ ///
177
+ /// # Safety
178
+ ///
179
+ /// If the object has not been initialized, It's 'init' method will be called. If the object
180
+ /// has been moved since `init` was called, this will panic. Otherwise, the caller must ensure
181
+ /// that the use of the returned pointer does not outlive the `&self`.
182
+ ///
183
+ /// The 'init' method will be called within a critical section, so should be careful to not
184
+ /// block, or take extra time.
185
+ pub unsafe fn get ( & self ) -> * mut T {
186
+ let addr = self . object . get ( ) ;
187
+
188
+ // First, try reading the atomic relaxed. This makes the common case of the object being
189
+ // initialized faster, and we can double check after.
190
+ match self . state . load ( Ordering :: Relaxed ) {
191
+ // Uninitialized. Falls through to the slower init case.
192
+ 0 => ( ) ,
193
+ // Initialized, and object has not been moved.
194
+ ptr if ptr == addr as usize => return addr,
195
+ _ => {
196
+ // Object was moved after init.
197
+ panic ! ( "Object moved after init" ) ;
198
+ }
199
+ }
200
+
201
+ // Perform the possible initialization within a critical section to avoid a race and double
202
+ // initialization.
203
+ critical_section:: with ( |_| {
204
+ // Reload, with Acquire ordering to see a determined value.
205
+ let state = self . state . load ( Ordering :: Acquire ) ;
206
+
207
+ // If the address does match, an initialization got in before the critical section.
208
+ if state == addr as usize {
209
+ // Note, this returns from the closure, not the function, but this is ok, as the
210
+ // critical section result is the return result of the whole function.
211
+ return addr;
212
+ } else if state != 0 {
213
+ // Initialization got in, and then it was moved. This shouldn't happen without
214
+ // unsafe code, but it is easy to detect.
215
+ panic ! ( "Object moved after init" ) ;
216
+ }
217
+
218
+ // Perform the initialization.
219
+ <Self as ObjectInit < T > >:: init ( addr) ;
220
+
221
+ addr
222
+ } )
223
+ }
224
+ }
225
+
226
+ /// All `ZephyrObject`s must implement `ObjectInit` in order for first use to be able to initialize
227
+ /// the object.
228
+ pub trait ObjectInit < T > {
229
+ /// Initialize the object.
230
+ ///
231
+ /// This is called upon first use. The address given may (and generally will) be different than
232
+ /// the initial address given to the `setup` call in the [`ZephyrObject::new`] constructor.
233
+ /// After this is called, all subsequent calls to [`ZephyrObject::get`] will return the same
234
+ /// address, or panic.
235
+ fn init ( item : * mut T ) ;
236
+ }
237
+
95
238
// The kernel object itself must be wrapped in `UnsafeCell` in Rust. This does several thing, but
96
239
// the primary feature that we want to declare to the Rust compiler is that this item has "interior
97
240
// mutability". One impact will be that the default linker section will be writable, even though
@@ -100,6 +243,10 @@ use crate::sync::atomic::{AtomicUsize, Ordering};
100
243
// the mutations happen from C code, so this is less important than the data being placed in the
101
244
// proper section. Many will have the link section overridden by the `kobj_define` macro.
102
245
246
+ /// ## Old Wrapped objects
247
+ ///
248
+ /// The wrapped objects was the original approach to managing Zephyr objects.
249
+ ///
103
250
/// Define the Wrapping of a kernel object.
104
251
///
105
252
/// This trait defines the association between a static kernel object and the two associated Rust
0 commit comments