Skip to content

Commit 49ba8ae

Browse files
author
whitequark
committedAug 30, 2016
Rust: implement a basic scheduler.
1 parent 051e6e0 commit 49ba8ae

File tree

9 files changed

+412
-1
lines changed

9 files changed

+412
-1
lines changed
 

Diff for: ‎artiq/runtime.rs/Cargo.lock

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: ‎artiq/runtime.rs/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ path = "src/lib.rs"
1111
[dependencies]
1212
std_artiq = { path = "libstd_artiq" }
1313

14+
[dependencies.fringe]
15+
git = "https://github.com/whitequark/libfringe"
16+
default-features = false
17+
features = ["alloc"]
18+
1419
[profile.dev]
1520
panic = 'abort'
1621
opt-level = 2

Diff for: ‎artiq/runtime.rs/libstd_artiq/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub mod prelude {
1313
}
1414
}
1515

16+
pub mod time;
17+
1618
use core::fmt::Write;
1719

1820
#[macro_export]

Diff for: ‎artiq/runtime.rs/libstd_artiq/time/duration.rs

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use core::ops::{Add, Sub, Mul, Div};
2+
3+
const MILLIS_PER_SEC: u64 = 1_000;
4+
const NANOS_PER_MILLI: u32 = 1_000_000;
5+
6+
/// A duration type to represent a span of time, typically used for system
7+
/// timeouts.
8+
///
9+
/// Each duration is composed of a number of seconds and nanosecond precision.
10+
/// APIs binding a system timeout will typically round up the nanosecond
11+
/// precision if the underlying system does not support that level of precision.
12+
///
13+
/// Durations implement many common traits, including `Add`, `Sub`, and other
14+
/// ops traits. Currently a duration may only be inspected for its number of
15+
/// seconds and its nanosecond precision.
16+
///
17+
/// # Examples
18+
///
19+
/// ```
20+
/// use std::time::Duration;
21+
///
22+
/// let five_seconds = Duration::new(5, 0);
23+
/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5);
24+
///
25+
/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5);
26+
/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5);
27+
///
28+
/// let ten_millis = Duration::from_millis(10);
29+
/// ```
30+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
31+
pub struct Duration {
32+
millis: u64
33+
}
34+
35+
impl Duration {
36+
/// Creates a new `Duration` from the specified number of seconds and
37+
/// additional nanosecond precision.
38+
///
39+
/// If the nanoseconds is greater than 1 billion (the number of nanoseconds
40+
/// in a second), then it will carry over into the seconds provided.
41+
pub fn new(secs: u64, nanos: u32) -> Duration {
42+
Duration { millis: secs * MILLIS_PER_SEC + (nanos / NANOS_PER_MILLI) as u64 }
43+
}
44+
45+
/// Creates a new `Duration` from the specified number of seconds.
46+
pub fn from_secs(secs: u64) -> Duration {
47+
Duration { millis: secs * MILLIS_PER_SEC }
48+
}
49+
50+
/// Creates a new `Duration` from the specified number of milliseconds.
51+
pub fn from_millis(millis: u64) -> Duration {
52+
Duration { millis: millis }
53+
}
54+
55+
/// Returns the number of whole milliseconds represented by this duration.
56+
pub fn as_millis(&self) -> u64 { self.millis }
57+
58+
/// Returns the number of whole seconds represented by this duration.
59+
///
60+
/// The extra precision represented by this duration is ignored (e.g. extra
61+
/// nanoseconds are not represented in the returned value).
62+
pub fn as_secs(&self) -> u64 {
63+
self.millis / MILLIS_PER_SEC
64+
}
65+
66+
/// Returns the nanosecond precision represented by this duration.
67+
///
68+
/// This method does **not** return the length of the duration when
69+
/// represented by nanoseconds. The returned number always represents a
70+
/// fractional portion of a second (e.g. it is less than one billion).
71+
pub fn subsec_nanos(&self) -> u32 {
72+
(self.millis % MILLIS_PER_SEC) as u32 * NANOS_PER_MILLI
73+
}
74+
}
75+
76+
impl Add for Duration {
77+
type Output = Duration;
78+
79+
fn add(self, rhs: Duration) -> Duration {
80+
Duration {
81+
millis: self.millis.checked_add(rhs.millis)
82+
.expect("overflow when adding durations")
83+
}
84+
}
85+
}
86+
87+
impl Sub for Duration {
88+
type Output = Duration;
89+
90+
fn sub(self, rhs: Duration) -> Duration {
91+
Duration {
92+
millis: self.millis.checked_sub(rhs.millis)
93+
.expect("overflow when subtracting durations")
94+
}
95+
}
96+
}
97+
98+
impl Mul<u32> for Duration {
99+
type Output = Duration;
100+
101+
fn mul(self, rhs: u32) -> Duration {
102+
Duration {
103+
millis: self.millis.checked_mul(rhs as u64)
104+
.expect("overflow when multiplying duration")
105+
}
106+
}
107+
}
108+
109+
impl Div<u32> for Duration {
110+
type Output = Duration;
111+
112+
fn div(self, rhs: u32) -> Duration {
113+
Duration {
114+
millis: self.millis / (rhs as u64)
115+
}
116+
}
117+
}

