thread.rs 23.6 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
qinsoon's avatar
qinsoon committed
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3 4 5
// 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
qinsoon's avatar
qinsoon committed
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
qinsoon's avatar
qinsoon committed
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9 10 11 12 13 14
// 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.

qinsoon's avatar
qinsoon committed
15
use ast::ir::*;
qinsoon's avatar
qinsoon committed
16 17 18
use ast::ptr::*;
use ast::types::*;
use vm::VM;
19
use runtime::ValueLocation;
20
use runtime::mm;
qinsoon's avatar
qinsoon committed
21

qinsoon's avatar
qinsoon committed
22
use utils::ByteSize;
qinsoon's avatar
qinsoon committed
23
use utils::Address;
qinsoon's avatar
qinsoon committed
24
use utils::Word;
25
use utils::POINTER_SIZE;
qinsoon's avatar
qinsoon committed
26 27 28
use utils::mem::memmap;
use utils::mem::memsec;

29
use std;
qinsoon's avatar
qinsoon committed
30
use std::ptr;
qinsoon's avatar
qinsoon committed
31 32
use std::thread;
use std::thread::JoinHandle;
33
use std::sync::Arc;
34
use std::fmt;
qinsoon's avatar
qinsoon committed
35

qinsoon's avatar
qinsoon committed
36
/// a 4mb Mu stack
37
#[cfg(not(feature = "sel4-rumprun"))]
qinsoon's avatar
qinsoon committed
38
pub const STACK_SIZE: ByteSize = (4 << 20); // 4mb
39 40
/// a .25mb Mu stack for sel4-rumprun
#[cfg(feature = "sel4-rumprun")]
41
pub const STACK_SIZE: ByteSize = (4 << 16); // 256kb
qinsoon's avatar
qinsoon committed
42

qinsoon's avatar
qinsoon committed
43 44
/// operating system page size
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
qinsoon's avatar
qinsoon committed
45
pub const PAGE_SIZE: ByteSize = (4 << 10); // 4kb
qinsoon's avatar
qinsoon committed
46

qinsoon's avatar
qinsoon committed
47
// MuThread and MuStack are MuEntity (has MuID and an optional MuName)
qinsoon's avatar
qinsoon committed
48 49 50
impl_mu_entity!(MuThread);
impl_mu_entity!(MuStack);

qinsoon's avatar
qinsoon committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/// MuStack represents metadata for a Mu stack.
/// A Mu stack is explicitly different from a native stack that comes with the thread from
/// OS, and is managed by the VM. A Mu stack is logically independent from a Mu thread,
/// as we allow creation of stacks to be independent of thread creation, we allow binding stack
/// to a new thread and swap stack to rebind stacks. A Mu stack is seen as a piece of memory
/// that contains function execution records.

/// Zebu stack has a layout as below:
///                              <- stack grows this way <-
///    lo addr                                                    hi addr
///     | overflow guard page | actual stack ..................... | underflow guard page|
///     |                     |                                    |                     |

/// We use guard page for overflow/underflow detection.
//  FIXME: we need to capture the signal (See Issue #50)
//  FIXME: this mechanics may ignore cases when frame size is larger than page size (Issue #49)
67
#[repr(C)]
qinsoon's avatar
qinsoon committed
68
pub struct MuStack {
qinsoon's avatar
qinsoon committed
69
    pub hdr: MuEntityHeader,
70

qinsoon's avatar
qinsoon committed
71
    /// stack size
qinsoon's avatar
qinsoon committed
72
    size: ByteSize,
qinsoon's avatar
qinsoon committed
73

qinsoon's avatar
qinsoon committed
74 75 76 77
    //    lo addr                                                    hi addr
    //     | overflow guard page | actual stack ..................... | underflow guard page|
    //     |                     |                                    |                     |
    // overflowGuard           lowerBound                           upperBound
qinsoon's avatar
qinsoon committed
78 79
    //                                                              underflowGuard
    /// start address of overflow guard page
qinsoon's avatar
qinsoon committed
80
    overflow_guard: Address,
qinsoon's avatar
qinsoon committed
81
    /// lower bound of the stack
qinsoon's avatar
qinsoon committed
82
    lower_bound: Address,
qinsoon's avatar
qinsoon committed
83
    /// upper bound of the stack
qinsoon's avatar
qinsoon committed
84
    upper_bound: Address,
qinsoon's avatar
qinsoon committed
85
    /// start address of underflow guard page
qinsoon's avatar
qinsoon committed
86
    underflow_guard: Address,
qinsoon's avatar
qinsoon committed
87

qinsoon's avatar
qinsoon committed
88 89
    // these frame/instruction pointers should only be used when the stack is not active
    /// stack pointer to the stack (before it becomes inactive)
qinsoon's avatar
qinsoon committed
90
    sp: Address,
qinsoon's avatar
qinsoon committed
91
    /// frame pointer to the stack (before it becomes inactive)
qinsoon's avatar
qinsoon committed
92
    bp: Address,
qinsoon's avatar
qinsoon committed
93
    /// instruction pointer (before the stack becomes inactive)
qinsoon's avatar
qinsoon committed
94
    ip: Address,
qinsoon's avatar
qinsoon committed
95 96 97

