Skip to content

Commit 42b7e8a

Browse files
committed
zephyr: work: Generalize Work pointer type
First step in generalizing the worker type. Place the inner pointer type into a trait, and allow the work methods to work on pointers of that type. This continues to work the same as before with `Pin<Arc<Work<T>>>`. It needs a bit more massaging to be able to work with static pointers, namely that the constructor has to be able to be static, and as such, the initialization can't happen until it has been pinned down. This will likely change the "new" API. Signed-off-by: David Brown <[email protected]>
1 parent 5628a23 commit 42b7e8a

File tree

2 files changed

+85
-68
lines changed

2 files changed

+85
-68
lines changed

samples/bench/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ impl Simple {
583583

584584
fn run(&self, workers: usize, iterations: usize) {
585585
// printkln!("Running Simple");
586-
let main = Work::new(SimpleMain::new(workers * iterations, self.workq));
586+
let main: Pin<Arc<Work<_>>> = Work::new(SimpleMain::new(workers * iterations, self.workq));
587587

588588
let children: VecDeque<_> = (0..workers)
589589
.map(|n| Work::new(SimpleWorker::new(main.clone(), self.workq, n)))

zephyr/src/work.rs

Lines changed: 84 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -331,24 +331,6 @@ impl SubmitResult {
331331
}
332332
}
333333

334-
/*
335-
pub trait Queueable: Send + Sync {
336-
fn as_ptr(&self) -> *const ();
337-
}
338-
339-
impl<T: Send + Sync> Queueable for Arc<T> {
340-
fn as_ptr(&self) -> *const () {
341-
todo!()
342-
}
343-
}
344-
345-
impl<T: Send + Sync> Queueable for &'static T {
346-
fn as_ptr(&self) -> *const () {
347-
todo!()
348-
}
349-
}
350-
*/
351-
352334
/// A simple action that just does something with its data.
353335
///
354336
/// This is similar to a Future, except there is no concept of it completing. It manages its
@@ -394,16 +376,15 @@ impl<T: SimpleAction + Send> Work<T> {
394376
/// inter-thread sharing mechanisms are needed.
395377
///
396378
/// TODO: Can we come up with a way to allow sharing on the same worker using Rc instead of Arc?
397-
pub fn new(action: T) -> Pin<Arc<Self>> {
398-
let this = Arc::pin(Self {
399-
// SAFETY: will be initialized below, after this is pinned.
400-
work: unsafe { mem::zeroed() },
401-
action,
402-
});
379+
pub fn new<P>(action: T) -> P
380+
where
381+
P: SubmittablePointer<T>,
382+
{
383+
let this: P = unsafe { SubmittablePointer::new_ptr(action) };
403384

404-
// SAFETY: Initializes above zero-initialized struct.
385+
// SAFETY: Initializes the above zero-initialized struct.
405386
unsafe {
406-
k_work_init(this.work.get(), Some(Self::handler));
387+
k_work_init(this.get_work(), Some(P::handler));
407388
}
408389

409390
this
@@ -413,89 +394,117 @@ impl<T: SimpleAction + Send> Work<T> {
413394
///
414395
/// This can return several possible `Ok` results. See the docs on [`SubmitResult`] for an
415396
/// explanation of them.
416-
pub fn submit(this: Pin<Arc<Self>>) -> crate::Result<SubmitResult> {
397+
pub fn submit<P>(this: P) -> crate::Result<SubmitResult>
398+
where
399+
P: SubmittablePointer<T>,
400+
{
417401
// We "leak" the arc, so that when the handler runs, it can be safely turned back into an
418402
// Arc, and the drop on the arc will then run.
419-
let work = this.work.get();
403+
let work = this.get_work();
420404

421405
// SAFETY: C the code does not perform moves on the data, and the `from_raw` below puts it
422406
// back into a Pin when it reconstructs the Arc.
423-
let this = unsafe { Pin::into_inner_unchecked(this) };
424-
let _ = Arc::into_raw(this.clone());
407+
unsafe {
408+
P::into_raw(this);
409+
}
425410

426411
// SAFETY: The Pin ensures this will not move. Our implementation of drop ensures that the
427412
// work item is no longer queued when the data is dropped.
428413
let result = SubmitResult::to_result(unsafe { k_work_submit(work) });
429414

430-
Self::check_drop(work, &result);
415+
P::check_drop(work, &result);
431416

432417
result
433418
}
434419

435420
/// Submit this work to a specified work queue.
436421
///
437422
/// TODO: Change when we have better wrappers for work queues.
438-
pub fn submit_to_queue(
439-
this: Pin<Arc<Self>>,
440-
queue: &'static WorkQueue,
441-
) -> crate::Result<SubmitResult> {
442-
let work = this.work.get();
423+
pub fn submit_to_queue<P>(this: P, queue: &'static WorkQueue) -> crate::Result<SubmitResult>
424+
where
425+
P: SubmittablePointer<T>,
426+
{
427+
let work = this.get_work();
443428

444429
// "leak" the arc to give to C. We'll reconstruct it in the handler.
445430
// SAFETY: The C code does not perform moves on the data, and the `from_raw` below puts it
446431
// back into a Pin when it reconstructs the Arc.
447-
let this = unsafe { Pin::into_inner_unchecked(this) };
448-
let _ = Arc::into_raw(this);
432+
unsafe {
433+
P::into_raw(this);
434+
}
449435

450436
// SAFETY: The Pin ensures this will not move. Our implementation of drop ensures that the
451437
// work item is no longer queued when the data is dropped.
452438
let result =
453439
SubmitResult::to_result(unsafe { k_work_submit_to_queue(queue.item.get(), work) });
454440

455-
Self::check_drop(work, &result);
441+
P::check_drop(work, &result);
456442

457443
result
458444
}
459445

460-
/// Callback, through C, but bound by a specific type.
461-
extern "C" fn handler(work: *mut k_work) {
462-
// We want to avoid needing a `repr(C)` on our struct, so the `k_work` pointer is not
463-
// necessarily at the beginning of the struct.
464-
// SAFETY: Converts raw pointer to work back into the box.
465-
let this = unsafe { Self::from_raw(work) };
446+
/// Access the inner action.
447+
pub fn action(&self) -> &T {
448+
&self.action
449+
}
450+
}
466451

467-
// Access the action within, still pinned.
468-
// SAFETY: It is safe to keep the pin on the interior.
469-
let action = unsafe { this.as_ref().map_unchecked(|p| &p.action) };
452+
/// Capture the kinds of pointers that are safe to submit to work queues.
453+
pub trait SubmittablePointer<T> {
454+
/// Create a new version of a pointer for this particular type. The pointer should be pinned
455+
/// after this call, and can then be initialized and used by C code.
456+
unsafe fn new_ptr(action: T) -> Self;
470457

471-
action.act();
458+
/// Given a raw pointer to the work_q burried within, recover the Self pointer containing our
459+
/// data.
460+
unsafe fn from_raw(ptr: *const k_work) -> Self;
461+
462+
/// Given our Self, indicate that this reference is now owned by the C code. For something like
463+
/// Arc, this should leak a reference, and is the opposite of from_raw.
464+
unsafe fn into_raw(self);
465+
466+
/// Determine from the submitted work if this work has been enqueued, and if not, cause a "drop"
467+
/// to happen on the Self pointer type.
468+
fn check_drop(work: *const k_work, result: &crate::Result<SubmitResult>);
469+
470+
/// Get the inner work pointer.
471+
fn get_work(&self) -> *mut k_work;
472+
473+
/// The low-level handler for this specific type.
474+
extern "C" fn handler(work: *mut k_work);
475+
}
476+
477+
impl<T: SimpleAction + Send> SubmittablePointer<T> for Pin<Arc<Work<T>>> {
478+
unsafe fn new_ptr(action: T) -> Self {
479+
Arc::pin(Work {
480+
work: unsafe { mem::zeroed() },
481+
action,
482+
})
472483
}
473484

474-
/*
475-
/// Consume this Arc, returning the internal pointer. Needs to have a complementary `from_raw`
476-
/// called to avoid leaking the item.
477-
fn into_raw(this: Pin<Arc<Self>>) -> *const Self {
478-
// SAFETY: This removes the Pin guarantee, but is given as a raw pointer to C, which doesn't
479-
// generally use move.
480-
let this = unsafe { Pin::into_inner_unchecked(this) };
481-
Arc::into_raw(this)
485+
fn get_work(&self) -> *mut k_work {
486+
self.work.get()
482487
}
483-
*/
484488

485-
/// Given a pointer to the work_q burried within, recover the Pinned Box containing our data.
486-
unsafe fn from_raw(ptr: *const k_work) -> Pin<Arc<Self>> {
489+
unsafe fn from_raw(ptr: *const k_work) -> Self {
487490
// SAFETY: This fixes the pointer back to the beginning of Self. This also assumes the
488491
// pointer is valid.
489492
let ptr = ptr
490493
.cast::<u8>()
491-
.sub(mem::offset_of!(Self, work))
492-
.cast::<Self>();
494+
.sub(mem::offset_of!(Work<T>, work))
495+
.cast::<Work<T>>();
493496
let this = Arc::from_raw(ptr);
494497
Pin::new_unchecked(this)
495498
}
496499

497-
/// Determine if this work was submitted, and cause a drop of the Arc to happen if it was not.
498-
pub fn check_drop(work: *const k_work, result: &crate::Result<SubmitResult>) {
500+
unsafe fn into_raw(self) {
501+
// SAFETY: The C code does not perform moves on the data, and the `from_raw` that gets back
502+
// our Arc puts it back into the pin when it reconstructs the Arc.
503+
let this = unsafe { Pin::into_inner_unchecked(self) };
504+
let _ = Arc::into_raw(this.clone());
505+
}
506+
507+
fn check_drop(work: *const k_work, result: &crate::Result<SubmitResult>) {
499508
if matches!(result, Ok(SubmitResult::AlreadySubmitted) | Err(_)) {
500509
// SAFETY: If the above submit indicates that it was already running, the work will not
501510
// be submitted (no additional handle will be called). "un leak" the work so that it
@@ -507,9 +516,17 @@ impl<T: SimpleAction + Send> Work<T> {
507516
}
508517
}
509518

510-
/// Access the inner action.
511-
pub fn action(&self) -> &T {
512-
&self.action
519+
extern "C" fn handler(work: *mut k_work) {
520+
// We want to avoid needing a `repr(C)` on our struct, so the `k_work` pointer is not
521+
// necessarily at the beginning of the struct.
522+
// SAFETY: Converts raw pointer to work back into the box.
523+
let this = unsafe { Self::from_raw(work) };
524+
525+
// Access the action within, still pinned.
526+
// SAFETY: It is safe to keep the pin on the interior.
527+
let action = unsafe { this.as_ref().map_unchecked(|p| &p.action) };
528+
529+
action.act();
513530
}
514531
}
515532

0 commit comments

Comments
 (0)