(index<- )        ./libgreen/context.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  use stack::Stack;
  12  use std::uint;
  13  use std::cast::{transmute, transmute_mut_unsafe};
  14  use std::rt::stack;
  15  use std::raw;
  16  
  17  // FIXME #7761: Registers is boxed so that it is 16-byte aligned, for storing
  18  // SSE regs.  It would be marginally better not to do this. In C++ we
  19  // use an attribute on a struct.
  20  // FIXME #7761: It would be nice to define regs as `Box<Option<Registers>>`
  21  // since the registers are sometimes empty, but the discriminant would
  22  // then misalign the regs again.
  23  pub struct Context {
  24      /// Hold the registers while the task or scheduler is suspended
  25      regs: Box<Registers>,
  26      /// Lower bound and upper bound for the stack
  27      stack_bounds: Option<(uint, uint)>,
  28  }
  29  
  30  pub type InitFn = extern "C" fn(uint, *(), *()) -> !;
  31  
  32  impl Context {
  33      pub fn empty() -> Context {
  34          Context {
  35              regs: new_regs(),
  36              stack_bounds: None,
  37          }
  38      }
  39  
  40      /// Create a new context that will resume execution by running proc()
  41      ///
  42      /// The `init` function will be run with `arg` and the `start` procedure
  43      /// split up into code and env pointers. It is required that the `init`
  44      /// function never return.
  45      ///
  46      /// FIXME: this is basically an awful the interface. The main reason for
  47      ///        this is to reduce the number of allocations made when a green
  48      ///        task is spawned as much as possible
  49      pub fn new(initInitFn, arguint, startproc():Send,
  50                 stack&mut Stack) -> Context {
  51  
  52          let sp*uint = stack.end();
  53          let sp*mut uint = unsafe { transmute_mut_unsafe(sp) };
  54          // Save and then immediately load the current context,
  55          // which we will then modify to call the given function when restored
  56          let mut regs = new_regs();
  57  
  58          initialize_call_frame(&mut *regs,
  59                                init,
  60                                arg,
  61                                unsafe { transmute(start) },
  62                                sp);
  63  
  64          // Scheduler tasks don't have a stack in the "we allocated it" sense,
  65          // but rather they run on pthreads stacks. We have complete control over
  66          // them in terms of the code running on them (and hopefully they don't
  67          // overflow). Additionally, their coroutine stacks are listed as being
  68          // zero-length, so that's how we detect what's what here.
  69          let stack_base*uint = stack.start();
  70          let bounds = if sp as uint == stack_base as uint {
  71              None
  72          } else {
  73              Some((stack_base as uint, sp as uint))
  74          };
  75          return Context {
  76              regs: regs,
  77              stack_bounds: bounds,
  78          }
  79      }
  80  
  81      /* Switch contexts
  82  
  83      Suspend the current execution context and resume another by
  84      saving the registers values of the executing thread to a Context
  85      then loading the registers from a previously saved Context.
  86      */
  87      pub fn swap(out_context&mut Context, in_context&Context) {
  88          rtdebug!("swapping contexts");
  89          let out_regs&mut Registers = match out_context {
  90              &Context { regs: box ref mut r, .. } => r
  91          };
  92          let in_regs&Registers = match in_context {
  93              &Context { regs: box ref r, .. } => r
  94          };
  95  
  96          rtdebug!("noting the stack limit and doing raw swap");
  97  
  98          unsafe {
  99              // Right before we switch to the new context, set the new context's
 100              // stack limit in the OS-specified TLS slot. This also  means that
 101              // we cannot call any more rust functions after record_stack_bounds
 102              // returns because they would all likely fail due to the limit being
 103              // invalid for the current task. Lucky for us `rust_swap_registers`
 104              // is a C function so we don't have to worry about that!
 105              match in_context.stack_bounds {
 106                  Some((lo, hi)) => stack::record_stack_bounds(lo, hi),
 107                  // If we're going back to one of the original contexts or
 108                  // something that's possibly not a "normal task", then reset
 109                  // the stack limit to 0 to make morestack never fail
 110                  None => stack::record_stack_bounds(0, uint::MAX),
 111              }
 112              rust_swap_registers(out_regs, in_regs)
 113          }
 114      }
 115  }
 116  
 117  #[link(name = "context_switch", kind = "static")]
 118  extern {
 119      fn rust_swap_registers(out_regs: *mut Registers, in_regs: *Registers);
 120  }
 121  
 122  // Register contexts used in various architectures
 123  //
 124  // These structures all represent a context of one task throughout its
 125  // execution. Each struct is a representation of the architecture's register
 126  // set. When swapping between tasks, these register sets are used to save off
 127  // the current registers into one struct, and load them all from another.
 128  //
 129  // Note that this is only used for context switching, which means that some of
 130  // the registers may go unused. For example, for architectures with
 131  // callee/caller saved registers, the context will only reflect the callee-saved
 132  // registers. This is because the caller saved registers are already stored
 133  // elsewhere on the stack (if it was necessary anyway).
 134  //
 135  // Additionally, there may be fields on various architectures which are unused
 136  // entirely because they only reflect what is theoretically possible for a
 137  // "complete register set" to show, but user-space cannot alter these registers.
 138  // An example of this would be the segment selectors for x86.
 139  //
 140  // These structures/functions are roughly in-sync with the source files inside
 141  // of src/rt/arch/$arch. The only currently used function from those folders is
 142  // the `rust_swap_registers` function, but that's only because for now segmented
 143  // stacks are disabled.
 144  
 145  #[cfg(target_arch = "x86")]
 146  struct Registers {
 147      eax: u32, ebx: u32, ecx: u32, edx: u32,
 148      ebp: u32, esi: u32, edi: u32, esp: u32,
 149      cs: u16, ds: u16, ss: u16, es: u16, fs: u16, gs: u16,
 150      eflags: u32, eip: u32
 151  }
 152  
 153  #[cfg(target_arch = "x86")]
 154  fn new_regs() -> Box<Registers> {
 155      box Registers {
 156          eax: 0, ebx: 0, ecx: 0, edx: 0,
 157          ebp: 0, esi: 0, edi: 0, esp: 0,
 158          cs: 0, ds: 0, ss: 0, es: 0, fs: 0, gs: 0,
 159          eflags: 0, eip: 0
 160      }
 161  }
 162  
 163  #[cfg(target_arch = "x86")]
 164  fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
 165                           procedure: raw::Procedure, sp: *mut uint) {
 166  
 167      // x86 has interesting stack alignment requirements, so do some alignment
 168      // plus some offsetting to figure out what the actual stack should be.
 169      let sp = align_down(sp);
 170      let sp = mut_offset(sp, -4);
 171  
 172      unsafe { *mut_offset(sp, 2) = procedure.env as uint };
 173      unsafe { *mut_offset(sp, 1) = procedure.code as uint };
 174      unsafe { *mut_offset(sp, 0) = arg as uint };
 175      let sp = mut_offset(sp, -1);
 176      unsafe { *sp = 0 }; // The final return address
 177  
 178      regs.esp = sp as u32;
 179      regs.eip = fptr as u32;
 180  
 181      // Last base pointer on the stack is 0
 182      regs.ebp = 0;
 183  }
 184  
 185  // windows requires saving more registers (both general and XMM), so the windows
 186  // register context must be larger.
 187  #[cfg(windows, target_arch = "x86_64")]
 188  type Registers = [uint, ..34];
 189  #[cfg(not(windows), target_arch = "x86_64")]
 190  type Registers = [uint, ..22];
 191  
 192  #[cfg(windows, target_arch = "x86_64")]
 193  fn new_regs() -> Box<Registers> { box [0, .. 34] }
 194  #[cfg(not(windows), target_arch = "x86_64")]
 195  fn new_regs() -> Box<Registers> { box {let v = [0, .. 22]; v} }
 196  
 197  #[cfg(target_arch = "x86_64")]
 198  fn initialize_call_frame(regs: &mut Registers, fptrInitFn, arg: uint,
 199                           procedureraw::Procedure, sp: *mut uint) {
 200      extern { fn rust_bootstrap_green_task(); }
 201  
 202      // Redefinitions from rt/arch/x86_64/regs.h
 203      static RUSTRT_RSP: uint = 1;
 204      static RUSTRT_IP: uint = 8;
 205      static RUSTRT_RBP: uint = 2;
 206      static RUSTRT_R12: uint = 4;
 207      static RUSTRT_R13: uint = 5;
 208      static RUSTRT_R14: uint = 6;
 209      static RUSTRT_R15: uint = 7;
 210  
 211      let sp = align_down(sp);
 212      let sp = mut_offset(sp, -1);
 213  
 214      // The final return address. 0 indicates the bottom of the stack
 215      unsafe { *sp = 0; }
 216  
 217      rtdebug!("creating call frame");
 218      rtdebug!("fptr {:#x}", fptr as uint);
 219      rtdebug!("arg {:#x}", arg);
 220      rtdebug!("sp {}", sp);
 221  
 222      // These registers are frobbed by rust_bootstrap_green_task into the right
 223      // location so we can invoke the "real init function", `fptr`.
 224      regs[RUSTRT_R12] = arg as uint;
 225      regs[RUSTRT_R13] = procedure.code as uint;
 226      regs[RUSTRT_R14] = procedure.env as uint;
 227      regs[RUSTRT_R15] = fptr as uint;
 228  
 229      // These registers are picked up by the regulard context switch paths. These
 230      // will put us in "mostly the right context" except for frobbing all the
 231      // arguments to the right place. We have the small trampoline code inside of
 232      // rust_bootstrap_green_task to do that.
 233      regs[RUSTRT_RSP] = sp as uint;
 234      regs[RUSTRT_IP] = rust_bootstrap_green_task as uint;
 235  
 236      // Last base pointer on the stack should be 0
 237      regs[RUSTRT_RBP] = 0;
 238  }
 239  
 240  #[cfg(target_arch = "arm")]
 241  type Registers = [uint, ..32];
 242  
 243  #[cfg(target_arch = "arm")]
 244  fn new_regs() -> Box<Registers> { box {[0, .. 32]} }
 245  
 246  #[cfg(target_arch = "arm")]
 247  fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
 248                           procedure: raw::Procedure, sp: *mut uint) {
 249      extern { fn rust_bootstrap_green_task(); }
 250  
 251      let sp = align_down(sp);
 252      // sp of arm eabi is 8-byte aligned
 253      let sp = mut_offset(sp, -2);
 254  
 255      // The final return address. 0 indicates the bottom of the stack
 256      unsafe { *sp = 0; }
 257  
 258      // ARM uses the same technique as x86_64 to have a landing pad for the start
 259      // of all new green tasks. Neither r1/r2 are saved on a context switch, so
 260      // the shim will copy r3/r4 into r1/r2 and then execute the function in r5
 261      regs[0] = arg as uint;              // r0
 262      regs[3] = procedure.code as uint;   // r3
 263      regs[4] = procedure.env as uint;    // r4
 264      regs[5] = fptr as uint;             // r5
 265      regs[13] = sp as uint;                          // #52 sp, r13
 266      regs[14] = rust_bootstrap_green_task as uint;   // #56 pc, r14 --> lr
 267  }
 268  
 269  #[cfg(target_arch = "mips")]
 270  type Registers = [uint, ..32];
 271  
 272  #[cfg(target_arch = "mips")]
 273  fn new_regs() -> Box<Registers> { box [0, .. 32] }
 274  
 275  #[cfg(target_arch = "mips")]
 276  fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
 277                           procedure: raw::Procedure, sp: *mut uint) {
 278      let sp = align_down(sp);
 279      // sp of mips o32 is 8-byte aligned
 280      let sp = mut_offset(sp, -2);
 281  
 282      // The final return address. 0 indicates the bottom of the stack
 283      unsafe { *sp = 0; }
 284  
 285      regs[4] = arg as uint;
 286      regs[5] = procedure.code as uint;
 287      regs[6] = procedure.env as uint;
 288      regs[29] = sp as uint;
 289      regs[25] = fptr as uint;
 290      regs[31] = fptr as uint;
 291  }
 292  
 293  fn align_down(sp: *mut uint) -> *mut uint {
 294      let sp = (sp as uint) & !(16 - 1);
 295      sp as *mut uint
 296  }
 297  
 298  // ptr::mut_offset is positive ints only
 299  #[inline]
 300  pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T {
 301      use std::mem::size_of;
 302      (ptr as int + count * (size_of::<T>() as int)) as *mut T
 303  }