    /// state of this stack: ready (inactive), active, dead
    //  TODO: we are not using this for now
98
    state: MuStackState,
qinsoon's avatar
qinsoon committed
99 100

    /// the Mmap that keeps this memory alive
qinsoon's avatar
qinsoon committed
101
    #[allow(dead_code)]
102
    mmap: Option<memmap::MmapMut>
qinsoon's avatar
qinsoon committed
103
}
104 105 106 107
lazy_static!{
    pub static ref MUSTACK_SP_OFFSET : usize =
        offset_of!(MuStack=>sp).get_byte_offset();
}
qinsoon's avatar
qinsoon committed
108
impl MuStack {
qinsoon's avatar
qinsoon committed
109
    /// creates a new MuStack for given entry function and function address
110
    pub fn new(id: MuID, func_addr: Address, stack_arg_size: usize) -> MuStack {
qinsoon's avatar
qinsoon committed
111
        // allocate memory for the stack
112
        let mut anon_mmap = {
qinsoon's avatar
qinsoon committed
113 114
            // reserve two guard pages more than we need for the stack
            let total_size = PAGE_SIZE * 2 + STACK_SIZE;
115
            match memmap::MmapMut::map_anon(total_size) {
qinsoon's avatar
qinsoon committed
116
                Ok(m) => m,
117
                Err(_) => panic!("failed to mmap for a stack")
qinsoon's avatar
qinsoon committed
118
            }
qinsoon's avatar
qinsoon committed
119
        };
qinsoon's avatar
qinsoon committed
120

121
        let mmap_start = Address::from_ptr(anon_mmap.as_mut_ptr());
qinsoon's avatar
qinsoon committed
122
        debug_assert!(mmap_start.is_aligned_to(PAGE_SIZE));
qinsoon's avatar
qinsoon committed
123 124

        // calculate the addresses
qinsoon's avatar
qinsoon committed
125
        let overflow_guard = mmap_start;
126 127
        let lower_bound = mmap_start + PAGE_SIZE;
        let upper_bound = lower_bound + STACK_SIZE;
qinsoon's avatar
qinsoon committed
128
        let underflow_guard = upper_bound;
qinsoon's avatar
qinsoon committed
129 130

        // protect the guard pages
qinsoon's avatar
qinsoon committed
131
        unsafe {
qinsoon's avatar
qinsoon committed
132 133 134
            memsec::mprotect(
                overflow_guard.to_ptr_mut::<u8>(),
                PAGE_SIZE,
135
                memsec::Prot::NoAccess
qinsoon's avatar
qinsoon committed
136 137 138 139
            );
            memsec::mprotect(
                underflow_guard.to_ptr_mut::<u8>(),
                PAGE_SIZE,
140
                memsec::Prot::NoAccess
qinsoon's avatar
qinsoon committed
141
            );
qinsoon's avatar
qinsoon committed
142
        }
qinsoon's avatar
qinsoon committed
143

144 145
        // Set up the stack
        let mut sp = upper_bound;
146 147 148

        // Allocate space for the arguments
        sp -= stack_arg_size;
149 150 151

        // Push entry as the return address
        sp -= POINTER_SIZE;
152 153 154
        unsafe {
            sp.store(func_addr);
        }
155 156 157

        // Push a null frame pointer
        sp -= POINTER_SIZE;
158 159 160
        unsafe {
            sp.store(Address::zero());
        }
161

162 163 164 165 166 167 168
        debug!("creating stack {} with entry address {:?}", id, func_addr);
        debug!("overflow_guard : {}", overflow_guard);
        debug!("lower_bound    : {}", lower_bound);
        debug!("stack_pointer  : {}", sp);
        debug!("upper_bound    : {}", upper_bound);
        debug!("underflow_guard: {}", underflow_guard);

qinsoon's avatar
qinsoon committed
169 170
        MuStack {
            hdr: MuEntityHeader::unnamed(id),
171
            state: MuStackState::Unknown,
qinsoon's avatar
qinsoon committed
172

qinsoon's avatar
qinsoon committed
173 174 175 176 177
            size: STACK_SIZE,
            overflow_guard: overflow_guard,
            lower_bound: lower_bound,
            upper_bound: upper_bound,
            underflow_guard: upper_bound,
qinsoon's avatar
qinsoon committed
178

179
            sp: sp,
qinsoon's avatar
qinsoon committed
180
            bp: upper_bound,
qinsoon's avatar
qinsoon committed
181 182
            ip: unsafe { Address::zero() },

183
            mmap: Some(anon_mmap)
qinsoon's avatar
qinsoon committed
184
        }
qinsoon's avatar
qinsoon committed
185
    }
186

qinsoon's avatar
qinsoon committed
187 188 189
    /// sets up arguments for the stack's entry function, so it is ready to be executed.
    /// We use a special calling convention for the entry function: we push all the argument
    /// registers for the platform to the stack. If the argument register is used, we get
190
    /// its value and push the value. Otherwise we push an empty value (0). muthread_start_normal
qinsoon's avatar
qinsoon committed
191
    /// will consume those values on the stack by popping them one by one.
192
    /// NOTE: any changes to here need to be reflected in muthread_start_normal, which consumes
qinsoon's avatar
qinsoon committed
193 194 195 196
    /// those values pushed to the stack
    pub fn setup_args(&mut self, vals: Vec<ValueLocation>) {
        use utils::Word;
        use utils::WORD_SIZE;
197
        use compiler::backend::RegGroup;
198
        use compiler::backend::{ARGUMENT_FPRS, ARGUMENT_GPRS};
199 200 201 202

        let mut gpr_used = vec![];
        let mut fpr_used = vec![];

qinsoon's avatar
qinsoon committed
203
        // load values for each argument
204 205 206 207 208 209 210
        for i in 0..vals.len() {
            let ref val = vals[i];
            let (reg_group, word) = val.load_value();

            match reg_group {
                RegGroup::GPR => gpr_used.push(word),
                RegGroup::FPR => fpr_used.push(word),
211
                RegGroup::GPREX => unimplemented!()
212 213 214
            }
        }

qinsoon's avatar
qinsoon committed
215 216
        // store floating point argument registers
        for i in 0..ARGUMENT_FPRS.len() {
217
            self.sp -= WORD_SIZE;
218 219 220 221 222 223 224 225
            let val = {
                if i < fpr_used.len() {
                    fpr_used[i]
                } else {
                    0 as Word
                }
            };

226
            debug!("store {} to {}", val, self.sp);
qinsoon's avatar
qinsoon committed
227
            unsafe {
228
                self.sp.store(val);
qinsoon's avatar
qinsoon committed
229
            }
230 231
        }

qinsoon's avatar
qinsoon committed
232 233
        // store general purpose argument registers
        for i in 0..ARGUMENT_GPRS.len() {
234
            self.sp -= WORD_SIZE;
235 236 237 238 239 240 241 242
            let val = {
                if i < gpr_used.len() {
                    gpr_used[i]
                } else {
                    0 as Word
                }
            };

243
            debug!("store {} to {}", val, self.sp);
qinsoon's avatar
qinsoon committed
244
            unsafe {
245
                self.sp.store(val);
qinsoon's avatar
qinsoon committed
246
            }
247 248
        }

qinsoon's avatar
qinsoon committed
249 250
        if cfg!(debug_assertions) {
            self.print_stack(Some(20));
251
        }
252
    }
qinsoon's avatar
qinsoon committed
253 254 255

