esp_wifi/preempt_builtin/
mod.rs

1#[cfg_attr(target_arch = "riscv32", path = "preempt_riscv.rs")]
2#[cfg_attr(target_arch = "xtensa", path = "preempt_xtensa.rs")]
3mod arch_specific;
4pub mod timer;
5
6use core::{ffi::c_void, mem::size_of};
7
8use arch_specific::*;
9use esp_hal::sync::Locked;
10use esp_wifi_sys::include::malloc;
11use timer::{disable_multitasking, setup_multitasking};
12pub(crate) use timer::{disable_timer, setup_timer};
13
14use crate::{compat::malloc::free, hal::trapframe::TrapFrame, memory_fence::memory_fence};
15
16#[repr(transparent)]
17struct ContextWrapper(*mut Context);
18
19unsafe impl Send for ContextWrapper {}
20
21static CTX_NOW: Locked<ContextWrapper> = Locked::new(ContextWrapper(core::ptr::null_mut()));
22
23static mut SCHEDULED_TASK_TO_DELETE: *mut Context = core::ptr::null_mut();
24
25use crate::preempt::Scheduler;
26
27struct BuiltinScheduler {}
28
29crate::scheduler_impl!(static SCHEDULER: BuiltinScheduler = BuiltinScheduler {});
30
31impl Scheduler for BuiltinScheduler {
32    fn enable(&self) {
33        // allocate the main task
34        allocate_main_task();
35        setup_multitasking();
36    }
37
38    fn disable(&self) {
39        disable_multitasking();
40        delete_all_tasks();
41
42        timer::TIMER.with(|timer| timer.take());
43    }
44
45    fn yield_task(&self) {
46        timer::yield_task()
47    }
48
49    fn task_create(
50        &self,
51        task: extern "C" fn(*mut c_void),
52        param: *mut c_void,
53        task_stack_size: usize,
54    ) -> *mut c_void {
55        arch_specific::task_create(task, param, task_stack_size) as *mut c_void
56    }
57
58    fn current_task(&self) -> *mut c_void {
59        current_task() as *mut c_void
60    }
61
62    fn schedule_task_deletion(&self, task_handle: *mut c_void) {
63        schedule_task_deletion(task_handle as *mut Context)
64    }
65
66    fn current_task_thread_semaphore(&self) -> *mut crate::binary::c_types::c_void {
67        unsafe {
68            &mut ((*current_task()).thread_semaphore) as *mut _
69                as *mut crate::binary::c_types::c_void
70        }
71    }
72}
73
74fn allocate_main_task() -> *mut Context {
75    CTX_NOW.with(|ctx_now| unsafe {
76        if !ctx_now.0.is_null() {
77            panic!("Tried to allocate main task multiple times");
78        }
79
80        let ptr = malloc(size_of::<Context>() as u32) as *mut Context;
81        core::ptr::write(ptr, Context::new());
82        (*ptr).next = ptr;
83        ctx_now.0 = ptr;
84        ptr
85    })
86}
87
88fn allocate_task() -> *mut Context {
89    CTX_NOW.with(|ctx_now| unsafe {
90        if ctx_now.0.is_null() {
91            panic!("Called `allocate_task` before allocating main task");
92        }
93
94        let ptr = malloc(size_of::<Context>() as u32) as *mut Context;
95        core::ptr::write(ptr, Context::new());
96        (*ptr).next = (*ctx_now.0).next;
97        (*ctx_now.0).next = ptr;
98        ptr
99    })
100}
101
102fn next_task() {
103    CTX_NOW.with(|ctx_now| unsafe {
104        ctx_now.0 = (*ctx_now.0).next;
105    });
106}
107
108/// Delete the given task.
109///
110/// This will also free the memory (stack and context) allocated for it.
111fn delete_task(task: *mut Context) {
112    CTX_NOW.with(|ctx_now| unsafe {
113        let mut ptr = ctx_now.0;
114        let initial = ptr;
115        loop {
116            if (*ptr).next == task {
117                (*ptr).next = (*((*ptr).next)).next;
118
119                free((*task).allocated_stack as *mut u8);
120                free(task as *mut u8);
121                break;
122            }
123
124            ptr = (*ptr).next;
125
126            if ptr == initial {
127                break;
128            }
129        }
130
131        memory_fence();
132    });
133}
134
135fn delete_all_tasks() {
136    CTX_NOW.with(|ctx_now| unsafe {
137        let current_task = ctx_now.0;
138
139        if current_task.is_null() {
140            return;
141        }
142
143        let mut task_to_delete = current_task;
144
145        loop {
146            let next_task = (*task_to_delete).next;
147
148            free((*task_to_delete).allocated_stack as *mut u8);
149            free(task_to_delete as *mut u8);
150
151            if next_task == current_task {
152                break;
153            }
154
155            task_to_delete = next_task;
156        }
157
158        ctx_now.0 = core::ptr::null_mut();
159
160        memory_fence();
161    });
162}
163
164fn current_task() -> *mut Context {
165    CTX_NOW.with(|ctx_now| ctx_now.0)
166}
167
168fn schedule_task_deletion(task: *mut Context) {
169    unsafe {
170        SCHEDULED_TASK_TO_DELETE = task;
171    }
172
173    if task == current_task() {
174        loop {
175            timer::yield_task();
176        }
177    }
178}
179
180pub(crate) fn task_switch(trap_frame: &mut TrapFrame) {
181    save_task_context(current_task(), trap_frame);
182
183    unsafe {
184        if !SCHEDULED_TASK_TO_DELETE.is_null() {
185            delete_task(SCHEDULED_TASK_TO_DELETE);
186            SCHEDULED_TASK_TO_DELETE = core::ptr::null_mut();
187        }
188    }
189
190    next_task();
191    restore_task_context(current_task(), trap_frame);
192}