Skip to content

Commit 11c2c3a

Browse files
authored
add test (#343)
2 parents e4ad8ee + e5b243d commit 11c2c3a

File tree

6 files changed

+160
-12
lines changed

6 files changed

+160
-12
lines changed

.github/workflows/coverage.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ on:
1212

1313
env:
1414
CARGO_TERM_COLOR: always
15+
CODECOV_TOKEN: 86ae569f-70d3-4c7b-833e-6e8fc97ea9f3
1516

1617
jobs:
1718
coverage:
1819
name: Run cargo coverage
1920
runs-on: ubuntu-latest
20-
env:
21-
CARGO_TERM_COLOR: always
2221
steps:
2322
- name: Checkout sources
2423
uses: actions/checkout@v4
@@ -32,6 +31,6 @@ jobs:
3231
- name: Install cargo-llvm-cov
3332
uses: taiki-e/install-action@cargo-llvm-cov
3433
- name: Generate code coverage
35-
run: sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && sudo -u runner /home/runner/.cargo/bin/cargo llvm-cov --all-features --release --workspace --lcov --output-path lcov.info"
34+
run: sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && sudo -u runner /home/runner/.cargo/bin/cargo llvm-cov --all-features --release --all --lcov --output-path lcov.info"
3635
- name: Upload coverage to Codecov
37-
run: bash <(curl -s https://codecov.io/bash) -f lcov.info -t ${{ secrets.CODECOV_TOKEN }}
36+
run: bash <(curl -s https://codecov.io/bash) -f lcov.info -t ${{ env.CODECOV_TOKEN }}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# open-coroutine
22

33
[![crates.io](https://img.shields.io/crates/v/open-coroutine.svg)](https://crates.io/crates/open-coroutine)
4+
[![docs.rs](https://img.shields.io/badge/docs-release-blue)](https://docs.rs/open-coroutine)
45
[![LICENSE](https://img.shields.io/github/license/acl-dev/open-coroutine.svg?style=flat-square)](https://github.com/acl-dev/open-coroutine/blob/master/LICENSE-APACHE)
56
[![Build Status](https://github.com/acl-dev/open-coroutine/workflows/CI/badge.svg)](https://github.com/acl-dev/open-coroutine/actions)
67
[![Codecov](https://codecov.io/github/acl-dev/open-coroutine/graph/badge.svg?token=MSM3R7CBEX)](https://codecov.io/github/acl-dev/open-coroutine)

core/src/coroutine/korosensei.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,28 @@ impl<'c, Param, Yield, Return> Coroutine<'c, Param, Yield, Return> {
324324
r
325325
});
326326
}
327-
DefaultStack::new(stack_size).map(|stack| corosensei::on_stack(stack, callback))
327+
// in thread
328+
thread_local! {
329+
#[allow(clippy::missing_const_for_thread_local)]
330+
static STACK_INFOS: RefCell<VecDeque<StackInfo>> = const { RefCell::new(VecDeque::new()) };
331+
}
332+
if let Some(last_stack_info) = STACK_INFOS.with(|s| s.borrow().back().copied()) {
333+
let remaining_stack = psm::stack_pointer() as usize - last_stack_info.stack_bottom;
334+
if remaining_stack >= red_zone {
335+
return Ok(callback());
336+
}
337+
}
338+
DefaultStack::new(stack_size).map(|stack| {
339+
STACK_INFOS.with(|s| {
340+
s.borrow_mut().push_back(StackInfo {
341+
stack_top: stack.base().get(),
342+
stack_bottom: stack.limit().get(),
343+
});
344+
});
345+
let r = corosensei::on_stack(stack, callback);
346+
_ = STACK_INFOS.with(|s| s.borrow_mut().pop_back());
347+
r
348+
})
328349
}
329350
}
330351

core/src/coroutine/state.rs

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ where
3333
/// if change state fails.
3434
pub(crate) fn ready(&self) -> std::io::Result<()> {
3535
let current = self.state();
36-
if let CoroutineState::Suspend(_, timestamp) = current {
37-
if timestamp <= now() {
38-
let new_state = CoroutineState::Ready;
39-
let old_state = self.change_state(new_state);
40-
self.on_ready(self, old_state);
41-
return Ok(());
36+
match current {
37+
CoroutineState::Ready => return Ok(()),
38+
CoroutineState::Suspend(_, timestamp) => {
39+
if timestamp <= now() {
40+
let new_state = CoroutineState::Ready;
41+
let old_state = self.change_state(new_state);
42+
self.on_ready(self, old_state);
43+
return Ok(());
44+
}
4245
}
46+
_ => {}
4347
}
4448
Err(Error::new(
4549
ErrorKind::Other,
@@ -198,3 +202,84 @@ where
198202
))
199203
}
200204
}
205+
206+
#[cfg(test)]
207+
mod tests {
208+
use super::*;
209+
use crate::coroutine::suspender::Suspender;
210+
211+
#[test]
212+
fn test_ready() -> std::io::Result<()> {
213+
let co = co!(|_: &Suspender<(), ()>, ()| {})?;
214+
assert_eq!(CoroutineState::Ready, co.state());
215+
co.ready()?;
216+
assert_eq!(CoroutineState::Ready, co.state());
217+
co.running()?;
218+
co.suspend((), u64::MAX)?;
219+
assert_eq!(CoroutineState::Suspend((), u64::MAX), co.state());
220+
assert!(co.ready().is_err());
221+
Ok(())
222+
}
223+
224+
#[test]
225+
fn test_running() -> std::io::Result<()> {
226+
let co = co!(|_: &Suspender<(), ()>, ()| {})?;
227+
assert_eq!(CoroutineState::Ready, co.state());
228+
co.running()?;
229+
co.running()?;
230+
co.complete(())?;
231+
assert_eq!(CoroutineState::Complete(()), co.state());
232+
assert!(co.running().is_err());
233+
Ok(())
234+
}
235+
236+
#[test]
237+
fn test_suspend() -> std::io::Result<()> {
238+
let mut co = co!(|_: &Suspender<(), ()>, ()| {})?;
239+
assert_eq!(CoroutineState::Ready, co.state());
240+
co.running()?;
241+
co.suspend((), u64::MAX)?;
242+
assert_eq!(CoroutineState::Suspend((), u64::MAX), co.state());
243+
assert!(co.resume().is_err());
244+
assert!(co.suspend((), u64::MAX).is_err());
245+
Ok(())
246+
}
247+
248+
#[test]
249+
fn test_syscall() -> std::io::Result<()> {
250+
let co = co!(|_: &Suspender<(), ()>, ()| {})?;
251+
assert_eq!(CoroutineState::Ready, co.state());
252+
co.running()?;
253+
co.syscall((), Syscall::nanosleep, SyscallState::Executing)?;
254+
assert_eq!(
255+
CoroutineState::SystemCall((), Syscall::nanosleep, SyscallState::Executing),
256+
co.state()
257+
);
258+
assert!(co
259+
.syscall((), Syscall::sleep, SyscallState::Executing)
260+
.is_err());
261+
Ok(())
262+
}
263+
264+
#[test]
265+
fn test_complete() -> std::io::Result<()> {
266+
let co = co!(|_: &Suspender<(), ()>, ()| {})?;
267+
assert_eq!(CoroutineState::Ready, co.state());
268+
co.running()?;
269+
co.complete(())?;
270+
assert_eq!(CoroutineState::Complete(()), co.state());
271+
assert!(co.complete(()).is_err());
272+
Ok(())
273+
}
274+
275+
#[test]
276+
fn test_error() -> std::io::Result<()> {
277+
let co = co!(|_: &Suspender<(), ()>, ()| {})?;
278+
assert_eq!(CoroutineState::Ready, co.state());
279+
co.running()?;
280+
co.error("test error, ignore it")?;
281+
assert_eq!(CoroutineState::Error("test error, ignore it"), co.state());
282+
assert!(co.error("abc").is_err());
283+
Ok(())
284+
}
285+
}

core/tests/coroutine.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,48 @@ fn coroutine_delay() -> std::io::Result<()> {
7070
Ok(())
7171
}
7272

73+
#[test]
74+
fn sp_in_bounds() -> std::io::Result<()> {
75+
let mut coroutine = co!(|suspender, input| {
76+
let current = Coroutine::<(), (), ()>::current().unwrap();
77+
if let Some(stack_info) = current.stack_infos().back().copied() {
78+
assert!(current.stack_ptr_in_bounds(psm::stack_pointer() as u64));
79+
assert_eq!(
80+
current.stack_ptr_in_bounds(stack_info.stack_top as u64 + 1),
81+
false
82+
);
83+
assert_eq!(
84+
current.stack_ptr_in_bounds(stack_info.stack_bottom as u64 - 1),
85+
false
86+
);
87+
}
88+
assert_eq!(1, input);
89+
assert_eq!(3, suspender.suspend_with(2));
90+
4
91+
})?;
92+
assert_eq!(CoroutineState::Suspend(2, 0), coroutine.resume_with(1)?);
93+
assert_eq!(CoroutineState::Complete(4), coroutine.resume_with(3)?);
94+
println!("{:?}", coroutine);
95+
println!("{}", coroutine);
96+
Ok(())
97+
}
98+
99+
#[test]
100+
fn thread_stack_growth() {
101+
fn recurse(i: u32, p: &mut [u8; 10240]) {
102+
Coroutine::<(), (), ()>::maybe_grow(|| {
103+
// Ensure the stack allocation isn't optimized away.
104+
unsafe { std::ptr::read_volatile(&p) };
105+
if i > 0 {
106+
recurse(i - 1, &mut [0; 10240]);
107+
}
108+
})
109+
.expect("allocate stack failed")
110+
}
111+
// Use 10MB of stack.
112+
recurse(1000, &mut [0; 10240]);
113+
}
114+
73115
#[test]
74116
fn coroutine_stack_growth() -> std::io::Result<()> {
75117
let mut coroutine = co!(|_: &Suspender<(), ()>, ()| {

open-coroutine/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ macro_rules! maybe_grow {
226226
};
227227
}
228228

229-
/// Create a coroutine.
229+
/// Grows the call stack if necessary.
230230
pub fn maybe_grow<R: 'static, F: FnOnce() -> R>(
231231
red_zone: usize,
232232
stack_size: usize,

0 commit comments

Comments
 (0)