    /// prints n * POINTER_SIZE slots from the stack top (upper bound)
    /// prints either n slots or until meet the stack bottom (lower bound)
256
    pub fn print_stack(&self, n_entries: Option<usize>) {
257 258
        use utils::Word;
        use utils::WORD_SIZE;
qinsoon's avatar
qinsoon committed
259

260
        let mut cursor = self.upper_bound - WORD_SIZE;
261
        let mut count = 0;
qinsoon's avatar
qinsoon committed
262

qinsoon's avatar
qinsoon committed
263
        debug!("0x{:x} | UPPER_BOUND", self.upper_bound);
264
        while cursor >= self.lower_bound {
qinsoon's avatar
qinsoon committed
265 266
            let val = unsafe { cursor.load::<Word>() };

267
            if cursor == self.sp {
qinsoon's avatar
qinsoon committed
268 269 270
                debug!("0x{:x} | 0x{:x} ({}) <- SP", cursor, val, val);
            } else {
                debug!("0x{:x} | 0x{:x} ({})", cursor, val, val);
271
            }
qinsoon's avatar
qinsoon committed
272

273
            cursor -= WORD_SIZE;
274
            count += 1;
qinsoon's avatar
qinsoon committed
275

276
            if n_entries.is_some() && count > n_entries.unwrap() {
qinsoon's avatar
qinsoon committed
277
                debug!("...");
278 279 280
                break;
            }
        }
qinsoon's avatar
qinsoon committed
281

qinsoon's avatar
qinsoon committed
282
        debug!("0x{:x} | LOWER_BOUND", self.lower_bound);
283
    }
qinsoon's avatar
qinsoon committed
284 285
}

