1
//! A generic work queue.
2
//!
3
//! Work queues are the backbone of cryptographic drivers. They enable asynchronous and
4
//! blocking operations using shared peripherals, like crypto accelerators. Clients
5
//! post work items into the work queue, and poll for completion. The act of polling
6
//! processes the queue, which allows any number of clients to post work without deadlocking.
7
//!
8
//! Work queues are configured by backends. Backends register a `process` callback which is
9
//! called when a client posts a work item or polls for completion.
10
//!
11
//! Posting a work item into the queue returns a handle. The handle can be used to poll whether
12
//! the work item has been processed. Dropping the handle will cancel the work item.
13
#![cfg_attr(not(feature = "unstable"), allow(unused))]
14

            
15
use core::{future::poll_fn, marker::PhantomData, ptr::NonNull, task::Context};
16

            
17
use embassy_sync::waitqueue::WakerRegistration;
18
use esp_sync::NonReentrantMutex;
19

            
20
/// Queues driver operations.
21
///
22
/// Functions in this VTable are provided by drivers that consume work items.
23
/// These functions may be called both in the context of the queue frontends and drivers.
24
pub(crate) struct VTable<T: Sync + Send> {
25
    /// Starts processing a new work item.
26
    ///
27
    /// The function returns whether the work item was accepted, and its poll status if it was
28
    /// accepted. If there is no driver currently processing the queue, this function will
29
    /// return None to prevent removing the work item from the queue.
30
    ///
31
    /// Should be as short as possible.
32
    pub(crate) post: fn(NonNull<()>, &mut T) -> Option<Poll>,
33

            
34
    /// Polls the status of the current work item.
35
    ///
36
    /// The work queue ensures that the item passed here has been first passed to the driver by
37
    /// `post`.
38
    ///
39
    /// Should be as short as possible.
40
    pub(crate) poll: fn(NonNull<()>, &mut T) -> Poll,
41

            
42
    /// Attempts to abort processing a work item.
43
    ///
44
    /// Should be as short as possible.
45
    pub(crate) cancel: fn(NonNull<()>, &mut T),
46

            
47
    /// Called when the driver may be stopped.
48
    ///
49
    /// Should be as short as possible.
50
    pub(crate) stop: fn(NonNull<()>),
51
}
52

            
53
impl<T: Sync + Send> VTable<T> {
54
    pub(crate) const fn noop() -> Self {
55
        Self {
56
            post: |_, _| None,
57
            poll: |_, _| unreachable!(),
58
            cancel: |_, _| (),
59
            stop: |_| (),
60
        }
61
    }
62
}
63

            
64
struct Inner<T: Sync + Send> {
65
    head: Option<NonNull<WorkItem<T>>>,
66
    tail: Option<NonNull<WorkItem<T>>>,
67
    current: Option<NonNull<WorkItem<T>>>,
68

            
69
    // The data pointer will be passed to VTable functions, which may be called in any context.
70
    data: NonNull<()>,
71
    vtable: VTable<T>,
72

            
73
    // Counts suspend requests. When this reaches 0 again, the all wakers in the queue need to be
74
    // waken to continue processing.
75
    suspend_count: usize,
76
    // The task waiting for the queue to be suspended. There can be multiple tasks, but that's
77
    // practically rare (in this setup, it needs both HMAC and DSA to want to work at the same
78
    // time).
79
    suspend_waker: WakerRegistration,
80
}
81

            
82
unsafe impl<T: Sync + Send> Send for Inner<T> {}
83
unsafe impl<T: Sync + Send> Sync for Inner<T> {}
84

            
85
impl<T: Sync + Send> Inner<T> {
86
    /// Places a work item at the end of the queue.
87
    fn enqueue(&mut self, ptr: NonNull<WorkItem<T>>) {
88
        if let Some(tail) = self.tail.as_mut() {
89
            // Queue contains something, append to `tail`.
90
2
            unsafe { tail.as_mut().next = Some(ptr) };
91
        } else {
92
            // Queue was empty, set `head` to the first element.
93
146
            self.head = Some(ptr);
94
        }
95

            
96
        // Move `tail` to the newly inserted item.
97
146
        self.tail = Some(ptr);
98
    }
99

            
100
    /// Places a work item at the front of the queue.
101
    fn enqueue_front(&mut self, mut ptr: NonNull<WorkItem<T>>) {
102
        // Chain the node into the list.
103
1
        unsafe { ptr.as_mut().next = self.head };
104

            
105
        // Adjust list `head` to point at the new-first element.
106
1
        self.head = Some(ptr);
107
1
        if self.tail.is_none() {
108
            // The queue was empty, we need to set `tail` to the last element.
109
1
            self.tail = Some(ptr);
110
        }
111
    }
112

            
113
    /// Runs one processing iteration.
114
    ///
115
    /// Enqueues a new work item or polls the status of the currently processed one.
116
    /// Returns whether the function should be re-called by the caller.
117
    fn process(&mut self) -> bool {
118
294
        if let Some(mut current) = self.current {
119
270
            let poll_result = (self.vtable.poll)(self.data, &mut unsafe { current.as_mut() }.data);
120

            
121
270
            match poll_result {
122
                Poll::Ready(status) => {
123
                    unsafe { current.as_mut() }.complete(status);
124
270
                    self.current = None;
125
270
                    if self.suspend_count > 0 {
126
                        // Queue suspended, stop the driver.
127
                        (self.vtable.stop)(self.data);
128
                        self.suspend_waker.wake();
129
                        false
130
                    } else {
131
                        self.dequeue_and_post(true)
132
                    }
133
                }
134
                Poll::Pending(recall) => recall,
135
            }
136
        } else {
137
            // If the queue is empty, the driver should already have been notified when the queue
138
            // became empty, so we don't notify it here.
139
            self.dequeue_and_post(false)
140
        }
141
    }
142

            
143
    /// Retrieves the next work queue item and sends it to the driver.
144
    ///
145
    /// Returns whether the queue needs to be polled again.
146
    // Note: even if the queue itself may be implemented lock-free, dequeuing and posting to the
147
    // driver must be done atomically to ensure that the queue can be processed fully by any of
148
    // the frontends polling it.
149
    fn dequeue_and_post(&mut self, notify_on_empty: bool) -> bool {
150
        let Some(mut ptr) = self.dequeue() else {
151
26
            if notify_on_empty {
152
                debug!("Stop driver - no more work items");
153
                // There are no more work items. Notify the driver that it can stop.
154
270
                (self.vtable.stop)(self.data);
155
            }
156
            return false;
157
        };
158

            
159
        // Start processing a new work item.
160

            
161
        debug!("Post new work item to driver");
162
294
        if let Some(poll_status) = (self.vtable.post)(self.data, &mut unsafe { ptr.as_mut() }.data)
163
        {
164
            match poll_status {
165
                Poll::Pending(recall) => {
166
270
                    unsafe { ptr.as_mut().status = Poll::Pending(recall) };
167
270
                    self.current = Some(ptr);
168
                    recall
169
270
                }
170
                Poll::Ready(status) => {
171
                    unsafe { ptr.as_mut() }.complete(status);
172
                    // The driver immediately processed the work item.
173
                    // Polling again needs to dequeue the next item.
174
                    true
175
                }
176
            }
177
        } else {
178
            // If the driver didn't accept the work item, place it back to the front of the
179
            // queue.
180
            self.enqueue_front(ptr);
181
            false
182
        }
183
    }
184

            
185
    /// Pops and returns a work item from the start of the queue.
186
    fn dequeue(&mut self) -> Option<NonNull<WorkItem<T>>> {
187
        // If the `head` is None, the queue is empty. Return None and do nothing.
188
294
        let ptr = self.head?;
189

            
190
294
        self.head = unsafe { ptr.as_ref() }.next;
191

            
192
        // If the new `head` is null, the queue is empty. Clear the `tail` pointer.
193
294
        if self.head.is_none() {
194
293
            self.tail = None;
195
        }
196

            
197
        Some(ptr)
198
    }
199

            
200
    /// Cancels a particular work item.
201
    ///
202
    /// If the work item is currently being processed, this function notifies the driver. Otherwise,
203
    /// it tries to remove the pointer from the work queue.
204
    ///
205
    /// Returns whether the item was immediately cancelled.
206
    ///
207
    /// Not `unsafe` because it only dereferences `work_item` if the item belongs
208
    /// to this queue.
209
    fn cancel(&mut self, mut work_item: NonNull<WorkItem<T>>) -> bool {
210
        if self.current == Some(work_item) {
211
            // Cancelling an in-progress item is more complicated than plucking it from the
212
            // queue. Forward the request to the driver to (maybe) cancel the
213
            // operation.
214
            (self.vtable.cancel)(
215
                self.data,
216
                // This is safe to do, because the work item is currently owned by this queue.
217
                &mut unsafe { work_item.as_mut() }.data,
218
            );
219
            // Queue will need to be polled to query item status.
220
            return false;
221
        }
222

            
223
294
        if unsafe { work_item.as_ref() }.status.is_ready() {
224
            // Nothing to do.
225
            return true;
226
        }
227

            
228
        // The work item is not the current one, remove it from the queue. This immediately
229
        // cancels the work item. `remove` only uses the address of the work item without
230
        // dereferencing it.
231
        if self.remove(work_item) {
232
            unsafe { work_item.as_mut() }.complete(Status::Cancelled);
233
            // Cancelled immediately, no further polling necessary for this item.
234
            return true;
235
        }
236

            
237
        // In this case the item doesn't belong to this queue, it can be in any state. The item will
238
        // need to be polled, but this also means something may have gone wrong.
239
        false
240
    }
241

            
242
    /// Removes the item from the queue.
243
    ///
244
    /// Returns whether the work item was successfully removed.
245
    ///
246
    /// Returns `false` if the work item was not found in the queue.
247
    ///
248
    /// Not `unsafe` because it does not dereference `ptr`, so it does not matter
249
    /// that `ptr` may belong to a different work queue
250
    fn remove(&mut self, ptr: NonNull<WorkItem<T>>) -> bool {
251
        // Walk the queue to find `ptr`.
252
        let mut prev = None;
253
1
        let mut current = self.head;
254
1
        while let Some(current_item) = current {
255
1
            let next = unsafe { current_item.as_ref() }.next;
256

            
257
1
            if current_item != ptr {
258
                // Not what we're looking for. Move to the next element.
259
                prev = current;
260
                current = next;
261
                continue;
262
            }
263

            
264
            // We've found `ptr`. Remove it from the list.
265
            if Some(ptr) == self.head {
266
1
                self.head = next;
267
            } else {
268
                // Unwrapping is fine, because if the current pointer is not the `head`, the
269
                // previous pointer must be Some.
270
                unsafe { unwrap!(prev).as_mut() }.next = next;
271
            }
272

            
273
            if Some(ptr) == self.tail {
274
                self.tail = prev;
275
            }
276

            
277
            return true;
278
        }
279

            
280
        // Did not find `ptr`.
281
        false
282
    }
283

            
284
    /// Increases the suspend counter, preventing new work items from starting to be processed.
285
    ///
286
    /// If the current work item finishes processing, the driver is shut down. Call `is_active` to
287
    /// determine when the queue enters suspended state.
288
    fn suspend(&mut self, ctx: Option<&Context<'_>>) {
289
        self.suspend_count += 1;
290
        if let Some(ctx) = ctx {
291
            if self.current.is_some() {
292
                self.suspend_waker.register(ctx.waker());
293
            } else {
294
                ctx.waker().wake_by_ref();
295
            }
296
        }
297
    }
298

            
299
    /// Decreases the suspend counter.
300
    ///
301
    /// When it reaches 0, this function wakes async tasks that poll the queue. They need to be
302
    /// waken to ensure that their items do not end up stuck. Blocking pollers eventually end up
303
    /// looping when their turn comes.
304
    fn resume(&mut self) {
305
        self.suspend_count -= 1;
306
        if self.suspend_count == 0 {
307
            self.wake_polling_tasks();
308
        }
309
    }
310

            
311
    fn wake_polling_tasks(&mut self) {
312
        if self.data == NonNull::dangling() {
313
            // No VTable means no driver, no need to continue processing.
314
            return;
315
        }
316
        // Walk through the list and wake polling tasks.
317
        let mut current = self.head;
318
5
        while let Some(mut current_item) = current {
319
            let item = unsafe { current_item.as_mut() };
320

            
321
5
            item.waker.wake();
322

            
323
            current = item.next;
324
        }
325
    }
326

            
327
    fn is_active(&self) -> bool {
328
        self.current.is_some()
329
    }
330

            
331
    unsafe fn configure(&mut self, data: NonNull<()>, vtable: VTable<T>) {
332
9
        (self.vtable.stop)(self.data);
333

            
334
9
        self.data = data;
335
9
        self.vtable = vtable;
336

            
337
5
        if self.suspend_count == 0 {
338
            self.wake_polling_tasks();
339
        }
340
    }
341
}
342

            
343
/// A generic work queue.
344
pub(crate) struct WorkQueue<T: Sync + Send> {
345
    inner: NonReentrantMutex<Inner<T>>,
346
}
347

            
348
impl<T: Sync + Send> WorkQueue<T> {
349
    /// Creates a new `WorkQueue`.
350
    pub const fn new() -> Self {
351
        Self {
352
            inner: NonReentrantMutex::new(Inner {
353
                head: None,
354
                tail: None,
355
                current: None,
356

            
357
                data: NonNull::dangling(),
358
                vtable: VTable::noop(),
359

            
360
                suspend_count: 0,
361
                suspend_waker: WakerRegistration::new(),
362
            }),
363
        }
364
    }
365

            
366
    /// Configures the queue.
367
    ///
368
    /// The provided data pointer will be passed to the VTable functions.
369
    ///
370
    /// # Safety
371
    ///
372
    /// The `data` pointer must be valid as long as the `WorkQueue` is configured with it. The
373
    /// driver must access the data pointer appropriately (i.e. it must not move !Send data out of
374
    /// it).
375
    pub unsafe fn configure<D: Sync + Send>(&self, data: NonNull<D>, vtable: VTable<T>) {
376
        self.inner
377
            .with(|inner| unsafe { inner.configure(data.cast(), vtable) })
378
    }
379

            
380
    /// Enqueues a work item.
381
    pub fn post_work<'t>(&'t self, work_item: &'t mut WorkItem<T>) -> Handle<'t, T> {
382
        let ptr = unsafe {
383
            // Safety: `Handle` and `work_item` have lifetime 't, which ensures this call
384
            // can't be called on an in-flight work item. As `Handle` (and the underlying driver
385
            // that processed the work queue) does not use the reference, and using the
386
            // reference is not possible while `Handle` exists, this should be safe.
387
            work_item.prepare()
388
        };
389

            
390
        self.inner.with(|inner| inner.enqueue(ptr));
391

            
392
        Handle {
393
            queue: self,
394
            work_item: ptr,
395
            _marker: PhantomData,
396
        }
397
    }
398

            
399
    /// Polls the queue once.
400
    ///
401
    /// Returns whether the queue needs to be polled again.
402
    #[allow(unused)]
403
    pub fn process(&self) -> bool {
404
        self.inner.with(|inner| inner.process())
405
    }
406

            
407
    /// Polls the queue once and returns the status of the given work item.
408
    ///
409
    /// # Safety
410
    ///
411
    /// The caller must ensure that `item` belongs to the polled queue. An item belongs to the
412
    /// **last queue it was enqueued in**, even if the item is no longer in the queue's linked
413
    /// list. This relationship is broken when the Handle that owns the WorkItem is dropped.
414
    pub unsafe fn poll(&self, item: NonNull<WorkItem<T>>) -> Poll {
415
62
        self.inner.with(|inner| {
416
565
            let status = unsafe { &*item.as_ptr() }.status;
417
565
            if status.is_pending() {
418
                inner.process();
419
                unsafe { &*item.as_ptr() }.status
420
            } else {
421
                status
422
            }
423
        })
424
    }
425

            
426
    /// Schedules the work item to be cancelled.
427
    ///
428
    /// Returns whether the item was immediately cancelled. If it returns
429
    /// `false`, the item will need to be polled until its status becomes [`Poll::Ready`].
430
    ///
431
    /// The work item should not be assumed to be immediately cancelled. Polling its handle
432
    /// is necessary to ensure it is no longer being processed by the underlying driver.
433
    pub fn cancel(&self, work_item: NonNull<WorkItem<T>>) -> bool {
434
        self.inner.with(|inner| inner.cancel(work_item))
435
    }
436
}
437

            
438
/// The status of a work item.
439
#[derive(Clone, Copy, PartialEq, Debug)]
440
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
441
pub enum Status {
442
    /// The processing has completed.
443
    Completed,
444

            
445
    /// The work item has been cancelled.
446
    Cancelled,
447
}
448

            
449
/// A unit of work in the work queue.
450
pub(crate) struct WorkItem<T: Sync + Send> {
451
    next: Option<NonNull<WorkItem<T>>>,
452
    status: Poll,
453
    data: T,
454
    waker: WakerRegistration,
455
}
456

            
457
impl<T: Sync + Send + Clone> Clone for WorkItem<T> {
458
    fn clone(&self) -> Self {
459
        Self {
460
            // A work item can only be cloned when it's not in a queue.
461
            next: None,
462
            status: Poll::Pending(false),
463
            data: self.data.clone(),
464
            waker: WakerRegistration::new(),
465
        }
466
    }
467
}
468

            
469
impl<T: Sync + Send> WorkItem<T> {
470
    /// Completes the work item.
471
    ///
472
    /// Intended to be called from the underlying drivers.
473
    pub fn complete(&mut self, status: Status) {
474
270
        self.status = Poll::Ready(status);
475
270
        self.waker.wake();
476
    }
477

            
478
    /// Prepares a work item to be enqueued.
479
    ///
480
    /// # Safety
481
    ///
482
    /// The caller must ensure the reference is not used again while the pointer returned by this
483
    /// function is in use.
484
    unsafe fn prepare(&mut self) -> NonNull<Self> {
485
148
        self.next = None;
486
148
        self.status = Poll::Pending(false);
487

            
488
        NonNull::from(self)
489
    }
490
}
491

            
492
/// The status of a work item posted to a work queue.
493
#[derive(Clone, Copy, PartialEq, Debug)]
494
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
495
pub(crate) enum Poll {
496
    /// The work item has not yet been fully processed. Contains whether the caller should poll the
497
    /// queue again. This only has effect on async pollers, which will need to wake their tasks
498
    /// immediately.
499
    Pending(bool),
500

            
501
    /// The work item has been processed.
502
    Ready(Status),
503
}
504

            
505
impl Poll {
506
    /// Returns whether the current result is still pending.
507
    pub fn is_pending(self) -> bool {
508
        matches!(self, Self::Pending(_))
509
    }
510

            
511
    /// Returns whether the current result is ready.
512
    pub fn is_ready(self) -> bool {
513
        !self.is_pending()
514
    }
515
}
516

            
517
/// A reference to a posted [`WorkItem`].
518
///
519
/// Ensures that the work item is valid until the item is processed or is removed from
520
/// the work queue.
521
///
522
/// Dropping the handle cancels the work item, but may block for some time if the work item is
523
/// already being processed.
524
pub(crate) struct Handle<'t, T: Sync + Send> {
525
    queue: &'t WorkQueue<T>,