Diff for: ‎artiq/runtime.rs/libstd_artiq/time/instant.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use core::ops::{Add, Sub};
2+
use time::duration::Duration;
3+
4+
/// A measurement of a monotonically increasing clock.
5+
///
6+
/// Instants are always guaranteed to be greater than any previously measured
7+
/// instant when created, and are often useful for tasks such as measuring
8+
/// benchmarks or timing how long an operation takes.
9+
///
10+
/// Note, however, that instants are not guaranteed to be **steady**. In other
11+
/// words, each tick of the underlying clock may not be the same length (e.g.
12+
/// some seconds may be longer than others). An instant may jump forwards or
13+
/// experience time dilation (slow down or speed up), but it will never go
14+
/// backwards.
15+
///
16+
/// Instants are opaque types that can only be compared to one another. There is
17+
/// no method to get "the number of seconds" from an instant. Instead, it only
18+
/// allows measuring the duration between two instants (or comparing two
19+
/// instants).
20+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
21+
pub struct Instant {
22+
millis: u64
23+
}
24+
25+
impl Instant {
26+
/// Returns an instant corresponding to "now".
27+
pub fn now() -> Instant {
28+
extern {
29+
fn clock_get_ms() -> i64;
30+
}
31+
32+
Instant { millis: unsafe { clock_get_ms() as u64 } }
33+
}
34+
35+
/// Returns the amount of time elapsed from another instant to this one.
36+
///
37+
/// # Panics
38+
///
39+
/// This function will panic if `earlier` is later than `self`, which should
40+
/// only be possible if `earlier` was created after `self`. Because
41+
/// `Instant` is monotonic, the only time that this should happen should be
42+
/// a bug.
43+
pub fn duration_from_earlier(&self, earlier: Instant) -> Duration {
44+
let millis = self.millis.checked_sub(earlier.millis)
45+
.expect("`earlier` is later than `self`");
46+
Duration::from_millis(millis)
47+
}
48+
49+
/// Returns the amount of time elapsed since this instant was created.
50+
///
51+
/// # Panics
52+
///
53+
/// This function may panic if the current time is earlier than this
54+
/// instant, which is something that can happen if an `Instant` is
55+
/// produced synthetically.
56+
pub fn elapsed(&self) -> Duration {
57+
Instant::now().duration_from_earlier(*self)
58+
}
59+
}
60+
61+
impl Add<Duration> for Instant {
62+
type Output = Instant;
63+
64+
fn add(self, other: Duration) -> Instant {
65+
Instant {
66+
millis: self.millis.checked_add(other.as_millis())
67+
.expect("overflow when adding duration to instant")
68+
}
69+
}
70+
}
71+
72+
impl Sub<Duration> for Instant {
73+
type Output = Instant;
74+
75+
fn sub(self, other: Duration) -> Instant {
76+
Instant {
77+
millis: self.millis.checked_sub(other.as_millis())
78+
.expect("overflow when subtracting duration from instant")
79+
}
80+
}
81+
}

Diff for: ‎artiq/runtime.rs/libstd_artiq/time/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub use self::duration::Duration;
2+
pub use self::instant::Instant;
3+
4+
mod duration;
5+
mod instant;

Diff for: ‎artiq/runtime.rs/src/lib.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,27 @@
44
extern crate std_artiq as std;
55

66
use std::prelude::v1::*;
7+
use std::time::Duration;
8+
use scheduler::Scheduler;
9+
10+
pub mod scheduler;
711

812
#[no_mangle]
913
pub extern "C" fn rust_main() {
10-
println!("hello from rust!");
14+
// let mut scheduler = Scheduler::new();
15+
// unsafe {
16+
// scheduler.spawn(4096, move |mut io| {
17+
// loop {
18+
// println!("thread A");
19+
// io.sleep(Duration::from_secs(1)).unwrap()
20+
// }
21+
// });
22+
// scheduler.spawn(4096, move |mut io| {
23+
// loop {
24+
// println!("thread B");
25+
// io.sleep(Duration::from_millis(333)).unwrap()
26+
// }
27+
// });
28+
// }
29+
// loop { scheduler.run() }
1130
}