qinsoon's avatar
qinsoon committed
286
/// MuStackState represents the state for a mu stack
qinsoon's avatar
qinsoon committed
287
pub enum MuStackState {
qinsoon's avatar
qinsoon committed
288 289 290
    /// ready to resume when values of given types are supplied (can be empty)
    Ready(Vec<P<MuType>>),
    /// running mu code
qinsoon's avatar
qinsoon committed
291
    Active,
qinsoon's avatar
qinsoon committed
292
    /// can be destroyed
293
    Dead,
294
    Unknown
qinsoon's avatar
qinsoon committed
295 296
}

qinsoon's avatar
qinsoon committed
297 298
/// 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
qinsoon's avatar
qinsoon committed
299 300
/// thread local info, such as allocator, stack, user-level thread local pointer, exception object,
/// and an Arc reference to the VM.
qinsoon's avatar
qinsoon committed
301 302 303
/// 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.
304
#[repr(C)]
qinsoon's avatar
qinsoon committed
305
pub struct MuThread {
qinsoon's avatar
qinsoon committed
306
    pub hdr: MuEntityHeader,
qinsoon's avatar
qinsoon committed
307
    /// the allocator from memory manager
308
    pub allocator: mm::Mutator,
qinsoon's avatar
qinsoon committed
309
    /// current stack (a thread can execute different stacks, but one stack at a time)
310
    pub stack: *mut MuStack,
qinsoon's avatar
qinsoon committed
311 312
    /// native stack pointer before we switch to this mu stack
    /// (when the thread exits, we restore to native stack, and allow proper destruction)
313
    pub native_sp_loc: Address,
qinsoon's avatar
qinsoon committed
314 315 316
    /// user supplied thread local address, can be zero
    pub user_tls: Address,
    /// exception object being thrown by the thread
317
    pub exception_obj: Address,
qinsoon's avatar
qinsoon committed
318
    /// a pointer to the virtual machine
319
    pub vm: Arc<VM>
qinsoon's avatar
qinsoon committed
320
}
321 322
unsafe impl Sync for MuThread {}
unsafe impl Send for MuThread {}
qinsoon's avatar
qinsoon committed
323

