Single-threaded Waker for embedded usage

I'm writing a Future executor (crates.io, docs) for my single-threaded, embedded target. Unlike most other implementations, I have no threads or interrupts, so there is no need for synchronization. Currently, I'm using the unstable Wake trait like this:

struct TaskWaker {
    wake_marker: AtomicBool,
}

impl TaskWaker {
    fn wake_task(&self) {
        self.wake_marker.store(true, Ordering::Relaxed);
    }
}

impl Wake for TaskWaker {
    fn wake(self: Arc<Self>) {
        self.wake_task();
    }

    fn wake_by_ref(self: &Arc<Self>) {
        self.wake_task();
    }
}

/// Spawns a task and blocks until the future resolves, returning its result.
pub fn block_on<T>(listeners: &AsyncListeners, task: impl Future<Output = T>) -> T {
    let wake_marker = Arc::new(TaskWaker {
         wake_marker: AtomicBool::new(true),
    });
    let waker = Waker::from(wake_marker.clone());
    let mut context = Context::from_waker(&waker);
    pin_mut!(task);
    let mut task = task;
    // Poll it, look at the GitHub for the full source
    unimplemented!()
}

Is there a more efficient way to do this? My target, which is ARMv5TE, doesn't have atomic instructions. I don't know if this means that Arc acts as an Rc or if it emulates atomics itself, which would be unnecessary or slow.

I'm thinking that I should just reimplement this using RawWaker and Rc. Are there any examples or crates doing this? (I would assume not, I haven't seen any other executors that don't use threads or interrupts)

I don't know what atomics do on your device, but if you have no threading, you could use UnsafeCell<bool> to interact with it. Reimplementing the arc-waker thing to use Rc should not be super difficult.

Are you saying that your target does not support interrupts at all? A user could take your waker and move it into an interrupt handler and wake it from there, it’s not about whether your executor uses threads/interrupts, but whether it’s at all possible for an arbitrary Future impl to use them.

1 Like

Ah, I didn't realize that Waker must be Send and Sync, so I guess I'll keep it that way. The OS that my target uses takes control over interrupts, and doesn't provide an interface for receiving callbacks, and I've told Rust as such by adding "singlethread": true attribute to my target.json. I guess now that I think about it Arc and AtomicBool are probably getting optimized to regular Rcs and bools because of that.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.