Some code refactoring, PLUS adding core RT threading

parent af8b1b22
......@@ -26,6 +26,7 @@ doctest = false
default = ["aot"]
aot = []
jit = []
realtime = []
[build-dependencies]
cc = "*"
......@@ -34,7 +35,7 @@ built = "*"
[dependencies]
mu_ast = {path = "src/ast"}
mu_utils = {path = "src/utils"}
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", branch = "rust-1.30.1", version = "^0.3.18" }
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", branch = "master", version = "^0.4.0" }
libc="*"
field-offset = "*"
......
......@@ -26,5 +26,5 @@ lazy_static = "0.2.11"
log = "*"
simple_logger = "*"
regex = "0.2.2"
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", branch = "rust-1.30.1", version = "^0.3.18" }
#rodal = { path = "../../rodal", version = "*" }
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", branch = "master", version = "^0.4.0" }
libc = "*"
\ No newline at end of file
// Copyright 2019 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(target_os = "linux")]
pub use ir_rt_linux::*;
pub type RTMuThreadID = SysThreadID;
pub type RTMuThreadAttr = SysThreadAttr;
pub type RTMuPriority = SysPriority;
pub type RTMuTime = SysTime;
pub type RTMuAffinityMask = SysAffinityMask;
pub type RTMuAffinitySize = SysAffinitySize;
// Copyright 2019 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate libc;
pub type SysThreadID = libc::pthread_t;
pub type SysThreadAttr = libc::pthread_attr_t;
pub type SysPriority = libc::c_int;
pub type SysTime = libc::timespec;
pub type SysAffinityMask = libc::cpu_set_t;
pub type SysAffinitySize = libc::size_t;
......@@ -78,3 +78,9 @@ pub mod inst;
pub mod op;
pub mod ptr;
pub mod types;
#[cfg(feature="realtime")]
pub mod ir_rt;
#[cfg(all(feature="realtime", target_os = "linux"))]
pub mod ir_rt_linux;
\ No newline at end of file
......@@ -36,4 +36,4 @@ stderrlog = "*"
aligned_alloc = "*"
crossbeam = "0.3.0"
field-offset = "*"
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", branch = "rust-1.30.1", version = "^0.3.18" }
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", branch = "master", version = "^0.4.0" }
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
uintptr_t immmix_get_stack_ptr() {
uintptr_t rsp;
// get current rsp, rbp (this C func frame)
__asm__(
"mov %%rsp, %0 \n"
: "=rm" (rsp)
);
return rsp;
}
int get_registers_count() {
return 16;
}
uintptr_t* get_registers () {
uintptr_t rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15;
__asm__(
"mov %%rax, %0 \n"
"mov %%rbx, %1 \n"
"mov %%rcx, %2 \n"
"mov %%rdx, %3 \n"
"mov %%rbp, %4 \n"
"mov %%rsp, %5 \n"
"mov %%rsi, %5 \n"
"mov %%rdi, %6 \n"
"mov %%r8, %7 \n"
"mov %%r9, %8 \n"
"mov %%r10, %10\n"
"mov %%r11, %11\n"
"mov %%r12, %12\n"
"mov %%r13, %13\n"
"mov %%r14, %14\n"
"mov %%r15, %15\n"
: "=m" (rax),
"=m" (rbx),
"=m" (rcx),
"=m" (rdx),
"=m" (rbp),
"=m" (rsp),
"=m" (rsi),
"=m" (rdi),
"=m" (r8),
"=m" (r9),
"=m" (r10),
"=m" (r11),
"=m" (r12),
"=m" (r13),
"=m" (r14),
"=m" (r15)
:
:
);
uintptr_t* ret = (uintptr_t*) malloc(sizeof(uintptr_t) * 16);
ret[0] = rax;
ret[1] = rbx;
ret[2] = rcx;
ret[3] = rdx;
ret[4] = rbp;
ret[5] = rsp;
ret[6] = rsi;
ret[7] = rdi;
ret[8] = r8;
ret[9] = r9;
ret[10] = r10;
ret[11] = r11;
ret[12] = r12;
ret[13] = r13;
ret[14] = r14;
ret[15] = r15;
return ret;
}
// __thread uintptr_t low_water_mark;
int lwm_initialized = 0;
static pthread_key_t low_water_mark;
void set_low_water_mark () {
uintptr_t rsp;
if(lwm_initialized == 0){
lwm_initialized = 1;
int result = pthread_key_create(&low_water_mark, NULL);
if(result != 0){
printf("set_low_water_mark(): PThread key create failed with error code = %d\n", result);
assert(0);
}
}
// get current rsp, rbp (this C func frame)
__asm__(
"mov %%rsp, %0 \n"
: "=rm" (rsp)
);
int result = pthread_setspecific(low_water_mark, (const void *) rsp);
if(result != 0){
printf("set_low_water_mark(): PThread set specific failed with error code = %d\n", result);
assert(0);
}
}
uintptr_t get_low_water_mark() {
if(lwm_initialized == 0){
printf("get_low_water_mark(): PThread key MUST be initialized before first use!!\n");
}
void * result = pthread_getspecific(low_water_mark);
if(result == NULL){
printf("get_low_water_mark(): NO pthread key found for current thread!!\n");
assert(0);
}
return (uintptr_t) result;
}
......@@ -33,6 +33,9 @@ use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
#[cfg(feature = "realtime")]
use ast::ir_rt::*;
/// a 4mb Mu stack
pub const STACK_SIZE: ByteSize = (4 << 20); // 4mb
......@@ -289,6 +292,9 @@ pub enum MuStackState {
Unknown,
}
// RTMU - add rt thread metadata to MuThread
// this includes priority, affinity, ...
/// MuThread represents metadata for a Mu thread.
/// A Mu thread in Zebu is basically an OS thread (pthread). However, we need to maintain our own
/// thread local info, such as allocator, stack, user-level thread local pointer, exception object,
......@@ -296,6 +302,7 @@ pub enum MuStackState {
/// We keep the pointer to MuThread for each thread, so that we can query our MuThread metadata.
/// The user-level thread local pointer can be found within MuThread.
/// The compiler emits code that uses offsets to some fields in this struct.
#[cfg(not(feature = "realtime"))]
#[repr(C)]
pub struct MuThread {
pub hdr: MuEntityHeader,
......@@ -313,7 +320,32 @@ pub struct MuThread {
/// a pointer to the virtual machine
pub vm: Arc<VM>,
}
#[cfg(feature = "realtime")]
#[repr(C)]
pub struct MuThread {
pub hdr: MuEntityHeader,
// the next 3 attributes are specific to realtime
pub priority: MuPriority,
pub affinity: MuAffinityMask,
pub deadline: MuTime,
/// the allocator from memory manager
pub allocator: mm::Mutator,
/// current stack (a thread can execute different stacks, but one stack at a time)
pub stack: *mut MuStack,
/// native stack pointer before we switch to this mu stack
/// (when the thread exits, we restore to native stack, and allow proper destruction)
pub native_sp_loc: Address,
/// user supplied thread local address, can be zero
pub user_tls: Address,
/// exception object being thrown by the thread
pub exception_obj: Address,
/// a pointer to the virtual machine
pub vm: Arc<VM>,
}
unsafe impl Sync for MuThread {}
unsafe impl Send for MuThread {}
// a few field offsets the compiler uses
......@@ -327,6 +359,15 @@ lazy_static! {
offset_of!(MuThread=>exception_obj).get_byte_offset();
}
#[cfg(feature = "realtime")]
macro_rules! write_rt_spec {
() => (
write!(f, "- priority @{:?}\n", &self.priority as *const MuPriority).unwrap();
write!(f, "- affinity @{:?}\n", &self.affinity as *const MuAffinityMask).unwrap();
write!(f, "- deadline @{:?}\n", &self.deadline as *const MuTime).unwrap();
)
}
impl fmt::Display for MuThread {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
......@@ -334,33 +375,37 @@ impl fmt::Display for MuThread {
"MuThread @{:?}: {}\n",
self as *const MuThread, self.hdr
)
.unwrap();
.unwrap();
write!(f, "- header @{:?}\n", &self.hdr as *const MuEntityHeader).unwrap();
#[cfg(feature = "realtime")]
write_rt_spec!();
write!(
f,
"- allocator @{:?}\n",
&self.allocator as *const mm::Mutator
)
.unwrap();
.unwrap();
write!(f, "- stack @{:?}\n", &self.stack as *const *mut MuStack).unwrap();
write!(
f,
"- native sp @{:?}: {}\n",
&self.native_sp_loc as *const Address, self.native_sp_loc
)
.unwrap();
.unwrap();
write!(
f,
"- user_tls @{:?}: {}\n",
&self.user_tls as *const Address, self.user_tls
)
.unwrap();
.unwrap();
write!(
f,
"- exc obj @{:?}: {}\n",
&self.exception_obj as *const Address, self.exception_obj
)
.unwrap();
.unwrap();
Ok(())
}
......@@ -419,6 +464,8 @@ pub fn check_result() -> c_int {
}
impl MuThread {
// RTMU - add another new_thread_normal or e.g. add new_thread_rt
/// creates a new Mu thread with normal execution
pub fn new_thread_normal(
mut stack: Box<MuStack>,
......@@ -433,6 +480,11 @@ impl MuThread {
vm.push_join_handle(join_handle);
}
// RTMU - lunching a thread needs more customizations
// instead of using the safe thread::Builder thing,
// we'll have to use libc::pthread for linux
// we'll add another mu_thread_launch for cfg rtmu feature
/// creates and launches a mu thread, returns a JoinHandle and address to its MuThread structure
fn mu_thread_launch(
id: MuID,
......@@ -483,6 +535,8 @@ impl MuThread {
)
}
// RTMU - add rt thread metadata
/// creates metadata for a Mu thread
fn new(
id: MuID,
......@@ -609,6 +663,8 @@ impl MuThread {
}
}
// RTMU - we will need to keep more info, e.g. pthread_t * & ...
/// PrimordialThreadInfo stores information about primordial thread/entry function for a boot image
#[derive(Debug)]
pub struct PrimordialThreadInfo {
......@@ -659,7 +715,29 @@ pub unsafe extern "C" fn muentry_new_thread_exceptional(
muthread
}
// RTMU - this function is emitted in machine code for NEWTHREAD,
// so it should be changed accordingly
// Creates a new thread
#[cfg(not(feature="realtime"))]
#[no_mangle]
pub unsafe extern "C" fn muentry_new_thread_normal(
stack: *mut MuStack,
thread_local: Address,
) -> *mut MuThread {
let vm = MuThread::current_mut().vm.clone();
let (join_handle, muthread) = MuThread::mu_thread_launch(
vm.next_id(),
Box::from_raw(stack),
thread_local,
None,
vm.clone(),
);
vm.push_join_handle(join_handle);
muthread
}
#[cfg(feature="realtime")]
#[no_mangle]
pub unsafe extern "C" fn muentry_new_thread_normal(
stack: *mut MuStack,
......@@ -676,3 +754,4 @@ pub unsafe extern "C" fn muentry_new_thread_normal(
vm.push_join_handle(join_handle);
muthread
}
......@@ -43,7 +43,7 @@ impl RuntimeEntrypoint {
}
}
// decl: thread.rs
// decl: _thread.rs
lazy_static! {
// impl: runtime_ARCH_OS.c
pub static ref GET_THREAD_LOCAL : RuntimeEntrypoint = RuntimeEntrypoint::new(
......@@ -59,7 +59,7 @@ lazy_static! {
vec![ADDRESS_TYPE.clone()],
vec![]);
// impl: thread.rs
// impl: _thread.rs
pub static ref NEW_STACK: RuntimeEntrypoint = RuntimeEntrypoint::new(
"muentry_new_stack",
vec![ADDRESS_TYPE.clone(), ADDRESS_TYPE.clone()],
......
......@@ -125,7 +125,7 @@ pub fn resolve_symbol(symbol: MuName) -> Address {
// result
// }
// *************************************************
// This code has been moved to thread.rs \
// This code has been moved to _thread.rs \
// due to the linkage with libruntime.a happenning there once
/// ValueLocation represents the runtime location for a value.
......@@ -237,6 +237,8 @@ pub extern "C" fn mu_trace_level_log() {
#[no_mangle]
pub static mut LAST_TIME: c_ulong = 0;
// RTMU mu_main needs changes
/// the main function for executable boot image, this function will be called from C
#[no_mangle]
pub extern "C" fn mu_main(
......
This diff is collapsed.
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// MU (non-real-time) specific threading stuff goes to this module
/// Currently it's empty
use super::*;
impl MuThread {
/// creates a new Mu thread with normal execution
pub fn new_thread_normal(
mut stack: Box<MuStack>,
threadlocal: Address,
vals: Vec<ValueLocation>,
vm: Arc<VM>,
) {
// set up arguments on stack
stack.setup_args(vals);
let (join_handle, _) =
MuThread::mu_thread_launch(vm.next_id(), stack, threadlocal, None, vm.clone());
vm.push_join_handle(join_handle);
}
/// creates and launches a mu thread, returns a JoinHandle and address to its MuThread structure
fn mu_thread_launch(
id: MuID,
stack: Box<MuStack>,
user_tls: Address,
exception: Option<Address>,
vm: Arc<VM>,
) -> (JoinHandle<()>, *mut MuThread) {
let new_sp = stack.sp;
let mut thread = Box::new(MuThread::new(id, mm::new_mutator(), stack, user_tls, vm));
{
// set mutator for each allocator
let mutator_ptr = &mut thread.allocator as *mut mm::Mutator;
thread.allocator.update_mutator_ptr(mutator_ptr);
}
// we need to return the pointer, but we cannot send it to other thread
let muthread_ptr = Box::into_raw(thread);
let muthread = unsafe { Box::from_raw(muthread_ptr) };
(
match thread::Builder::new()
.name(format!("Mu Thread #{}", id))
.spawn(move || {
let muthread = Box::into_raw(muthread);
// set thread local
unsafe { set_thread_local(muthread) };
let addr = unsafe { muentry_get_thread_local() };
let sp_threadlocal_loc = addr + *NATIVE_SP_LOC_OFFSET;
debug!("new sp: 0x{:x}", new_sp);
debug!("sp_store: 0x{:x}", sp_threadlocal_loc);
unsafe {
match exception {
Some(e) => muthread_start_exceptional(e, new_sp, sp_threadlocal_loc),
None => muthread_start_normal(new_sp, sp_threadlocal_loc),
}
// Thread finished, delete it's data
Box::from_raw(muthread);
}
}) {
Ok(handle) => handle,
Err(_) => panic!("failed to create a thread"),
},
muthread_ptr,
)
}
/// creates metadata for a Mu thread
fn new(
id: MuID,
allocator: mm::Mutator,
stack: Box<MuStack>,
user_tls: Address,
vm: Arc<VM>,
) -> MuThread {
MuThread {
hdr: MuEntityHeader::unnamed(id),
allocator,
stack: Box::into_raw(stack),
native_sp_loc: unsafe { Address::zero() },
user_tls,
vm,
exception_obj: unsafe { Address::zero() },
}
}
}
// Creates a new thread
#[no_mangle]
pub unsafe extern "C" fn muentry_new_thread_exceptional(
stack: *mut MuStack,
thread_local: Address,
exception: Address,
) -> *mut MuThread {
let vm = MuThread::current_mut().vm.clone();
let (join_handle, muthread) = MuThread::mu_thread_launch(
vm.next_id(),
Box::from_raw(stack),
thread_local,
Some(exception),
vm.clone(),
);
vm.push_join_handle(join_handle);
muthread
}
// Creates a new thread
#[cfg(not(feature = "realtime"))]
#[no_mangle]
pub unsafe extern "C" fn muentry_new_thread_normal(
stack: *mut MuStack,
thread_local: Address,
) -> *mut MuThread {
let vm = MuThread::current_mut().vm.clone();
let (join_handle, muthread) = MuThread::mu_thread_launch(
vm.next_id(),
Box::from_raw(stack),
thread_local,
None,
vm.clone(),
);
vm.push_join_handle(join_handle);
muthread
}
// Copyright 2019 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// RTMU specific threading stuff goes here
use super::*;
pub use ast::ir_rt::*;
#[cfg(target_os = "linux")]
use super::thread_rtmu_linux::*;
pub struct RTAttr {
pub priority: RTMuPriority,
pub affinity: RTMuAffinityMask,
pub affinity_size: RTMuAffinitySize,
// pub deadline: RTMuTime, TODO add the logic and uncomment
}
impl RTAttr {
/// returns a `RTAttr` object with default attributes
pub fn new() -> RTAttr {
let mut attr = RTAttr {
priority: RTMU_DEFAULT_PRIORITY,
affinity: (),
affinity_size: RTMU_DEFAULT_AFF_SIZE,
};
sys_affinity_init(&mut attr.affinity);
attr
}
// pub fn to_rtmu_thread_attr(&self) -> RTMuThreadAttr {
// let mut attr: RTMuThreadAttr = unsafe { mem::zeroed() };
//
// let res = sys_thread_attr_init(&attr as *mut RTMuThreadAttr);
// // TODO - add error codes and error to string function
// assert_eq!(res, RTMU_SUCCESS, "Failed to initialize sys thread attr with error#{}", res);
//
// let res = sys_thread_attr_set_affinity(
// &attr as *mut RTMuThreadAttr,
// self.affinity_size,
// &self.affinity as *const RTMuAffinityMask,
// );
// assert_eq!(res, RTMU_SUCCESS, "Failed to set sys thread attr->affinity with error#{}", res);
//
// let res = sys_thread_attr_set_priority(
// attr,
// self.priority,
// );
// assert_eq!(res, RTMU_SUCCESS, "Failed to set sys thread attr->priority with error#{}", res);
//
// attr
// }
}
struct LibcThreadArg {
pub muthread: Box<T>,
pub exception: Option<Address>,
pub new_sp: Address,
}
impl MuThread {
pub fn new_thread_normal(
attr: &RTAttr,
mut stack: Box<MuStack>,
threadlocal: Address,
vals: Vec<ValueLocation>,
vm: Arc<VM>,
) {
// set up arguments on stack
stack.setup_args(vals);
let (join_handle, _) =
MuThread::mu_thread_launch(attr, vm.next_id(), stack, threadlocal, None, vm.clone());
vm.push_join_handle(join_handle);
}
/// creates and launches a mu thread, returns a JoinHandle and address to its MuThread structure
fn mu_thread_launch(
attr: &RTAttr,
id: MuID,
stack: Box<MuStack>,
user_tls: Address,
exception: Option<Address>,
vm: Arc<VM>,
) -> (JoinHandle<()>, *mut MuThread) {
let new_sp = stack.sp;
let mut thread: Box<MuThread> = Box::new(MuThread::new(attr, id, stack, user_tls, vm));
// we need to return the pointer, but we cannot send it to other thread
let muthread_ptr = Box::into_raw(thread);
let muthread = unsafe { Box::from_raw(muthread_ptr) };
(
match thread::Builder::new()
.name(format!("Mu Thread #{}", id))
.spawn(move || {
// Save the sys thread ID before starting
thread.sys_thread_id = sys_thread_self();
// apply the initial rt attributes to the sys thread
let res = sys_thread_apply_rtattr(thread.sys_thread_id, &attr);
assert_eq!(res, RTMU_SUCCESS, "Failed to apply new thread attributes with error #{}", res);
let muthread = Box::into_raw(muthread);
// set thread local
unsafe { set_thread_local(muthread) };
let addr = unsafe { muentry_get_thread_local() };
let sp_threadlocal_loc = addr + *NATIVE_SP_LOC_OFFSET;
debug!("new sp: 0x{:x}", new_sp);
debug!("sp_store: 0x{:x}", sp_threadlocal_loc);
unsafe {
match exception {
Some(e) => muthread_start_exceptional(e, new_sp, sp_threadlocal_loc),
None => muthread_start_normal(new_sp, sp_threadlocal_loc),
}
// Thread finished, delete it's data
Box::from_raw(muthread);
}
}) {
Ok(handle) => handle,