qinsoon's avatar
qinsoon committed
324
// a few field offsets the compiler uses
325
lazy_static! {
qinsoon's avatar
qinsoon committed
326 327 328 329 330 331
    pub static ref ALLOCATOR_OFFSET     : usize =
        offset_of!(MuThread=>allocator).get_byte_offset();
    pub static ref NATIVE_SP_LOC_OFFSET : usize =
        offset_of!(MuThread=>native_sp_loc).get_byte_offset();
    pub static ref USER_TLS_OFFSET      : usize =
        offset_of!(MuThread=>user_tls).get_byte_offset();
332 333
    pub static ref STACK_OFFSET      : usize =
        offset_of!(MuThread=>stack).get_byte_offset();
qinsoon's avatar
qinsoon committed
334 335
    pub static ref EXCEPTION_OBJ_OFFSET : usize =
        offset_of!(MuThread=>exception_obj).get_byte_offset();
336 337 338 339
}

impl fmt::Display for MuThread {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
qinsoon's avatar
qinsoon committed
340 341 342 343 344 345 346 347 348 349 350 351
        write!(
            f,
            "MuThread    @{:?}: {}\n",
            self as *const MuThread,
            self.hdr
        ).unwrap();
        write!(f, "- header    @{:?}\n", &self.hdr as *const MuEntityHeader).unwrap();
        write!(
            f,
            "- allocator @{:?}\n",
            &self.allocator as *const mm::Mutator
        ).unwrap();
352
        write!(f, "- stack     @{:?}\n", &self.stack as *const *mut MuStack).unwrap();
qinsoon's avatar
qinsoon committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
        write!(
            f,
            "- native sp @{:?}: {}\n",
            &self.native_sp_loc as *const Address,
            self.native_sp_loc
        ).unwrap();
        write!(
            f,
            "- user_tls  @{:?}: {}\n",
            &self.user_tls as *const Address,
            self.user_tls
        ).unwrap();
        write!(
            f,
            "- exc obj   @{:?}: {}\n",
            &self.exception_obj as *const Address,
            self.exception_obj
        ).unwrap();
371 372 373

        Ok(())
    }
374 375
}

376 377 378
use std::os::raw::c_int;

#[cfg(not(feature = "sel4-rumprun-target-side"))]
qinsoon's avatar
qinsoon committed
379
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
380
#[cfg(any(target_os = "macos", target_os = "linux"))]
qinsoon's avatar
qinsoon committed
381
#[link(name = "runtime_asm")]
382
extern "C" {
qinsoon's avatar
qinsoon committed
383 384 385 386 387 388
    /// swaps from a native stack to a mu stack
    /// we create OS threads with native stack, then before executing any mu code,
    /// we swap to mu stack and execute the entry function
    /// args:
    /// new_sp: stack pointer for the mu stack
    /// old_sp_loc: the location to store native stack pointer so we can later swap back
389
    fn muthread_start_normal(new_sp: Address, old_sp_loc: Address);
390
    fn muthread_start_exceptional(exception: Address, new_sp: Address, old_sp_loc: Address);
qinsoon's avatar
qinsoon committed
391 392 393 394 395 396 397 398 399 400 401

    /// gets base poniter for current frame
    pub fn get_current_frame_bp() -> Address;

    /// restores exception: restore callee saved registers, then set execution to certain point
    /// (this function will not return)
    /// args:
    /// dest: code address to execute (catch block)
    /// callee_saved: a sequence of value that will be restored in order
    /// sp: stack pointer for the new execution
    pub fn exception_restore(dest: Address, callee_saved: *const Word, sp: Address) -> !;
402 403
}

404
#[cfg(not(feature = "sel4-rumprun-target-side"))]
qinsoon's avatar
qinsoon committed
405
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
406
#[cfg(any(target_os = "macos", target_os = "linux"))]
qinsoon's avatar
qinsoon committed
407
#[link(name = "runtime_c")]
408 409
#[allow(improper_ctypes)]
extern "C" {
qinsoon's avatar
qinsoon committed
410 411 412
    /// sets thread local for Zebu
    /// Note: this thread local points to a MuThread, which contains all the thread local info
    /// for a Mu thread and further contains a pointer to the user-level thread local
413
    pub fn set_thread_local(thread: *mut MuThread);
qinsoon's avatar
qinsoon committed
414 415
    /// gets thread local for Zebu
    /// compiler may emit calls to this
416
    pub fn muentry_get_thread_local() -> Address;
qinsoon's avatar
qinsoon committed
417 418
    /// sets return value for a Zebu executable image
    /// compiler emits calls to this for SetRetval instruction (internal use only)
419
    pub fn muentry_set_retval(val: u32);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    /// a C wrapper for checking results written in asm
    fn c_check_result() -> c_int;
}

