Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use stack instead of parent pointer #37

Merged
merged 18 commits into from
May 31, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
language: rust
rust: nightly

os:
- linux
- osx
script:
- cargo build -v
- cargo test -v
- cargo run --example simple
- cargo doc --no-deps
- cargo doc --no-deps
8 changes: 0 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@ homepage = "https://github.com/rustcc/coroutine-rs"
build = "build.rs"
keywords = ["coroutine", "green", "thread", "fiber"]

[features]

default = [
"enable-stack-recycle"
]

enable-stack-recycle = []

[build-dependencies]
gcc = "*"
log = "*"
Expand Down
25 changes: 25 additions & 0 deletions LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright (c) 2014 Rustcc Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# coroutine-rs

[![Build Status](https://travis-ci.org/rustcc/coroutine-rs.png?branch=master)](https://travis-ci.org/rustcc/coroutine-rs)
[![Build Status](https://img.shields.io/travis/rustcc/coroutine-rs.svg)](https://travis-ci.org/rustcc/coroutine-rs) [![crates.io](https://img.shields.io/crates/v/coroutine.svg)](https://crates.io/crates/coroutine) [![crates.io](https://img.shields.io/crates/l/coroutine.svg)](https://crates.io/crates/coroutine)

Coroutine library in Rust

Expand Down
89 changes: 55 additions & 34 deletions src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ use std::rt::unwind::try;
use std::any::Any;
use std::cell::UnsafeCell;
use std::ops::Deref;
use std::ptr;
use std::sync::Arc;
use std::cell::RefCell;

Expand Down Expand Up @@ -161,6 +160,8 @@ impl Handle {
match self.state() {
State::Finished | State::Running => return Ok(()),
State::Panicked => panic!("Trying to resume a panicked coroutine"),
State::Normal => panic!("Coroutine {:?} is waiting for its child to return, cannot resume!",
self.name().unwrap_or("<unnamed>")),
_ => {}
}

Expand All @@ -174,13 +175,11 @@ impl Handle {

// Save state
to_coro.set_state(State::Running);
to_coro.parent = from_coro;
from_coro.set_state(State::Normal);

env.current_running = self.clone();
env.coroutine_stack.push(self.clone());
Context::swap(&mut from_coro.saved_context, &to_coro.saved_context);
}
env.current_running = from_coro_hdl;

match env.running_state.take() {
Some(err) => Err(err),
Expand All @@ -204,7 +203,7 @@ impl Handle {
pub fn join(&self) -> ResumeResult<()> {
loop {
match self.state() {
State::Suspended => try!(self.resume()),
State::Suspended | State::Blocked => try!(self.resume()),
_ => break,
}
}
Expand Down Expand Up @@ -245,9 +244,6 @@ pub struct Coroutine {
/// Always valid if the task is alive and not running.
saved_context: Context,

/// Parent coroutine, will always be valid
parent: *mut Coroutine,

/// State
state: State,

Expand Down Expand Up @@ -279,7 +275,9 @@ extern "C" fn coroutine_initialize(_: usize, f: *mut ()) -> ! {

let env = Environment::current();

let cur: &mut Coroutine = unsafe { env.current_running.get_inner_mut() };
let cur: &mut Coroutine = unsafe {
env.coroutine_stack.last().expect("Impossible happened! No current coroutine!").get_inner_mut()
};

let state = match ret {
Ok(..) => {
Expand Down Expand Up @@ -320,7 +318,6 @@ impl Coroutine {
Handle::new(Coroutine {
current_stack_segment: None,
saved_context: Context::empty(),
parent: ptr::null_mut(),
state: state,
name: name,
})
Expand All @@ -330,30 +327,28 @@ impl Coroutine {
Handle::new(Coroutine {
current_stack_segment: Some(stack),
saved_context: ctx,
parent: ptr::null_mut(),
state: state,
name: name,
})
}

/// Spawn a Coroutine with options
pub fn spawn_opts<F>(f: F, opts: Options) -> Handle
where F: FnOnce() + Send + 'static {
where F: FnOnce() + Send + 'static
{

let env = Environment::current();
let mut stack = env.stack_pool.take_stack(opts.stack_size);

let ctx = Context::new(coroutine_initialize,
0,
f,
&mut stack);
let ctx = Context::new(coroutine_initialize, 0, f, &mut stack);

Coroutine::new(opts.name, stack, ctx, State::Suspended)
}

/// Spawn a Coroutine with default options
pub fn spawn<F>(f: F) -> Handle
where F: FnOnce() + Send + 'static {
where F: FnOnce() + Send + 'static
{
Coroutine::spawn_opts(f, Default::default())
}

Expand All @@ -364,12 +359,19 @@ impl Coroutine {
assert!(state != State::Running);

let env = Environment::current();
unsafe {
let from_coro = env.current_running.get_inner_mut();
from_coro.set_state(state);
if env.coroutine_stack.len() == 1 {
// Environment root
return;
}

let to_coro: &mut Coroutine = &mut *from_coro.parent;
Context::swap(&mut from_coro.saved_context, &to_coro.saved_context);
unsafe {
match (env.coroutine_stack.pop(), env.coroutine_stack.last()) {
(Some(from_coro), Some(to_coro)) => {
from_coro.set_state(state);
Context::swap(&mut from_coro.get_inner_mut().saved_context, &to_coro.saved_context);
},
_ => unreachable!()
}
}
}

Expand All @@ -391,7 +393,8 @@ impl Coroutine {
/// in more than one native thread.
#[inline]
pub fn current() -> Handle {
Environment::current().current_running.clone()
Environment::current().coroutine_stack.last().map(|hdl| hdl.clone())
.expect("Impossible happened! No current coroutine!")
}

#[inline(always)]
Expand All @@ -418,25 +421,24 @@ thread_local!(static COROUTINE_ENVIRONMENT: UnsafeCell<Environment> = UnsafeCell
struct Environment {
stack_pool: StackPool,

current_running: Handle,
__main_coroutine: Handle,
coroutine_stack: Vec<Handle>,

running_state: Option<Box<Any + Send>>,
}

impl Environment {
/// Initialize a new environment
fn new() -> Environment {
let coro = unsafe {
let st = unsafe {
let mut st = Vec::new();
let coro = Coroutine::empty(Some("<Environment Root Coroutine>".to_string()), State::Running);
coro.0.borrow_mut().parent = coro.get_inner_mut(); // Point to itself
coro
st.push(coro);
st
};

Environment {
stack_pool: StackPool::new(),
current_running: coro.clone(),
__main_coroutine: coro,
coroutine_stack: st,

running_state: None,
}
Expand Down Expand Up @@ -484,7 +486,8 @@ impl Builder {

/// Spawn a new Coroutine, and return a handle for it.
pub fn spawn<F>(self, f: F) -> Handle
where F: FnOnce() + Send + 'static {
where F: FnOnce() + Send + 'static
{
Coroutine::spawn_opts(f, self.opts)
}
}
Expand All @@ -493,7 +496,8 @@ impl Builder {
///
/// Equavalent to `Coroutine::spawn`.
pub fn spawn<F>(f: F) -> Handle
where F: FnOnce() + Send + 'static {
where F: FnOnce() + Send + 'static
{
Builder::new().spawn(f)
}

Expand Down Expand Up @@ -633,9 +637,9 @@ mod bench {
use super::Coroutine;

#[bench]
fn bench_coroutine_spawning_with_recycle(b: &mut Bencher) {
fn bench_coroutine_spawning(b: &mut Bencher) {
b.iter(|| {
let _ = Coroutine::spawn(move|| {}).join();
let _ = Coroutine::spawn(move|| {});
});
}

Expand Down Expand Up @@ -677,4 +681,21 @@ mod bench {
assert_eq!(result, MAX_NUMBER);
});
}

#[bench]
fn bench_context_switch(b: &mut Bencher) {
let coro = Coroutine::spawn(|| {
loop {
// 3. Save current context
// 4. Switch
Coroutine::sched();
}
});

b.iter(|| {
// 1. Save current context
// 2. Switch
let _ = coro.resume();
});
}
}