Diff for: ‎artiq/runtime.rs/src/scheduler.rs

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
extern crate fringe;
2+
3+
use std::prelude::v1::*;
4+
use std::time::{Instant, Duration};
5+
use self::fringe::OwnedStack;
6+
use self::fringe::generator::{Generator, Yielder};
7+
8+
#[derive(Debug)]
9+
pub struct WaitRequest {
10+
timeout: Option<Instant>,
11+
event: Option<WaitEvent>
12+
}
13+
14+
#[derive(Debug)]
15+
pub enum WaitResult {
16+
Completed,
17+
TimedOut,
18+
Interrupted
19+
}
20+
21+
#[derive(Debug)]
22+
struct Thread {
23+
generator: Generator<WaitResult, WaitRequest, OwnedStack>,
24+
waiting_for: WaitRequest,
25+
interrupted: bool
26+
}
27+
28+
#[derive(Debug)]
29+
pub struct Scheduler {
30+
threads: Vec<Thread>,
31+
index: usize
32+
}
33+
34+
impl Scheduler {
35+
pub fn new() -> Scheduler {
36+
Scheduler { threads: Vec::new(), index: 0 }
37+
}
38+
39+
pub unsafe fn spawn<F: FnOnce(Io) + Send>(&mut self, stack_size: usize, f: F) {
40+
let stack = OwnedStack::new(stack_size);
41+
let thread = Thread {
42+
generator: Generator::unsafe_new(stack, move |yielder, _| {
43+
f(Io(yielder))
44+
}),
45+
waiting_for: WaitRequest {
46+
timeout: None,
47+
event: None
48+
},
49+
interrupted: false
50+
};
51+
self.threads.push(thread)
52+
}
53+
54+
pub fn run(&mut self) {
55+
if self.threads.len() == 0 { return }
56+
57+
let now = Instant::now();
58+
59+
let start_index = self.index;
60+
loop {
61+
self.index = (self.index + 1) % self.threads.len();
62+
63+
let result = {
64+
let thread = &mut self.threads[self.index];
65+
match thread.waiting_for {
66+
_ if thread.interrupted => {
67+
thread.interrupted = false;
68+
thread.generator.resume(WaitResult::Interrupted)
69+
}
70+
WaitRequest { timeout: Some(instant), .. } if now >= instant =>
71+
thread.generator.resume(WaitResult::TimedOut),
72+
WaitRequest { event: Some(ref event), .. } if event.completed() =>
73+
thread.generator.resume(WaitResult::Completed),
74+
WaitRequest { timeout: None, event: None } =>
75+
thread.generator.resume(WaitResult::Completed),
76+
_ => {
77+
if self.index == start_index {
78+
// We've checked every thread and none of them are runnable.
79+
break
80+
} else {
81+
continue
82+
}
83+
}
84+
}
85+
};
86+
87+
match result {
88+
None => {
89+
// The thread has terminated.
90+
self.threads.remove(self.index);
91+
self.index = 0
92+
},
93+
Some(wait_request) => {
94+
// The thread has suspended itself.
95+
self.threads[self.index].waiting_for = wait_request
96+
}
97+
}
98+
99+
break
100+
}
101+
}
102+
}
103+
104+
#[derive(Debug)]
105+
pub enum WaitEvent {}
106+
107+
impl WaitEvent {
108+
fn completed(&self) -> bool {
109+
match *self {}
110+
}
111+
}
112+
113+
pub type IoResult<T> = Result<T, ()>;
114+
115+
#[derive(Debug)]
116+
pub struct Io<'a>(&'a mut Yielder<WaitResult, WaitRequest, OwnedStack>);
117+
118+
impl<'a> Io<'a> {
119+
pub fn sleep(&mut self, duration: Duration) -> IoResult<()> {
120+
let request = WaitRequest {
121+
timeout: Some(Instant::now() + duration),
122+
event: None
123+
};
124+
125+
match self.0.suspend(request) {
126+
WaitResult::TimedOut => Ok(()),
127+
WaitResult::Interrupted => Err(()),
128+
_ => unreachable!()
129+
}
130+
}
131+
}

Diff for: ‎artiq/runtime/runtime.ld

+10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ SECTIONS
2626
_etext = .;
2727
} > runtime
2828

29+
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
30+
.got : {
31+
_GLOBAL_OFFSET_TABLE_ = .;
32+
*(.got)
33+
} > runtime
34+
35+
.got.plt : {
36+
*(.got.plt)
37+
} > runtime
38+
2939
.rodata :
3040
{
3141
. = ALIGN(4);

0 commit comments

Comments
 (0)
Please sign in to comment.