libgreen/context.rs:194:45-194:45 -fn- definition:
fn new_regs() -> Box<Registers> { box {let v = [0, .. 22]; v} }
fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
                         procedure: raw::Procedure, sp: *mut uint) {
references:- 2
55:         // which we will then modify to call the given function when restored
56:         let mut regs = new_regs();


libgreen/context.rs:22:33-22:33 -struct- definition:
// then misalign the regs again.
pub struct Context {
    /// Hold the registers while the task or scheduler is suspended
references:- 12
74:         };
75:         return Context {
76:             regs: regs,
--
86:     */
87:     pub fn swap(out_context: &mut Context, in_context: &Context) {
88:         rtdebug!("swapping contexts");
89:         let out_regs: &mut Registers = match out_context {
90:             &Context { regs: box ref mut r, .. } => r
91:         };
92:         let in_regs: &Registers = match in_context {
93:             &Context { regs: box ref r, .. } => r
94:         };
libgreen/coroutine.rs:
27:     /// Always valid if the task is alive and not running.
28:     pub saved_context: Context
29: }
libgreen/sched.rs:
680:     pub fn get_contexts<'a>(current_task: &mut GreenTask, next_task: &mut GreenTask) ->
681:         (&'a mut Context, &'a mut Context) {
682:         let current_task_context =
libgreen/context.rs:
32: impl Context {
33:     pub fn empty() -> Context {


libgreen/context.rs:189:45-189:45 -NK_AS_STR_TODO- definition:
type Registers = [uint, ..22];
fn new_regs() -> Box<Registers> { box [0, .. 34] }
fn new_regs() -> Box<Registers> { box {let v = [0, .. 22]; v} }
references:- 7
198: fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
199:                          procedure: raw::Procedure, sp: *mut uint) {


libgreen/context.rs:29:1-29:1 -NK_AS_STR_TODO- definition:
pub type InitFn = extern "C" fn(uint, *(), *()) -> !;
impl Context {
    pub fn empty() -> Context {
references:- 2
198: fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
199:                          procedure: raw::Procedure, sp: *mut uint) {