526
    work_item: NonNull<WorkItem<T>>,
527
    // Make sure lifetime is invariant to prevent UB.
528
    _marker: PhantomData<&'t mut WorkItem<T>>,
529
}
530

            
531
impl<'t, T: Sync + Send> Handle<'t, T> {
532
    #[cfg(sha_driver_supported)]
533
    pub(crate) fn from_completed_work_item(
534
        queue: &'t WorkQueue<T>,
535
        work_item: &'t mut WorkItem<T>,
536
    ) -> Self {
537
        // Don't use `complete` here, we don't need to wake anything, just ensure that the item will
538
        // not be put into the queue.
539
6
        work_item.status = Poll::Ready(Status::Completed);
540

            
541
        Self {
542
            queue,
543
            work_item: NonNull::from(work_item),
544
            _marker: PhantomData,
545
        }
546
    }
547

            
548
    fn poll_inner(&mut self) -> Poll {
549
4
        unsafe { self.queue.poll(self.work_item) }
550
    }
551

            
552
    /// Returns the status of the work item.
553
    pub fn poll(&mut self) -> bool {
554
        self.poll_inner().is_ready()
555
    }
556

            
557
    /// Polls the work item to completion, by busy-looping.
558
    ///
559
    /// Returns immediately if `poll` returns `true`.
560
    #[inline]
561
    pub fn wait_blocking(mut self) -> Status {
562
        loop {
563
62
            if let Poll::Ready(status) = self.poll_inner() {
564
                return status;
565
            }
566
        }
567
32
    }
568

            
569
    /// Waits until the work item is completed.
570
    pub fn wait(&mut self) -> impl Future<Output = Status> {
571
        poll_fn(|ctx| {
572
4
            unsafe { self.work_item.as_mut() }
573
                .waker
574
4
                .register(ctx.waker());
575
4
            match self.poll_inner() {
576
3
                Poll::Pending(recall) => {
577
3
                    if recall {
578
                        ctx.waker().wake_by_ref();
579
                    }
580
                    core::task::Poll::Pending
581
                }
582
                Poll::Ready(status) => core::task::Poll::Ready(status),
583
            }
584
        })
585
    }
586

            
587
    /// Cancels the work item and asynchronously waits until it is removed from the work queue.
588
    pub async fn cancel(&mut self) {
589
        if !self.queue.cancel(self.work_item) {
590
            self.wait().await;
591
        }
592
    }
593
}
594

            
595
impl<'t, T: Sync + Send> Drop for Handle<'t, T> {
596
    fn drop(&mut self) {
597
294
        if !self.queue.cancel(self.work_item) {
598
            // We must wait for the driver to release our WorkItem.
599
            while self.poll_inner().is_pending() {}
600
        }
601
    }
602
}
603

            
604
pub(crate) struct WorkQueueDriver<'t, D, T>
605
where
606
    D: Sync + Send,