#[cfg(feature = "sel4-rumprun-target-side")]
#[cfg(target_arch = "x86_64")]
#[link(name = "runtime_asm")]
extern "C" {
    fn swap_to_mu_stack(new_sp: Address, entry: Address, old_sp_loc: Address);
    #[allow(dead_code)]
    fn muentry_swap_back_to_native_stack(sp_loc: Address);
    pub fn get_current_frame_bp() -> Address;
    pub fn exception_restore(dest: Address, callee_saved: *const Word, sp: Address) -> !;
}

#[cfg(feature = "sel4-rumprun-target-side")]
#[cfg(target_arch = "x86_64")]
#[link(name = "runtime_c")]
#[allow(improper_ctypes)]
extern "C" {
    pub fn set_thread_local(thread: *mut MuThread);
    pub fn muentry_get_thread_local() -> Address;
    pub fn muentry_set_retval(val: u32);
    fn c_check_result() -> c_int;
}

/// a Rust wrapper for the C function which returns the last set result
pub fn check_result() -> c_int {
    let result = unsafe { c_check_result() };
    result
450 451
}

qinsoon's avatar
qinsoon committed
452 453
impl MuThread {
    /// creates a new Mu thread with normal execution
qinsoon's avatar
qinsoon committed
454 455 456 457
    pub fn new_thread_normal(
        mut stack: Box<MuStack>,
        threadlocal: Address,
        vals: Vec<ValueLocation>,
458
        vm: Arc<VM>
459
    ) {
qinsoon's avatar
qinsoon committed
460 461
        // set up arguments on stack
        stack.setup_args(vals);
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
462 463
        let (join_handle, _) =
            MuThread::mu_thread_launch(vm.next_id(), stack, threadlocal, None, vm.clone());
464
        vm.push_join_handle(join_handle);
qinsoon's avatar
qinsoon committed
465
    }
466

467 468
    /// creates and launches a mu thread, returns a JoinHandle and address to its MuThread structure
    fn mu_thread_launch(
qinsoon's avatar
qinsoon committed
469 470 471
        id: MuID,
        stack: Box<MuStack>,
        user_tls: Address,
472
        exception: Option<Address>,
473
        vm: Arc<VM>
474
    ) -> (JoinHandle<()>, *mut MuThread) {
qinsoon's avatar
qinsoon committed
475 476
        let new_sp = stack.sp;

Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
        // The conversions between boxes and ptrs are needed here as a '*mut MuThread* can't be
        // sent between threads but a Box can. Also converting a Box to a ptr consumes it.
        let muthread_ptr = Box::into_raw(Box::new(
            MuThread::new(id, mm::new_mutator(), stack, user_tls, vm)
        ));
        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);
505
                    }
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
506 507 508 509 510 511
                }) {
                Ok(handle) => handle,
                Err(_) => panic!("failed to create a thread")
            },
            muthread_ptr
        )
qinsoon's avatar
qinsoon committed
512 513 514
    }

    /// creates metadata for a Mu thread
qinsoon's avatar
qinsoon committed
515 516 517 518 519
    fn new(
        id: MuID,
        allocator: mm::Mutator,
        stack: Box<MuStack>,
        user_tls: Address,
520
        vm: Arc<VM>
qinsoon's avatar
qinsoon committed
521
    ) -> MuThread {
qinsoon's avatar
qinsoon committed
522 523 524
        MuThread {
            hdr: MuEntityHeader::unnamed(id),
            allocator: allocator,
525
            stack: Box::into_raw(stack),
qinsoon's avatar
qinsoon committed
526
            native_sp_loc: unsafe { Address::zero() },
527 528
            user_tls: user_tls,
            vm: vm,
529
            exception_obj: unsafe { Address::zero() }
qinsoon's avatar
qinsoon committed
530 531
        }
    }
