aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-11-15 01:19:32 +0100
committerDario Nieuwenhuis <[email protected]>2023-11-15 18:47:11 +0100
commit27e6634c9d7ddde5e4387416db94bdccf649ac2e (patch)
tree3f1f04e32168a1c46b17c5b76cd4b1c0c230eb0b /embassy-executor
parent1f9b649f80b52323be3e16fff064e1aec77d5ae8 (diff)
executor: add faster ARM-specific impl.
Does a wake+poll in 79 cycles in nrf52840.
Diffstat (limited to 'embassy-executor')
-rw-r--r--embassy-executor/src/raw/mod.rs3
-rw-r--r--embassy-executor/src/raw/state_atomics_arm.rs103
2 files changed, 105 insertions, 1 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index ed0bedd25..e9137f8fa 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -11,7 +11,8 @@
11#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")] 11#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
12mod run_queue; 12mod run_queue;
13 13
14#[cfg_attr(target_has_atomic = "8", path = "state_atomics.rs")] 14#[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")]
15#[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")]
15#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] 16#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
16mod state; 17mod state;
17 18
diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs
new file mode 100644
index 000000000..e4dfe5093
--- /dev/null
+++ b/embassy-executor/src/raw/state_atomics_arm.rs
@@ -0,0 +1,103 @@
1use core::arch::asm;
2use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
3
4// Must be kept in sync with the layout of `State`!
5pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
6pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
7
8#[repr(C, align(4))]
9pub(crate) struct State {
10 /// Task is spawned (has a future)
11 spawned: AtomicBool,
12 /// Task is in the executor run queue
13 run_queued: AtomicBool,
14 /// Task is in the executor timer queue
15 timer_queued: AtomicBool,
16 pad: AtomicBool,
17}
18
19impl State {
20 pub const fn new() -> State {
21 Self {
22 spawned: AtomicBool::new(false),
23 run_queued: AtomicBool::new(false),
24 timer_queued: AtomicBool::new(false),
25 pad: AtomicBool::new(false),
26 }
27 }
28
29 fn as_u32(&self) -> &AtomicU32 {
30 unsafe { &*(self as *const _ as *const AtomicU32) }
31 }
32
33 /// If task is idle, mark it as spawned + run_queued and return true.
34 #[inline(always)]
35 pub fn spawn(&self) -> bool {
36 compiler_fence(Ordering::Release);
37 let r = self
38 .as_u32()
39 .compare_exchange(
40 0,
41 STATE_SPAWNED | STATE_RUN_QUEUED,
42 Ordering::Relaxed,
43 Ordering::Relaxed,
44 )
45 .is_ok();
46 compiler_fence(Ordering::Acquire);
47 r
48 }
49
50 /// Unmark the task as spawned.
51 #[inline(always)]
52 pub fn despawn(&self) {
53 compiler_fence(Ordering::Release);
54 self.spawned.store(false, Ordering::Relaxed);
55 }
56
57 /// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
58 #[inline(always)]
59 pub fn run_enqueue(&self) -> bool {
60 unsafe {
61 loop {
62 let state: u32;
63 asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack));
64
65 if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
66 asm!("clrex", options(nomem, nostack));
67 return false;
68 }
69
70 let outcome: usize;
71 let new_state = state | STATE_RUN_QUEUED;
72 asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack));
73 if outcome == 0 {
74 return true;
75 }
76 }
77 }
78 }
79
80 /// Unmark the task as run-queued. Return whether the task is spawned.
81 #[inline(always)]
82 pub fn run_dequeue(&self) -> bool {
83 compiler_fence(Ordering::Release);
84
85 let r = self.spawned.load(Ordering::Relaxed);
86 self.run_queued.store(false, Ordering::Relaxed);
87 r
88 }
89
90 /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
91 #[cfg(feature = "integrated-timers")]
92 #[inline(always)]
93 pub fn timer_enqueue(&self) -> bool {
94 !self.timer_queued.swap(true, Ordering::Relaxed)
95 }
96
97 /// Unmark the task as timer-queued.
98 #[cfg(feature = "integrated-timers")]
99 #[inline(always)]
100 pub fn timer_dequeue(&self) {
101 self.timer_queued.store(false, Ordering::Relaxed);
102 }
103}