607
    T: Sync + Send,
608
{
609
    queue: &'t WorkQueue<T>,
610
    _marker: PhantomData<&'t mut D>,
611
}
612

            
613
impl<'t, D, T> WorkQueueDriver<'t, D, T>
614
where
615
    D: Sync + Send,
616
    T: Sync + Send,
617
{
618
    pub fn new(driver: &'t mut D, vtable: VTable<T>, queue: &'t WorkQueue<T>) -> Self {
619
        unsafe {
620
            // Safety: the lifetime 't ensures the pointer remains valid for the lifetime of the
621
            // WorkQueueDriver. The Drop implementation (and the general "Don't forget" clause)
622
            // ensure the pointer is not used after the WQD has been dropped.
623
            queue.configure(NonNull::from(driver), vtable);
624
        }
625
        Self {
626
            queue,
627
            _marker: PhantomData,
628
        }
629
    }
630

            
631
    /// Shuts down the driver.
632
    pub fn stop(self) -> impl Future<Output = ()> {
633
        let mut suspended = false;
634
        poll_fn(move |ctx| {
635
            self.queue.inner.with(|inner| {
636
                if !inner.is_active() {
637
                    unsafe {
638
                        // Safety: the noop VTable functions don't use the pointer at all.
639
                        self.queue
640
                            .configure(NonNull::<D>::dangling(), VTable::noop())
641
                    };
642
                    // Make sure the queue doesn't remain suspended when the driver is re-started.
643
                    if suspended {
644
                        inner.resume();
645
                    }
646
                    return core::task::Poll::Ready(());
647
                }
648
                // This may kick out other suspend() callers, but that should be okay. They will
649
                // only be able to do work if the queue is !active, for them it doesn't matter if
650
                // the queue is suspended or stopped completely - just that it isn't running. As for
651
                // the possible waker churn, we can use MultiWakerRegistration with a capacity
652
                // suitable for the number of possible suspenders (2-3 unless the work queue ends up
653
                // being used more widely), if this turns out to be a problem.
654
                inner.suspend_waker.register(ctx.waker());
655
                if !suspended {
656
                    inner.suspend(Some(ctx));
657
                    suspended = true;
658
                }
659

            
660
                core::task::Poll::Pending
661
            })
662
        })
663
    }
664
}
665

            
666
impl<D, T> Drop for WorkQueueDriver<'_, D, T>
667
where
668
    D: Sync + Send,
