thread.rs 23.5 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 390 391 392 393 394 395
    fn muthread_start_normal(new_sp: Address, old_sp_loc: Address, mu_tls: Address);
    fn muthread_start_exceptional(
        exception: Address,
        new_sp: Address,
        old_sp_loc: Address,
        mu_tls: Address
    );
qinsoon's avatar
qinsoon committed
396 397 398 399 400 401 402 403 404 405

    /// 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
406
    pub fn exception_restore(dest: Address, callee_saved: *const Word, sp: Address) -> !;
407 408
}

409
#[cfg(not(feature = "sel4-rumprun-target-side"))]
qinsoon's avatar
qinsoon committed
410
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
411
#[cfg(any(target_os = "macos", target_os = "linux"))]
qinsoon's avatar
qinsoon committed
412
#[link(name = "runtime_c")]
413 414
#[allow(improper_ctypes)]
extern "C" {
qinsoon's avatar
qinsoon committed
415 416 417
    /// 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
418
    pub fn set_thread_local(thread: *mut MuThread);
qinsoon's avatar
qinsoon committed
419 420
    /// gets thread local for Zebu
    /// compiler may emit calls to this
421
    pub fn muentry_get_thread_local() -> Address;
qinsoon's avatar
qinsoon committed
422 423
    /// sets return value for a Zebu executable image
    /// compiler emits calls to this for SetRetval instruction (internal use only)
424
    pub fn muentry_set_retval(val: u32);
425 426 427 428 429 430 431 432 433 434 435 436
    /// 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;
437
    pub fn exception_restore(dest: Address, callee_saved: *const Word, sp: Address) -> !;
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
}

#[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
455 456
}

qinsoon's avatar
qinsoon committed
457 458
impl MuThread {
    /// creates a new Mu thread with normal execution
qinsoon's avatar
qinsoon committed
459 460 461 462
    pub fn new_thread_normal(
        mut stack: Box<MuStack>,
        threadlocal: Address,
        vals: Vec<ValueLocation>,
463
        vm: Arc<VM>
464
    ) {
qinsoon's avatar
qinsoon committed
465 466
        // set up arguments on stack
        stack.setup_args(vals);
467
        MuThread::mu_thread_launch(vm.next_id(), stack, threadlocal, None, vm.clone());
qinsoon's avatar
qinsoon committed
468
    }
469

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

Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
480 481
        // 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.
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
482 483 484 485 486 487 488
        let muthread_ptr = Box::into_raw(Box::new(MuThread::new(
            id,
            mm::new_mutator(),
            stack,
            user_tls,
            vm.clone()
        )));
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
489 490
        let muthread = unsafe { Box::from_raw(muthread_ptr) };

491 492 493 494 495 496 497 498 499 500 501 502 503 504
        let join_handle = 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 {
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
505
                        Some(e) => muthread_start_exceptional(e, new_sp, sp_threadlocal_loc, addr),
506
                        None => muthread_start_normal(new_sp, sp_threadlocal_loc, addr)
507
                    }
508 509 510 511 512 513 514 515 516 517 518

                    // Thread finished, delete it's data
                    Box::from_raw(muthread);
                }
            }) {
            Ok(handle) => handle,
            Err(_) => panic!("failed to create a thread")
        };
        vm.push_join_handle(join_handle);

        muthread_ptr
qinsoon's avatar
qinsoon committed
519 520 521
    }

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

qinsoon's avatar
qinsoon committed
540
    /// is current thread a Mu thread?
qinsoon's avatar
qinsoon committed
541 542
    #[inline(always)]
    pub fn has_current() -> bool {
qinsoon's avatar
qinsoon committed
543
        !unsafe { muentry_get_thread_local() }.is_zero()
qinsoon's avatar
qinsoon committed
544
    }
qinsoon's avatar
qinsoon committed
545 546

    /// gets a reference to MuThread for current MuThread
qinsoon's avatar
qinsoon committed
547
    #[inline(always)]
548
    pub fn current() -> &'static MuThread {
qinsoon's avatar
qinsoon committed
549 550 551 552 553
        unsafe {
            muentry_get_thread_local()
                .to_ptr::<MuThread>()
                .as_ref()
                .unwrap()
qinsoon's avatar
qinsoon committed
554 555
        }
    }
qinsoon's avatar
qinsoon committed
556 557

    /// gets a mutable reference to MuThread for current MuThread
qinsoon's avatar
qinsoon committed
558
    #[inline(always)]
559
    pub fn current_mut() -> &'static mut MuThread {
qinsoon's avatar
qinsoon committed
560 561 562 563 564
        unsafe {
            muentry_get_thread_local()
                .to_ptr_mut::<MuThread>()
                .as_mut()
                .unwrap()
565 566
        }
    }
567

qinsoon's avatar
qinsoon committed
568 569 570
    /// 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.
571
    pub unsafe fn current_thread_as_mu_thread(threadlocal: Address, vm: Arc<VM>) -> bool {
572 573
        use std::usize;

qinsoon's avatar
qinsoon committed
574
        // build exception table as we may execute mu function
575
        vm.build_callsite_table();
576

qinsoon's avatar
qinsoon committed
577
        if !muentry_get_thread_local().is_zero() {
578
            warn!("current thread has a thread local (has a muthread to it)");
579
            return false;
580 581
        }

582 583
        // fake a stack for current thread
        let fake_mu_stack_for_cur = Box::new(MuStack {
qinsoon's avatar
qinsoon committed
584
            hdr: MuEntityHeader::unnamed(vm.next_id()),
qinsoon's avatar
qinsoon committed
585
            // active state
qinsoon's avatar
qinsoon committed
586
            state: MuStackState::Active,
587 588 589
            // we do not know anything about current stack
            // treat it as max size
            size: usize::MAX,
qinsoon's avatar
qinsoon committed
590 591 592 593
            overflow_guard: Address::zero(),
            lower_bound: Address::zero(),
            upper_bound: Address::max(),
            underflow_guard: Address::max(),
594 595
            // 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
596 597 598
            sp: Address::zero(),
            bp: Address::zero(),
            ip: Address::zero(),
599
            // we are not responsible for keeping the memory alive
600
            mmap: None
601 602
        });

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

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

621
        true
622
    }
623 624 625

    /// turn this current mu thread back as normal thread
    pub unsafe fn cleanup_current_mu_thread() {
626
        let mu_thread_addr = muentry_get_thread_local();
627 628

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

qinsoon's avatar
qinsoon committed
631 632
            // drop allocator
            mm::drop_mutator(&mut (*mu_thread).allocator as *mut mm::Mutator);
633

qinsoon's avatar
qinsoon committed
634
            // set thread local to zero
qinsoon's avatar
qinsoon committed
635
            set_thread_local(ptr::null_mut());
qinsoon's avatar
qinsoon committed
636

qinsoon's avatar
qinsoon committed
637 638
            // get mu thread back to Box (and will get dropped)
            Box::from_raw(mu_thread);
qinsoon's avatar
qinsoon committed
639 640
        }
    }
641 642
}

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

qinsoon's avatar
qinsoon committed
654 655 656
rodal_struct!(PrimordialThreadInfo {
    func_id,
    args,
657
    has_const_args
qinsoon's avatar
qinsoon committed
658
});
659

660 661 662 663
#[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));
664
    Box::into_raw(stack)
665 666
}

667 668
// Kills the given stack. WARNING! do not call this whilst on the given stack
#[no_mangle]
669
pub unsafe extern "C" fn muentry_kill_stack(stack: *mut MuStack) {
670
    // This new box will be destroyed upon returning
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
671
    Box::from_raw(stack);
672
}
673 674 675

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

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