Skip to content

Commit

Permalink
Merge pull request #42 from ForAeons/vm-concurrency-update
Browse files Browse the repository at this point in the history
feat: update concurrency implementation and add example bytecode
  • Loading branch information
wxiaoyun authored Apr 13, 2024
2 parents 1cd0cbf + 8cee6ab commit 7774616
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 110 deletions.
6 changes: 3 additions & 3 deletions src/bytecode/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub type ThreadID = i64;
/// and implementation details.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum ByteCode {
/// Signal that the program has finished executing.
/// Signal that the thread has finished executing.
DONE,
/// Assign the top of the operant stack to the given symbol in the current environment.
ASSIGN(Symbol),
Expand Down Expand Up @@ -40,8 +40,8 @@ pub enum ByteCode {
LDF(usize, Vec<Symbol>),
/// Call a function with the given number of arguments.
CALL(usize),
/// Spawn a new thread.
SPAWN,
/// Spawn a new thread with the address of the instruction for the child to execute.
SPAWN(usize),
/// Join a thread.
JOIN(ThreadID),
/// Yield the current thread.
Expand Down
3 changes: 1 addition & 2 deletions src/bytecode/src/stack_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::Environment;
pub enum FrameType {
BlockFrame,
CallFrame,
ThreadFrame,
}

#[derive(Debug, Clone)]
Expand All @@ -29,8 +28,8 @@ impl StackFrame {

pub fn new_with_address(
frame_type: FrameType,
address: usize,
env: Rc<RefCell<Environment>>,
address: usize,
) -> Self {
StackFrame {
frame_type,
Expand Down
4 changes: 4 additions & 0 deletions vm/ignite/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bytecode::ThreadID;
use thiserror::Error;

#[derive(Error, Debug)]
Expand All @@ -20,6 +21,9 @@ pub enum VmError {
#[error("Runtime stack underflow")]
RuntimeStackUnderflow,

#[error("Thread not found: {0}")]
ThreadNotFound(ThreadID),

#[error("PC out of bounds: {0}")]
PcOutOfBounds(usize),

Expand Down
22 changes: 16 additions & 6 deletions vm/ignite/src/micro_code/done.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use anyhow::Result;
use std::collections::hash_map::Entry;

use crate::{Runtime, ThreadState};
use anyhow::{Ok, Result};

use crate::{Runtime, ThreadState, VmError};

/// Set the state of the current thread to done.
///
Expand All @@ -10,10 +12,18 @@ use crate::{Runtime, ThreadState};
///
/// # Errors
///
/// Infallible.
/// * If the current thread is not found in the thread state hashmap.
pub fn done(rt: &mut Runtime) -> Result<()> {
rt.current_thread.state = ThreadState::Done;
Ok(())
let tid = rt.current_thread.thread_id;
let entry = rt.thread_states.entry(tid);

match entry {
Entry::Vacant(_) => Err(VmError::ThreadNotFound(tid).into()),
Entry::Occupied(mut entry) => {
entry.insert(ThreadState::Done);
Ok(())
}
}
}

#[cfg(test)]
Expand All @@ -24,7 +34,7 @@ mod tests {
fn test_done() -> Result<()> {
let mut rt = Runtime::new(vec![]);
done(&mut rt)?;
assert_eq!(rt.current_thread.state, ThreadState::Done);
assert_eq!(rt.thread_states.get(&1), Some(&ThreadState::Done));
Ok(())
}
}
20 changes: 15 additions & 5 deletions vm/ignite/src/micro_code/join.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::collections::hash_map::Entry;

use anyhow::Result;
use bytecode::ThreadID;

use crate::{Runtime, ThreadState};
use crate::{Runtime, ThreadState, VmError};

/// Set the state of the current thread to joining the thread with the given ID.
///
Expand All @@ -13,10 +15,18 @@ use crate::{Runtime, ThreadState};
///
/// # Errors
///
/// Infallible.
/// * If the thread with the given ID is not found in the thread state hashmap.
pub fn join(rt: &mut Runtime, tid: ThreadID) -> Result<()> {
rt.current_thread.state = ThreadState::Joining(tid);
Ok(())
let current_tid = rt.current_thread.thread_id;
let entry = rt.thread_states.entry(current_tid);

match entry {
Entry::Vacant(_) => Err(VmError::ThreadNotFound(current_tid).into()),
Entry::Occupied(mut entry) => {
entry.insert(ThreadState::Joining(tid));
Ok(())
}
}
}

#[cfg(test)]
Expand All @@ -27,7 +37,7 @@ mod tests {
fn test_join() -> Result<()> {
let mut rt = Runtime::new(vec![]);
join(&mut rt, 2)?;
assert_eq!(rt.current_thread.state, ThreadState::Joining(2));
assert_eq!(rt.thread_states.get(&1), Some(&ThreadState::Joining(2)));
Ok(())
}
}
2 changes: 1 addition & 1 deletion vm/ignite/src/micro_code/reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ mod tests {

let some_frame = StackFrame::new(FrameType::CallFrame, Rc::clone(&env_a));
let block_frame =
StackFrame::new_with_address(FrameType::BlockFrame, 123, Rc::clone(&env_c));
StackFrame::new_with_address(FrameType::BlockFrame, Rc::clone(&env_c), 123);
let call_frame = StackFrame::new(FrameType::CallFrame, Rc::clone(&env_b));

rt.current_thread.runtime_stack.push(some_frame);
Expand Down
21 changes: 12 additions & 9 deletions vm/ignite/src/micro_code/spawn.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use anyhow::Result;

use crate::Runtime;
use crate::{Runtime, ThreadState};

/// Spawn a new thread that clones the main thread at the time of the spawn.
/// The new thread is added to the ready queue.
/// The new thread is added to the thread state hashmap with a state of Ready.
/// The new thread is given a unique thread ID.
/// The new thread is added to the ready queue.
/// This thread ID is pushed onto the operand stack of the parent thread.
/// 0 is pushed onto the operand stack of the child thread.
///
Expand All @@ -15,17 +16,19 @@ use crate::Runtime;
/// # Errors
///
/// Infallible.
pub fn spawn(rt: &mut Runtime) -> Result<()> {
pub fn spawn(rt: &mut Runtime, addr: usize) -> Result<()> {
rt.thread_count += 1;

let new_thread_id = rt.thread_count;
let mut new_thread = rt.current_thread.spawn_new(new_thread_id);
let child_thread_id = rt.thread_count;
let mut child_thread = rt.current_thread.spawn_new(child_thread_id, addr);
// Add the child thread to the thread state hashmap.
rt.thread_states.insert(child_thread_id, ThreadState::Ready);

// The child thread ID is pushed onto the operand stack of the parent thread.
rt.current_thread.operand_stack.push(new_thread_id.into());
// 0 is pushed onto the operand stack of the child thread.
new_thread.operand_stack.push(0.into());
child_thread.operand_stack.push(0.into());
// The child thread ID is pushed onto the operand stack of the parent thread.
rt.current_thread.operand_stack.push(child_thread_id.into());

rt.ready_queue.push_back(new_thread);
rt.ready_queue.push_back(child_thread);
Ok(())
}
18 changes: 14 additions & 4 deletions vm/ignite/src/micro_code/yield_.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::hash_map::Entry;

use anyhow::Result;

use crate::{Runtime, ThreadState};
use crate::{Runtime, ThreadState, VmError};

/// Yield the current thread.
/// This will set the yield flag of the current thread to true.
Expand All @@ -14,8 +16,16 @@ use crate::{Runtime, ThreadState};
///
/// Infallible.
pub fn yield_(rt: &mut Runtime) -> Result<()> {
rt.current_thread.state = ThreadState::Yielded;
Ok(())
let tid = rt.current_thread.thread_id;
let entry = rt.thread_states.entry(tid);

match entry {
Entry::Vacant(_) => Err(VmError::ThreadNotFound(tid).into()),
Entry::Occupied(mut entry) => {
entry.insert(ThreadState::Yielded);
Ok(())
}
}
}

#[cfg(test)]
Expand All @@ -26,7 +36,7 @@ mod tests {
fn test_yield() -> Result<()> {
let mut rt = Runtime::new(vec![]);
yield_(&mut rt)?;
assert_eq!(rt.current_thread.state, ThreadState::Yielded);
assert_eq!(rt.thread_states.get(&1), Some(&ThreadState::Yielded));
Ok(())
}
}
Loading

0 comments on commit 7774616

Please sign in to comment.