Skip to content

Commit 3af006e

Browse files
projectgusdpgeorge
authored andcommitted
rp2: Support calling pendsv_suspend/resume from core 1.
Previously, this was subject to races incrementing/decrementing the counter variable pendsv_lock. Technically, all that's needed here would be to make pendsv_lock an atomic counter. This implementation fulfils a stronger guarantee: it also provides mutual exclusion for the core which calls pendsv_suspend(). This is because the current use of pendsv_suspend/resume in MicroPython is to ensure exclusive access to softtimer data structures, and this does require mutual exclusion. The conceptually cleaner implementation would split the mutual exclusion part out into a softtimer-specific spinlock, but this increases the complexity and doesn't seem like it makes for a better implementation in the long run. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
1 parent 83e82c5 commit 3af006e

File tree

3 files changed

+27
-7
lines changed

3 files changed

+27
-7
lines changed

ports/rp2/main.c

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ int main(int argc, char **argv) {
7676
// This is a tickless port, interrupts should always trigger SEV.
7777
SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
7878

79+
pendsv_init();
7980
soft_timer_init();
8081

8182
#if MICROPY_HW_ENABLE_UART_REPL

ports/rp2/pendsv.c

+25-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626

2727
#include <assert.h>
28+
#include "pico/mutex.h"
2829
#include "py/mpconfig.h"
2930
#include "pendsv.h"
3031
#include "RP2040.h"
@@ -34,15 +35,21 @@
3435
#endif
3536

3637
static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS];
37-
static int pendsv_lock;
38+
static recursive_mutex_t pendsv_mutex;
39+
40+
void pendsv_init(void) {
41+
recursive_mutex_init(&pendsv_mutex);
42+
}
3843

3944
void pendsv_suspend(void) {
40-
pendsv_lock++;
45+
// Recursive Mutex here as either core may call pendsv_suspend() and expect
46+
// both mutual exclusion (other core can't enter pendsv_suspend() at the
47+
// same time), and that no PendSV handler will run.
48+
recursive_mutex_enter_blocking(&pendsv_mutex);
4149
}
4250

4351
void pendsv_resume(void) {
44-
pendsv_lock--;
45-
assert(pendsv_lock >= 0);
52+
recursive_mutex_exit(&pendsv_mutex);
4653
// Run pendsv if needed. Find an entry with a dispatch and call pendsv dispatch
4754
// with it. If pendsv runs it will service all slots.
4855
int count = PENDSV_DISPATCH_NUM_SLOTS;
@@ -55,9 +62,11 @@ void pendsv_resume(void) {
5562
}
5663

5764
void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) {
58-
assert(pendsv_lock >= 0);
5965
pendsv_dispatch_table[slot] = f;
60-
if (pendsv_lock == 0) {
66+
if (pendsv_mutex.enter_count == 0) {
67+
// There is a race here where other core calls pendsv_suspend() before
68+
// ISR can execute, but dispatch will happen later when other core
69+
// calls pendsv_resume().
6170
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
6271
} else {
6372
#if MICROPY_PY_NETWORK_CYW43
@@ -68,7 +77,14 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) {
6877

6978
// PendSV interrupt handler to perform background processing.
7079
void PendSV_Handler(void) {
71-
assert(pendsv_lock == 0);
80+
81+
if (!recursive_mutex_try_enter(&pendsv_mutex, NULL)) {
82+
// Failure here means core 1 holds pendsv_mutex. ISR will
83+
// run again after core 1 calls pendsv_resume().
84+
return;
85+
}
86+
// Core 0 should not already have locked pendsv_mutex
87+
assert(pensv_mutex.enter_count == 1);
7288

7389
#if MICROPY_PY_NETWORK_CYW43
7490
CYW43_STAT_INC(PENDSV_RUN_COUNT);
@@ -81,4 +97,6 @@ void PendSV_Handler(void) {
8197
f();
8298
}
8399
}
100+
101+
recursive_mutex_exit(&pendsv_mutex);
84102
}

ports/rp2/pendsv.h

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum {
4747

4848
typedef void (*pendsv_dispatch_t)(void);
4949

50+
void pendsv_init(void);
5051
void pendsv_suspend(void);
5152
void pendsv_resume(void);
5253
void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f);

0 commit comments

Comments
 (0)