mirror of https://github.com/m-labs/artiq.git
Rust: implement a basic scheduler.
This commit is contained in:
parent
051e6e0447
commit
49ba8aec18
|
@ -2,6 +2,7 @@
|
||||||
name = "runtime"
|
name = "runtime"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"fringe 0.0.1 (git+https://github.com/whitequark/libfringe)",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -9,6 +10,30 @@ dependencies = [
|
||||||
name = "alloc_artiq"
|
name = "alloc_artiq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fringe"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "git+https://github.com/whitequark/libfringe#48512d0e4ecc913be7a56f94825ba856c5a1e8aa"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel32-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "std_artiq"
|
name = "std_artiq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -16,3 +41,19 @@ dependencies = [
|
||||||
"alloc_artiq 0.0.0",
|
"alloc_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-build"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum fringe 0.0.1 (git+https://github.com/whitequark/libfringe)" = "<none>"
|
||||||
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
|
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
||||||
|
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
|
|
|
@ -11,6 +11,11 @@ path = "src/lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
std_artiq = { path = "libstd_artiq" }
|
std_artiq = { path = "libstd_artiq" }
|
||||||
|
|
||||||
|
[dependencies.fringe]
|
||||||
|
git = "https://github.com/whitequark/libfringe"
|
||||||
|
default-features = false
|
||||||
|
features = ["alloc"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
opt-level = 2
|
opt-level = 2
|
||||||
|
|
|
@ -13,6 +13,8 @@ pub mod prelude {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod time;
|
||||||
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
use core::ops::{Add, Sub, Mul, Div};
|
||||||
|
|
||||||
|
const MILLIS_PER_SEC: u64 = 1_000;
|
||||||
|
const NANOS_PER_MILLI: u32 = 1_000_000;
|
||||||
|
|
||||||
|
/// A duration type to represent a span of time, typically used for system
|
||||||
|
/// timeouts.
|
||||||
|
///
|
||||||
|
/// Each duration is composed of a number of seconds and nanosecond precision.
|
||||||
|
/// APIs binding a system timeout will typically round up the nanosecond
|
||||||
|
/// precision if the underlying system does not support that level of precision.
|
||||||
|
///
|
||||||
|
/// Durations implement many common traits, including `Add`, `Sub`, and other
|
||||||
|
/// ops traits. Currently a duration may only be inspected for its number of
|
||||||
|
/// seconds and its nanosecond precision.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// let five_seconds = Duration::new(5, 0);
|
||||||
|
/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5);
|
||||||
|
///
|
||||||
|
/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5);
|
||||||
|
/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5);
|
||||||
|
///
|
||||||
|
/// let ten_millis = Duration::from_millis(10);
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||||
|
pub struct Duration {
|
||||||
|
millis: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Duration {
|
||||||
|
/// Creates a new `Duration` from the specified number of seconds and
|
||||||
|
/// additional nanosecond precision.
|
||||||
|
///
|
||||||
|
/// If the nanoseconds is greater than 1 billion (the number of nanoseconds
|
||||||
|
/// in a second), then it will carry over into the seconds provided.
|
||||||
|
pub fn new(secs: u64, nanos: u32) -> Duration {
|
||||||
|
Duration { millis: secs * MILLIS_PER_SEC + (nanos / NANOS_PER_MILLI) as u64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `Duration` from the specified number of seconds.
|
||||||
|
pub fn from_secs(secs: u64) -> Duration {
|
||||||
|
Duration { millis: secs * MILLIS_PER_SEC }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `Duration` from the specified number of milliseconds.
|
||||||
|
pub fn from_millis(millis: u64) -> Duration {
|
||||||
|
Duration { millis: millis }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of whole milliseconds represented by this duration.
|
||||||
|
pub fn as_millis(&self) -> u64 { self.millis }
|
||||||
|
|
||||||
|
/// Returns the number of whole seconds represented by this duration.
|
||||||
|
///
|
||||||
|
/// The extra precision represented by this duration is ignored (e.g. extra
|
||||||
|
/// nanoseconds are not represented in the returned value).
|
||||||
|
pub fn as_secs(&self) -> u64 {
|
||||||
|
self.millis / MILLIS_PER_SEC
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the nanosecond precision represented by this duration.
|
||||||
|
///
|
||||||
|
/// This method does **not** return the length of the duration when
|
||||||
|
/// represented by nanoseconds. The returned number always represents a
|
||||||
|
/// fractional portion of a second (e.g. it is less than one billion).
|
||||||
|
pub fn subsec_nanos(&self) -> u32 {
|
||||||
|
(self.millis % MILLIS_PER_SEC) as u32 * NANOS_PER_MILLI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Duration {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
fn add(self, rhs: Duration) -> Duration {
|
||||||
|
Duration {
|
||||||
|
millis: self.millis.checked_add(rhs.millis)
|
||||||
|
.expect("overflow when adding durations")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Duration {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Duration) -> Duration {
|
||||||
|
Duration {
|
||||||
|
millis: self.millis.checked_sub(rhs.millis)
|
||||||
|
.expect("overflow when subtracting durations")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<u32> for Duration {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
fn mul(self, rhs: u32) -> Duration {
|
||||||
|
Duration {
|
||||||
|
millis: self.millis.checked_mul(rhs as u64)
|
||||||
|
.expect("overflow when multiplying duration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<u32> for Duration {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
fn div(self, rhs: u32) -> Duration {
|
||||||
|
Duration {
|
||||||
|
millis: self.millis / (rhs as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
use core::ops::{Add, Sub};
|
||||||
|
use time::duration::Duration;
|
||||||
|
|
||||||
|
/// A measurement of a monotonically increasing clock.
|
||||||
|
///
|
||||||
|
/// Instants are always guaranteed to be greater than any previously measured
|
||||||
|
/// instant when created, and are often useful for tasks such as measuring
|
||||||
|
/// benchmarks or timing how long an operation takes.
|
||||||
|
///
|
||||||
|
/// Note, however, that instants are not guaranteed to be **steady**. In other
|
||||||
|
/// words, each tick of the underlying clock may not be the same length (e.g.
|
||||||
|
/// some seconds may be longer than others). An instant may jump forwards or
|
||||||
|
/// experience time dilation (slow down or speed up), but it will never go
|
||||||
|
/// backwards.
|
||||||
|
///
|
||||||
|
/// Instants are opaque types that can only be compared to one another. There is
|
||||||
|
/// no method to get "the number of seconds" from an instant. Instead, it only
|
||||||
|
/// allows measuring the duration between two instants (or comparing two
|
||||||
|
/// instants).
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Instant {
|
||||||
|
millis: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instant {
|
||||||
|
/// Returns an instant corresponding to "now".
|
||||||
|
pub fn now() -> Instant {
|
||||||
|
extern {
|
||||||
|
fn clock_get_ms() -> i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instant { millis: unsafe { clock_get_ms() as u64 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from another instant to this one.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will panic if `earlier` is later than `self`, which should
|
||||||
|
/// only be possible if `earlier` was created after `self`. Because
|
||||||
|
/// `Instant` is monotonic, the only time that this should happen should be
|
||||||
|
/// a bug.
|
||||||
|
pub fn duration_from_earlier(&self, earlier: Instant) -> Duration {
|
||||||
|
let millis = self.millis.checked_sub(earlier.millis)
|
||||||
|
.expect("`earlier` is later than `self`");
|
||||||
|
Duration::from_millis(millis)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed since this instant was created.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function may panic if the current time is earlier than this
|
||||||
|
/// instant, which is something that can happen if an `Instant` is
|
||||||
|
/// produced synthetically.
|
||||||
|
pub fn elapsed(&self) -> Duration {
|
||||||
|
Instant::now().duration_from_earlier(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Duration> for Instant {
|
||||||
|
type Output = Instant;
|
||||||
|
|
||||||
|
fn add(self, other: Duration) -> Instant {
|
||||||
|
Instant {
|
||||||
|
millis: self.millis.checked_add(other.as_millis())
|
||||||
|
.expect("overflow when adding duration to instant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Duration> for Instant {
|
||||||
|
type Output = Instant;
|
||||||
|
|
||||||
|
fn sub(self, other: Duration) -> Instant {
|
||||||
|
Instant {
|
||||||
|
millis: self.millis.checked_sub(other.as_millis())
|
||||||
|
.expect("overflow when subtracting duration from instant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub use self::duration::Duration;
|
||||||
|
pub use self::instant::Instant;
|
||||||
|
|
||||||
|
mod duration;
|
||||||
|
mod instant;
|
|
@ -4,8 +4,27 @@
|
||||||
extern crate std_artiq as std;
|
extern crate std_artiq as std;
|
||||||
|
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
use scheduler::Scheduler;
|
||||||
|
|
||||||
|
pub mod scheduler;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_main() {
|
pub extern "C" fn rust_main() {
|
||||||
println!("hello from rust!");
|
// let mut scheduler = Scheduler::new();
|
||||||
|
// unsafe {
|
||||||
|
// scheduler.spawn(4096, move |mut io| {
|
||||||
|
// loop {
|
||||||
|
// println!("thread A");
|
||||||
|
// io.sleep(Duration::from_secs(1)).unwrap()
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// scheduler.spawn(4096, move |mut io| {
|
||||||
|
// loop {
|
||||||
|
// println!("thread B");
|
||||||
|
// io.sleep(Duration::from_millis(333)).unwrap()
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// loop { scheduler.run() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
extern crate fringe;
|
||||||
|
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
use std::time::{Instant, Duration};
|
||||||
|
use self::fringe::OwnedStack;
|
||||||
|
use self::fringe::generator::{Generator, Yielder};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WaitRequest {
|
||||||
|
timeout: Option<Instant>,
|
||||||
|
event: Option<WaitEvent>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WaitResult {
|
||||||
|
Completed,
|
||||||
|
TimedOut,
|
||||||
|
Interrupted
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Thread {
|
||||||
|
generator: Generator<WaitResult, WaitRequest, OwnedStack>,
|
||||||
|
waiting_for: WaitRequest,
|
||||||
|
interrupted: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Scheduler {
|
||||||
|
threads: Vec<Thread>,
|
||||||
|
index: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
pub fn new() -> Scheduler {
|
||||||
|
Scheduler { threads: Vec::new(), index: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn spawn<F: FnOnce(Io) + Send>(&mut self, stack_size: usize, f: F) {
|
||||||
|
let stack = OwnedStack::new(stack_size);
|
||||||
|
let thread = Thread {
|
||||||
|
generator: Generator::unsafe_new(stack, move |yielder, _| {
|
||||||
|
f(Io(yielder))
|
||||||
|
}),
|
||||||
|
waiting_for: WaitRequest {
|
||||||
|
timeout: None,
|
||||||
|
event: None
|
||||||
|
},
|
||||||
|
interrupted: false
|
||||||
|
};
|
||||||
|
self.threads.push(thread)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
if self.threads.len() == 0 { return }
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
let start_index = self.index;
|
||||||
|
loop {
|
||||||
|
self.index = (self.index + 1) % self.threads.len();
|
||||||
|
|
||||||
|
let result = {
|
||||||
|
let thread = &mut self.threads[self.index];
|
||||||
|
match thread.waiting_for {
|
||||||
|
_ if thread.interrupted => {
|
||||||
|
thread.interrupted = false;
|
||||||
|
thread.generator.resume(WaitResult::Interrupted)
|
||||||
|
}
|
||||||
|
WaitRequest { timeout: Some(instant), .. } if now >= instant =>
|
||||||
|
thread.generator.resume(WaitResult::TimedOut),
|
||||||
|
WaitRequest { event: Some(ref event), .. } if event.completed() =>
|
||||||
|
thread.generator.resume(WaitResult::Completed),
|
||||||
|
WaitRequest { timeout: None, event: None } =>
|
||||||
|
thread.generator.resume(WaitResult::Completed),
|
||||||
|
_ => {
|
||||||
|
if self.index == start_index {
|
||||||
|
// We've checked every thread and none of them are runnable.
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
None => {
|
||||||
|
// The thread has terminated.
|
||||||
|
self.threads.remove(self.index);
|
||||||
|
self.index = 0
|
||||||
|
},
|
||||||
|
Some(wait_request) => {
|
||||||
|
// The thread has suspended itself.
|
||||||
|
self.threads[self.index].waiting_for = wait_request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WaitEvent {}
|
||||||
|
|
||||||
|
impl WaitEvent {
|
||||||
|
fn completed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type IoResult<T> = Result<T, ()>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Io<'a>(&'a mut Yielder<WaitResult, WaitRequest, OwnedStack>);
|
||||||
|
|
||||||
|
impl<'a> Io<'a> {
|
||||||
|
pub fn sleep(&mut self, duration: Duration) -> IoResult<()> {
|
||||||
|
let request = WaitRequest {
|
||||||
|
timeout: Some(Instant::now() + duration),
|
||||||
|
event: None
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.0.suspend(request) {
|
||||||
|
WaitResult::TimedOut => Ok(()),
|
||||||
|
WaitResult::Interrupted => Err(()),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,16 @@ SECTIONS
|
||||||
_etext = .;
|
_etext = .;
|
||||||
} > runtime
|
} > runtime
|
||||||
|
|
||||||
|
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
||||||
|
.got : {
|
||||||
|
_GLOBAL_OFFSET_TABLE_ = .;
|
||||||
|
*(.got)
|
||||||
|
} > runtime
|
||||||
|
|
||||||
|
.got.plt : {
|
||||||
|
*(.got.plt)
|
||||||
|
} > runtime
|
||||||
|
|
||||||
.rodata :
|
.rodata :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
|
|
Loading…
Reference in New Issue