qinsoon's avatar
qinsoon committed
532

qinsoon's avatar
qinsoon committed
533
    /// is current thread a Mu thread?
qinsoon's avatar
qinsoon committed
534 535
    #[inline(always)]
    pub fn has_current() -> bool {
qinsoon's avatar
qinsoon committed
536
        !unsafe { muentry_get_thread_local() }.is_zero()
qinsoon's avatar
qinsoon committed
537
    }
qinsoon's avatar
qinsoon committed
538 539

    /// gets a reference to MuThread for current MuThread
qinsoon's avatar
qinsoon committed
540
    #[inline(always)]
541
    pub fn current() -> &'static MuThread {
qinsoon's avatar
qinsoon committed
542 543 544 545 546
        unsafe {
            muentry_get_thread_local()
                .to_ptr::<MuThread>()
                .as_ref()
                .unwrap()
qinsoon's avatar
qinsoon committed
547 548
        }
    }
qinsoon's avatar
qinsoon committed
549 550

    /// gets a mutable reference to MuThread for current MuThread
qinsoon's avatar
qinsoon committed
551
    #[inline(always)]
552
    pub fn current_mut() -> &'static mut MuThread {
qinsoon's avatar
qinsoon committed
553 554 555 556 557
        unsafe {
            muentry_get_thread_local()
                .to_ptr_mut::<MuThread>()
                .as_mut()
                .unwrap()
558 559
        }
    }
560

qinsoon's avatar
qinsoon committed
561 562 563
    /// disguises current thread as a Mu thread (setup MuThread metadata, thread local for it),
    /// returns true if we have created a MuThread on this call, false means we had MuThread
    /// for current thread before.
564
    pub unsafe fn current_thread_as_mu_thread(threadlocal: Address, vm: Arc<VM>) -> bool {
565 566
        use std::usize;

qinsoon's avatar
qinsoon committed
567
        // build exception table as we may execute mu function
568
        vm.build_callsite_table();
569

qinsoon's avatar
qinsoon committed
570
        if !muentry_get_thread_local().is_zero() {
571
            warn!("current thread has a thread local (has a muthread to it)");
572
            return false;
573 574
        }

575 576
        // fake a stack for current thread
        let fake_mu_stack_for_cur = Box::new(MuStack {
qinsoon's avatar
qinsoon committed
577
            hdr: MuEntityHeader::unnamed(vm.next_id()),
qinsoon's avatar
qinsoon committed
578
            // active state
qinsoon's avatar
qinsoon committed
579
            state: MuStackState::Active,
580 581 582
            // we do not know anything about current stack
            // treat it as max size
            size: usize::MAX,
qinsoon's avatar
qinsoon committed
583 584 585 586
            overflow_guard: Address::zero(),
            lower_bound: Address::zero(),
            upper_bound: Address::max(),
            underflow_guard: Address::max(),
587 588
            // these will only be used when stack is not active (we still save to these fields)
            // their values do not matter now
qinsoon's avatar
qinsoon committed
589 590 591
            sp: Address::zero(),
            bp: Address::zero(),
            ip: Address::zero(),
592
            // we are not responsible for keeping the memory alive
593
            mmap: None
594 595
        });

qinsoon's avatar
qinsoon committed
596
        // fake a thread for current thread
597
        let fake_mu_thread = MuThread {
qinsoon's avatar
qinsoon committed
598
            hdr: MuEntityHeader::unnamed(vm.next_id()),
qinsoon's avatar
qinsoon committed
599
            // we need a valid allocator and stack
qinsoon's avatar
qinsoon committed
600
            allocator: mm::new_mutator(),
601
            stack: Box::into_raw(fake_mu_stack_for_cur),
qinsoon's avatar
qinsoon committed
602
            // we do not need native_sp_loc (we do not expect the thread to call THREADEXIT)
qinsoon's avatar
qinsoon committed
603
            native_sp_loc: Address::zero(),
qinsoon's avatar
qinsoon committed
604
            // valid thread local from user
qinsoon's avatar
qinsoon committed
605 606
            user_tls: threadlocal,
            vm: vm,
607
            exception_obj: Address::zero()
608 609 610
        };

        // set thread local
qinsoon's avatar
qinsoon committed
611
        let ptr_fake_mu_thread: *mut MuThread = Box::into_raw(Box::new(fake_mu_thread));
qinsoon's avatar
qinsoon committed
612
        set_thread_local(ptr_fake_mu_thread);
613

614
        true
615
    }
