Skip to content

Commit 5b038d4

Browse files
authored
Fix an interrupt safety bug (#164)
1 parent be4ca7f commit 5b038d4

3 files changed

Lines changed: 20 additions & 7 deletions

File tree

stm32-lvgl/Sources/Application/Interrupts.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,23 @@ func SystickTimerISR() {
7070
uptimeInMs += 1
7171
}
7272

73+
// The following interrupt set up is trickier than it looks on the surface. The
74+
// ISR Swift code must be "trivial" directly and transitively, and namely it
75+
// must avoid destroying any heap objects (because we could be inside malloc
76+
// when the interrupt hits). For that, the expectation is that
77+
// lcdInterruptVerticalSyncHandler is only ever set once, and it is not changing
78+
// after boot. The code inside the lcdInterruptVerticalSyncHandler closure is
79+
// expected to only perform trivial operations. lcdInterruptVerticalSyncEnabled
80+
// is allowed to change.
81+
7382
var lcdInterruptVerticalSyncHandler: (() -> Void)? = nil
83+
var lcdInterruptVerticalSyncEnabled: Bool = false
7484

7585
@_cdecl("LtdcIntHandlerISR")
7686
func LtdcIntHandlerISR() {
7787
let sr = ltdc.isr.read()
7888
ltdc.icr.write { $0.storage = sr.storage }
7989
if sr.raw.rrif != 0 {
80-
lcdInterruptVerticalSyncHandler?()
90+
if lcdInterruptVerticalSyncEnabled { lcdInterruptVerticalSyncHandler?() }
8191
}
8292
}

stm32-lvgl/Sources/Application/Main.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,16 @@ struct Main {
123123
// interrupt handler. Once we get our VBI, we can tell
124124
// LVGL that we're good to go to switch again.
125125
Lcd.setFrameBuffer(bufferToShow!)
126-
lcdInterruptVerticalSyncHandler = {
127-
lv_display_flush_ready(disp)
128-
lcdInterruptVerticalSyncHandler = nil
129-
}
126+
lcdInterruptVerticalSyncEnabled = true
130127
Lcd.reloadConfiguration()
131128
// the lv_display_flush_ready() will happen in the LCD frame interrupt.
132129
})
133130

131+
lcdInterruptVerticalSyncHandler = {
132+
lv_display_flush_ready(disp)
133+
lcdInterruptVerticalSyncEnabled = false
134+
}
135+
134136
let touch = lv_indev_create()
135137
lv_indev_set_type(touch, LV_INDEV_TYPE_POINTER)
136138
lv_indev_set_read_cb(
@@ -155,7 +157,7 @@ struct Main {
155157

156158
while true {
157159
// If we're pending a render, wait.
158-
while lcdInterruptVerticalSyncHandler != nil { /* busy wait */ nop() }
160+
while lcdInterruptVerticalSyncEnabled { /* busy wait */ nop() }
159161

160162
lv_timer_handler()
161163

stm32-lvgl/toolset.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"-llvgl", "-llvgl_demos",
3838
"-static",
3939
"-e", "_start_elf",
40-
"--orphan-handling=error"
40+
"--orphan-handling=error",
41+
"-Map", ".build/armv7em-none-none-eabi/release/Application.map"
4142
]
4243
}
4344
}

0 commit comments

Comments
 (0)