669
    T: Sync + Send,
670
{
671
    fn drop(&mut self) {
672
        let wait_for_suspended = self.queue.inner.with(|inner| {
673
9
            if inner.is_active() {
674
                inner.suspend(None);
675
                true
676
            } else {
677
                unsafe { inner.configure(NonNull::dangling(), VTable::noop()) };
678
                false
679
            }
680
        });
681

            
682
9
        if !wait_for_suspended {
683
            return;
684
        }
685

            
686
        loop {
687
            let done = self.queue.inner.with(|inner| {
688
                if inner.is_active() {
689
                    return false;
690
                }
691

            
692
                unsafe { inner.configure(NonNull::dangling(), VTable::noop()) };
693

            
694
                inner.resume();
695

            
696
                true
697
            });
698
            if done {
699
                break;
700
            }
701
        }
702
    }
703
}
704

            
705
/// Used by work queue clients, allows hiding WorkItem.
706
#[derive(Clone)]
707
pub(crate) struct WorkQueueFrontend<T: Sync + Send> {
708
    work_item: WorkItem<T>,
709
}
710

            
711
impl<T: Sync + Send> WorkQueueFrontend<T> {
712
    /// Creates a new work queue frontend with the given initial value.
713
    pub fn new(initial: T) -> Self {
714
        Self {
715
            work_item: WorkItem {
716
                next: None,
717
                status: Poll::Pending(false),
718
                data: initial,
719
                waker: WakerRegistration::new(),
720
            },
721
        }
722
    }
723

            
724
    /// Returns a reference to the work item's data.
725
    #[cfg(ecc_driver_supported)]
726
    pub fn data(&self) -> &T {
727
        &self.work_item.data
728
    }
729

            
730
    /// Returns a mutable reference to the work item's data.
731
    pub fn data_mut(&mut self) -> &mut T {
732
        &mut self.work_item.data
733
    }
734

            
735
    /// Posts the work item to the queue.
736
    ///
737
    /// Returns a [`Handle`] that can be used to wait for the work item to complete.
738
    pub fn post<'t>(&'t mut self, queue: &'t WorkQueue<T>) -> Handle<'t, T> {
739
        queue.post_work(&mut self.work_item)
740
    }
741

            
742
    /// Creates a new [`Handle`] for a work item that does not need to be put into the queue.
743
    #[cfg(sha_driver_supported)]
744
    pub fn post_completed<'t>(&'t mut self, queue: &'t WorkQueue<T>) -> Handle<'t, T> {
745
        Handle::from_completed_work_item(queue, &mut self.work_item)
746
    }
747
}