616 617 618

    /// turn this current mu thread back as normal thread
    pub unsafe fn cleanup_current_mu_thread() {
619
        let mu_thread_addr = muentry_get_thread_local();
620 621

        if !mu_thread_addr.is_zero() {
qinsoon's avatar
qinsoon committed
622
            let mu_thread: *mut MuThread = mu_thread_addr.to_ptr_mut();
623

qinsoon's avatar
qinsoon committed
624 625
            // drop allocator
            mm::drop_mutator(&mut (*mu_thread).allocator as *mut mm::Mutator);
626

qinsoon's avatar
qinsoon committed
627
            // set thread local to zero
qinsoon's avatar
qinsoon committed
628
            set_thread_local(ptr::null_mut());
qinsoon's avatar
qinsoon committed
629

qinsoon's avatar
qinsoon committed
630 631
            // get mu thread back to Box (and will get dropped)
            Box::from_raw(mu_thread);
qinsoon's avatar
qinsoon committed
632 633
        }
    }
634 635
}

qinsoon's avatar
qinsoon committed
636
/// PrimordialThreadInfo stores information about primordial thread/entry function for a boot image
637
#[derive(Debug)]
qinsoon's avatar
qinsoon committed
638 639
pub struct PrimordialThreadInfo {
    /// entry function id
640
    pub func_id: MuID,
qinsoon's avatar
qinsoon committed
641
    /// does user supply some contant arguments to start the primordial thread?
qinsoon's avatar
qinsoon committed
642
    pub has_const_args: bool,
qinsoon's avatar
qinsoon committed
643
    /// arguments
644
    pub args: Vec<Constant>
Kunshan Wang's avatar
Kunshan Wang committed
645
}
646

qinsoon's avatar
qinsoon committed
647 648 649
rodal_struct!(PrimordialThreadInfo {
    func_id,
    args,
650
    has_const_args
qinsoon's avatar
qinsoon committed
651
});
652

653 654 655 656
#[no_mangle]
pub unsafe extern "C" fn muentry_new_stack(entry: Address, stack_size: usize) -> *mut MuStack {
    let ref vm = MuThread::current_mut().vm;
    let stack = Box::new(MuStack::new(vm.next_id(), entry, stack_size));
657
    Box::into_raw(stack)
658 659
}

660 661
// Kills the given stack. WARNING! do not call this whilst on the given stack
#[no_mangle]
662
pub unsafe extern "C" fn muentry_kill_stack(stack: *mut MuStack) {
663
    // This new box will be destroyed upon returning
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
664
    Box::from_raw(stack);
665
}
666 667 668

// Creates a new thread
#[no_mangle]
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
669 670 671 672 673
pub unsafe extern "C" fn muentry_new_thread_exceptional(
    stack: *mut MuStack,
    thread_local: Address,
    exception: Address
) -> *mut MuThread {
674
    let vm = MuThread::current_mut().vm.clone();
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
675 676 677 678 679 680 681
    let (join_handle, muthread) = MuThread::mu_thread_launch(
        vm.next_id(),
        Box::from_raw(stack),
        thread_local,
        Some(exception),
        vm.clone()
    );
682 683 684 685 686 687
    vm.push_join_handle(join_handle);
    muthread
}

// Creates a new thread
#[no_mangle]
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
688 689 690 691
pub unsafe extern "C" fn muentry_new_thread_normal(
    stack: *mut MuStack,
    thread_local: Address
) -> *mut MuThread {
692
    let vm = MuThread::current_mut().vm.clone();
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
693 694 695 696 697 698 699
    let (join_handle, muthread) = MuThread::mu_thread_launch(
        vm.next_id(),
        Box::from_raw(stack),
        thread_local,
        None,
        vm.clone()
    );
700 701
    vm.push_join_handle(join_handle);
    muthread
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
702
}