-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test cases for forced unwinding.
- Loading branch information
Showing
12 changed files
with
458 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
name: Run Tests for Task Unwinding | ||
|
||
on: | ||
workflow_call: | ||
secrets: | ||
cookie: | ||
required: true | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
|
||
jobs: | ||
diverted: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Run test diverted | ||
uses: ./.github/workflows/actions/run-test | ||
with: | ||
cookie: ${{ secrets.cookie }} | ||
category: task | ||
sub-category: unwind | ||
test-name: diverted | ||
|
||
deferred_direct_drop: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Run test deferred_direct_drop | ||
uses: ./.github/workflows/actions/run-test | ||
with: | ||
cookie: ${{ secrets.cookie }} | ||
category: task | ||
sub-category: unwind | ||
test-name: deferred_direct_drop | ||
|
||
deferred_indirect_drop: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Run test deferred_indirect_drop | ||
uses: ./.github/workflows/actions/run-test | ||
with: | ||
cookie: ${{ secrets.cookie }} | ||
category: task | ||
sub-category: unwind | ||
test-name: deferred_indirect_drop | ||
|
||
deferred_nested_drop: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Run test deferred_nested_drop | ||
uses: ./.github/workflows/actions/run-test | ||
with: | ||
cookie: ${{ secrets.cookie }} | ||
category: task | ||
sub-category: unwind | ||
test-name: deferred_nested_drop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
//! A deferred forced unwinding should occur when a drop handler function | ||
//! overflows the call stack of a task that does not enable dynamic stack | ||
//! extension. | ||
#![no_std] | ||
#![no_main] | ||
|
||
extern crate alloc; | ||
use core::{ | ||
mem::MaybeUninit, | ||
sync::atomic::{AtomicUsize, Ordering}, | ||
}; | ||
use hopter::{boot::main, debug::semihosting, hprintln, task}; | ||
|
||
#[main] | ||
fn main(_: cortex_m::Peripherals) { | ||
task::build() | ||
.set_entry(test_task) | ||
.deny_dynamic_stack() | ||
.set_stack_size(512) | ||
.spawn_restartable() | ||
.unwrap(); | ||
} | ||
|
||
fn test_task() { | ||
// A persistent counter. | ||
static CNT: AtomicUsize = AtomicUsize::new(0); | ||
|
||
// Every time the task runs we increment it by 1. | ||
let cnt = CNT.fetch_add(1, Ordering::SeqCst); | ||
|
||
// When the task is executed for the first time, run the drop function. | ||
// Even if this drop function uses a large stack frame and will overflow | ||
// the task's stack while dynamic stack extension is not enabled, the | ||
// segmented stack runtime should still allow the drop function to proceed | ||
// because we cannot initiate an unwinding inside a drop function. A | ||
// deferred forced unwinding will be executed after the drop handler | ||
// finishes. | ||
if cnt == 0 { | ||
core::mem::drop(HasDrop); | ||
} | ||
|
||
if cnt == 0 { | ||
// The task should have been unwound so this print should not be | ||
// reachable. | ||
hprintln!("Should not print this."); | ||
} | ||
|
||
if cnt > 0 { | ||
hprintln!("Task successfully restarted after a deferred forced unwinding."); | ||
semihosting::terminate(true); | ||
} else { | ||
semihosting::terminate(false); | ||
} | ||
} | ||
|
||
struct HasDrop; | ||
|
||
// A drop function that uses a large stack frame. | ||
impl Drop for HasDrop { | ||
#[inline(never)] | ||
fn drop(&mut self) { | ||
let _padding = StackFramePadding::new(); | ||
hprintln!("Drop executed."); | ||
} | ||
} | ||
|
||
/// A padding that causes large stack frame. | ||
struct StackFramePadding { | ||
_padding: [u8; 1024], | ||
} | ||
|
||
impl StackFramePadding { | ||
/// Use volatile write to prevent the compiler from optimizing away the | ||
/// padding. | ||
fn new() -> Self { | ||
let mut padding = MaybeUninit::<[u8; 1024]>::uninit(); | ||
let mut ptr = unsafe { (*padding.as_mut_ptr()).as_mut_ptr() }; | ||
for _ in 0..1024 { | ||
unsafe { | ||
ptr.write_volatile(0); | ||
ptr = ptr.offset(1); | ||
} | ||
} | ||
let padding = unsafe { padding.assume_init() }; | ||
Self { _padding: padding } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Drop executed. | ||
Task successfully restarted after a deferred forced unwinding. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
//! A deferred forced unwinding should occur when a drop handler function | ||
//! is active in the call stack and it further calls a function that overflows | ||
//! the stack, and when the task does not enable dynamic stack extension. | ||
#![no_std] | ||
#![no_main] | ||
|
||
extern crate alloc; | ||
use core::{ | ||
mem::MaybeUninit, | ||
sync::atomic::{AtomicUsize, Ordering}, | ||
}; | ||
use hopter::{boot::main, debug::semihosting, hprintln, task}; | ||
|
||
#[main] | ||
fn main(_: cortex_m::Peripherals) { | ||
task::build() | ||
.set_entry(test_task) | ||
.deny_dynamic_stack() | ||
.set_stack_size(512) | ||
.spawn_restartable() | ||
.unwrap(); | ||
} | ||
|
||
fn test_task() { | ||
// A persistent counter. | ||
static CNT: AtomicUsize = AtomicUsize::new(0); | ||
|
||
// Every time the task runs we increment it by 1. | ||
let cnt = CNT.fetch_add(1, Ordering::SeqCst); | ||
|
||
// When the task is executed for the first time, run the drop function. | ||
// Even if this drop function uses a large stack frame and will overflow | ||
// the task's stack while dynamic stack extension is not enabled, the | ||
// segmented stack runtime should still allow the drop function to proceed | ||
// because we cannot initiate an unwinding inside a drop function. A | ||
// deferred forced unwinding will be executed after the drop handler | ||
// finishes. | ||
if cnt == 0 { | ||
core::mem::drop(HasDrop); | ||
} | ||
|
||
if cnt == 0 { | ||
// The task should have been unwound so this print should not be | ||
// reachable. | ||
hprintln!("Should not print this."); | ||
} | ||
|
||
if cnt > 0 { | ||
hprintln!("Task successfully restarted after a deferred forced unwinding."); | ||
semihosting::terminate(true); | ||
} else { | ||
semihosting::terminate(false); | ||
} | ||
} | ||
|
||
#[inline(never)] | ||
fn large_func() { | ||
let _padding = StackFramePadding::new(); | ||
hprintln!("Large function executed."); | ||
} | ||
|
||
struct HasDrop; | ||
|
||
// A drop function that uses a large stack frame. | ||
impl Drop for HasDrop { | ||
#[inline(never)] | ||
fn drop(&mut self) { | ||
large_func(); | ||
hprintln!("Drop executed."); | ||
} | ||
} | ||
|
||
/// A padding that causes large stack frame. | ||
struct StackFramePadding { | ||
_padding: [u8; 1024], | ||
} | ||
|
||
impl StackFramePadding { | ||
/// Use volatile write to prevent the compiler from optimizing away the | ||
/// padding. | ||
fn new() -> Self { | ||
let mut padding = MaybeUninit::<[u8; 1024]>::uninit(); | ||
let mut ptr = unsafe { (*padding.as_mut_ptr()).as_mut_ptr() }; | ||
for _ in 0..1024 { | ||
unsafe { | ||
ptr.write_volatile(0); | ||
ptr = ptr.offset(1); | ||
} | ||
} | ||
let padding = unsafe { padding.assume_init() }; | ||
Self { _padding: padding } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Large function executed. | ||
Drop executed. | ||
Task successfully restarted after a deferred forced unwinding. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
//! A deferred forced unwinding should occur when a drop handler function | ||
//! overflows the call stack of a task that does not enable dynamic stack | ||
//! extension. This test further tests the case where the drop handler is | ||
//! nested inside another drop handler. The forced unwinding should begin | ||
//! only after the return of the outmost drop handler finishes. | ||
#![no_std] | ||
#![no_main] | ||
|
||
extern crate alloc; | ||
use core::{ | ||
mem::MaybeUninit, | ||
sync::atomic::{AtomicUsize, Ordering}, | ||
}; | ||
use hopter::{boot::main, debug::semihosting, hprintln, task}; | ||
|
||
#[main] | ||
fn main(_: cortex_m::Peripherals) { | ||
task::build() | ||
.set_entry(test_task) | ||
.deny_dynamic_stack() | ||
.set_stack_size(512) | ||
.spawn_restartable() | ||
.unwrap(); | ||
} | ||
|
||
fn test_task() { | ||
// A persistent counter. | ||
static CNT: AtomicUsize = AtomicUsize::new(0); | ||
|
||
// Every time the task runs we increment it by 1. | ||
let cnt = CNT.fetch_add(1, Ordering::SeqCst); | ||
|
||
// When the task is executed for the first time, run the drop function. | ||
// Even if this drop function uses a large stack frame and will overflow | ||
// the task's stack while dynamic stack extension is not enabled, the | ||
// segmented stack runtime should still allow the drop function to proceed | ||
// because we cannot initiate an unwinding inside a drop function. A | ||
// deferred forced unwinding will be executed after the drop handler | ||
// finishes. | ||
if cnt == 0 { | ||
core::mem::drop(OuterDrop(InnerDrop)); | ||
} | ||
|
||
if cnt == 0 { | ||
// The task should have been unwound so this print should not be | ||
// reachable. | ||
hprintln!("Should not print this."); | ||
} | ||
|
||
if cnt > 0 { | ||
hprintln!("Task successfully restarted after a deferred forced unwinding."); | ||
semihosting::terminate(true); | ||
} else { | ||
semihosting::terminate(false); | ||
} | ||
} | ||
|
||
struct OuterDrop(InnerDrop); | ||
|
||
// Outter drop implicitly also invokes inner drop. | ||
impl Drop for OuterDrop { | ||
#[inline(never)] | ||
fn drop(&mut self) { | ||
hprintln!("Outter drop executed."); | ||
} | ||
} | ||
|
||
struct InnerDrop; | ||
|
||
// A drop function that uses a large stack frame. | ||
impl Drop for InnerDrop { | ||
#[inline(never)] | ||
fn drop(&mut self) { | ||
let _padding = StackFramePadding::new(); | ||
hprintln!("Inner drop executed."); | ||
} | ||
} | ||
|
||
/// A padding that causes large stack frame. | ||
struct StackFramePadding { | ||
_padding: [u8; 1024], | ||
} | ||
|
||
impl StackFramePadding { | ||
/// Use volatile write to prevent the compiler from optimizing away the | ||
/// padding. | ||
fn new() -> Self { | ||
let mut padding = MaybeUninit::<[u8; 1024]>::uninit(); | ||
let mut ptr = unsafe { (*padding.as_mut_ptr()).as_mut_ptr() }; | ||
for _ in 0..1024 { | ||
unsafe { | ||
ptr.write_volatile(0); | ||
ptr = ptr.offset(1); | ||
} | ||
} | ||
let padding = unsafe { padding.assume_init() }; | ||
Self { _padding: padding } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Outter drop executed. | ||
Inner drop executed. | ||
Task successfully restarted after a deferred forced unwinding. |
Oops, something went wrong.