<commit_before>use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::env;
use std::error::Error;
use std::fs;
use std::ops::Deref;
use std::path::Path;
use std::rc::{Rc, Weak};

use super::super::ast;
use super::super::ast_to_ir;
use super::super::ir;
use super::super::parser;
use super::super::target::bytecode;
use super::frame::Closure;
use super::value::Value;

lazy_static! {
    static ref DEBUG_ALL: bool = env::var("DEBUG_ALL").is_ok();
    static ref DEBUG_AST: bool = (*DEBUG_ALL || env::var("DEBUG_AST").is_ok());
    static ref DEBUG_IR: bool = (*DEBUG_ALL || env::var("DEBUG_IR").is_ok());
    static ref DEBUG_BYTECODE: bool = (*DEBUG_ALL || env::var("DEBUG_BYTECODE").is_ok());
}

fn read_and_parse_file<P: AsRef<Path>>(path: P) -> Result<ast::Module, Box<dyn Error>> {
    let source = fs::read_to_string(path)?;
    Ok(parser::parse(source))
}

pub fn compile_ast_into_module(
    ast_module: &ast::Module,
    name: String,
    ast_flags: ast_to_ir::CompilationFlags,
) -> Result<LoadedModule, Box<dyn Error>> {
    let ir_module = ast_to_ir::compile(ast_module, ast_flags);
    if *DEBUG_IR {
        println!("IR({}):", name);
        ir::printer::Printer::new(std::io::stdout()).print_module(&ir_module)?;
        println!();
    }

    let bytecode_module = ir::compiler::compile(&ir_module);
    if *DEBUG_BYTECODE {
        println!("Bytecode({}):", name);
        bytecode::printer::Printer::new(std::io::stdout()).print_module(&bytecode_module)?;
    }

    let loaded_module = LoadedModule::empty(name);
    let functions = bytecode_module
        .functions
        .into_iter()
        .map(|function| {
            let weak_loaded_module = Rc::downgrade(&loaded_module.0);
            LoadedFunction::new(weak_loaded_module, function)
        })
        .collect::<Vec<LoadedFunction>>();
    loaded_module.0.borrow_mut().functions = functions;

    Ok(loaded_module)
}

pub fn load_file<P: AsRef<Path>>(path: P) -> Result<LoadedModule, Box<dyn Error>> {
    let name = path
        .as_ref()
        .to_str()
        .expect("Couldn't convert path to string")
        .to_owned();

    let ast_module = read_and_parse_file(&name)?;
    if *DEBUG_AST {
        println!("AST({}):", name);
        ast::printer::Printer::new(std::io::stdout()).print_module(ast_module.clone())?;
        println!();
    }

    compile_ast_into_module(&ast_module, name, Default::default())
}

pub struct InnerLoadedModule {
    /// The name of the module. This should almost always be the canonicalized
    /// path to the source file.
    name: String,
    functions: Vec<LoadedFunction>,
    /// The closure holding the static scope that holds:
    ///   - Imports
    ///   - Bound or exported variables
    ///   - Bound or exported functions
    static_closure: Closure,
    // A module loaded into memory is uninitialized. Only after it has been
    // evaluated (and imports and exports resolved) is it initialized.
    initialized: bool,
    imports: ModuleImports,
    exports: ModuleExports,
}

impl InnerLoadedModule {
    fn empty(name: String) -> Self {
        Self {
            name,
            functions: vec![],
            static_closure: Closure::new_static(),
            initialized: false,
            imports: ModuleImports::new(),
            exports: ModuleExports::new(),
        }
    }
}

// Opaque wrapper around a reference-counted loaded unit.
#[derive(Clone)]
pub struct LoadedModule(Rc<RefCell<InnerLoadedModule>>);

type WeakLoadedModule = Weak<RefCell<InnerLoadedModule>>;

impl LoadedModule {
    pub fn empty(name: String) -> Self {
        Self(Rc::new(RefCell::new(InnerLoadedModule::empty(name))))
    }

    pub fn name(&self) -> String {
        self.0.borrow().name.clone()
    }

    pub fn main(&self) -> LoadedFunction {
        self.0.borrow().functions[0].clone()
    }

    pub fn static_closure(&self) -> Closure {
        self.0.borrow().static_closure.clone()
    }

    /// Should only be called by the REPL!
    pub fn override_static_closure(&self, static_closure: Closure) {
        self.0.borrow_mut().static_closure = static_closure;
    }

    pub fn function(&self, id: u16) -> LoadedFunction {
        self.0
            .borrow()
            .functions
            .iter()
            .find(|&function| function.id() == id)
            .expect("Function not found")
            .clone()
    }

    pub fn get_named_exports(&self) -> HashMap<String, Option<Value>> {
        self.0.borrow().exports.exports.to_owned()
    }

    // Used by bootstrapping: see `prelude.rs`.
    pub fn add_named_export<N: Into<String>>(&self, name: N, value: Value) {
        self.0
            .borrow_mut()
            .exports
            .exports
            .insert(name.into(), Some(value));
    }

    pub fn get_constant<N: AsRef<str>>(&self, name: N) -> Value {
        self.0.borrow().imports.get_import(name.as_ref())
    }

    pub fn set_import<N: Into<String>>(&self, name: N, value: Value) {
        self.0.borrow_mut().imports.set_import(name, value)
    }
}

struct ModuleImports {
    // Imports are resolved from `None`s into values at the beginning of
    // module initialization.
    imports: HashMap<String, Option<Value>>,
}

impl ModuleImports {
    fn new() -> Self {
        Self {
            imports: HashMap::new(),
        }
    }

    fn get_import(&self, name: &str) -> Value {
        // First look for the entry in the map.
        let import = self
            .imports
            .get(name)
            .expect(&format!("Import not found: {}", name));

        // Then check whether or not it's initialized.
        import
            .clone()
            .expect(&format!("Uninitialized import: {}", name))
    }

    fn set_import<N: Into<String>>(&mut self, name: N, value: Value) {
        self.imports.insert(name.into(), Some(value));
    }
}

pub struct ModuleExports {
    // Exports will start out as `None`s and are then filled in once the module
    // is initialized.
    pub exports: HashMap<String, Option<Value>>,
}

impl ModuleExports {
    fn new() -> Self {
        Self {
            exports: HashMap::new(),
        }
    }
}

#[derive(Clone)]
struct InnerLoadedFunction {
    module: WeakLoadedModule,
    id: u16,
    bytecode: BytecodeFunction,
}

/// Handle to a loaded function.
#[derive(Clone)]
pub struct LoadedFunction(Rc<InnerLoadedFunction>);

impl LoadedFunction {
    fn new(module: WeakLoadedModule, function: bytecode::layout::Function) -> Self {
        Self(Rc::new(InnerLoadedFunction {
            module,
            id: function.id,
            bytecode: BytecodeFunction::new(function),
        }))
    }

    pub fn id(&self) -> u16 {
        self.0.id
    }

    pub fn bytecode(&self) -> BytecodeFunction {
        self.0.bytecode.clone()
    }

    /// Returns whether or not this function binds/captures its environment
    /// when it is created.
    pub fn binds_on_create(&self) -> bool {
        self.0.bytecode.parent_bindings()
    }

    /// Returns a closure suitable for calling the function.
    pub fn build_closure_for_call(&self, parent: Option<Closure>) -> Option<Closure> {
        let bindings = self.0.bytecode.bindings();
        // Whether or not it needs to create bindings (a closure) when called.
        let binds_on_call = !bindings.is_empty() || self.0.bytecode.parent_bindings();
        if binds_on_call {
            let bindings = if bindings.is_empty() {
                None
            } else {
                Some(bindings)
            };
            Some(Closure::new(bindings, parent))
        } else {
            None
        }
    }

    pub fn module(&self) -> LoadedModule {
        let module = self.0.module.upgrade().expect("Module has been dropped");
        LoadedModule(module)
    }
}

pub struct InnerBytecodeFunction {
    function: bytecode::layout::Function,
}

impl InnerBytecodeFunction {
    pub fn name(&self) -> &str {
        &self.function.name
    }

    #[inline]
    pub fn registers(&self) -> u8 {
        self.function.registers
    }

    #[inline]
    pub fn locals(&self) -> u8 {
        self.function.locals
    }

    #[inline]
    pub fn instruction(&self, instruction_address: usize) -> bytecode::layout::Instruction {
        self.function.instructions[instruction_address].clone()
    }

    pub fn locals_names(&self) -> Vec<String> {
        self.function.locals_names.clone()
    }

    pub fn has_bindings(&self) -> bool {
        !self.function.bindings.is_empty()
    }

    pub fn bindings(&self) -> HashSet<String> {
        self.function.bindings.clone()
    }

    pub fn parent_bindings(&self) -> bool {
        self.function.parent_bindings
    }
}

#[derive(Clone)]
pub struct BytecodeFunction(Rc<InnerBytecodeFunction>);

impl BytecodeFunction {
    pub fn new(function: bytecode::layout::Function) -> Self {
        Self(Rc::new(InnerBytecodeFunction { function }))
    }
}

impl Deref for BytecodeFunction {
    type Target = InnerBytecodeFunction;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<commit_msg>Add `qualified_name` to get the name of a function and its module's short name<commit_after>use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::env;
use std::error::Error;
use std::fs;
use std::ops::Deref;
use std::path::Path;
use std::rc::{Rc, Weak};

use super::super::ast;
use super::super::ast_to_ir;
use super::super::ir;
use super::super::parser;
use super::super::target::bytecode;
use super::frame::Closure;
use super::value::Value;

lazy_static! {
    static ref DEBUG_ALL: bool = env::var("DEBUG_ALL").is_ok();
    static ref DEBUG_AST: bool = (*DEBUG_ALL || env::var("DEBUG_AST").is_ok());
    static ref DEBUG_IR: bool = (*DEBUG_ALL || env::var("DEBUG_IR").is_ok());
    static ref DEBUG_BYTECODE: bool = (*DEBUG_ALL || env::var("DEBUG_BYTECODE").is_ok());
}

fn read_and_parse_file<P: AsRef<Path>>(path: P) -> Result<ast::Module, Box<dyn Error>> {
    let source = fs::read_to_string(path)?;
    Ok(parser::parse(source))
}

pub fn compile_ast_into_module(
    ast_module: &ast::Module,
    name: String,
    ast_flags: ast_to_ir::CompilationFlags,
) -> Result<LoadedModule, Box<dyn Error>> {
    let ir_module = ast_to_ir::compile(ast_module, ast_flags);
    if *DEBUG_IR {
        println!("IR({}):", name);
        ir::printer::Printer::new(std::io::stdout()).print_module(&ir_module)?;
        println!();
    }

    let bytecode_module = ir::compiler::compile(&ir_module);
    if *DEBUG_BYTECODE {
        println!("Bytecode({}):", name);
        bytecode::printer::Printer::new(std::io::stdout()).print_module(&bytecode_module)?;
    }

    let loaded_module = LoadedModule::empty(name);
    let functions = bytecode_module
        .functions
        .into_iter()
        .map(|function| {
            let weak_loaded_module = Rc::downgrade(&loaded_module.0);
            LoadedFunction::new(weak_loaded_module, function)
        })
        .collect::<Vec<LoadedFunction>>();
    loaded_module.0.borrow_mut().functions = functions;

    Ok(loaded_module)
}

pub fn load_file<P: AsRef<Path>>(path: P) -> Result<LoadedModule, Box<dyn Error>> {
    let name = path
        .as_ref()
        .to_str()
        .expect("Couldn't convert path to string")
        .to_owned();

    let ast_module = read_and_parse_file(&name)?;
    if *DEBUG_AST {
        println!("AST({}):", name);
        ast::printer::Printer::new(std::io::stdout()).print_module(ast_module.clone())?;
        println!();
    }

    compile_ast_into_module(&ast_module, name, Default::default())
}

pub struct InnerLoadedModule {
    /// The name of the module. This should almost always be the canonicalized
    /// path to the source file.
    name: String,
    functions: Vec<LoadedFunction>,
    /// The closure holding the static scope that holds:
    ///   - Imports
    ///   - Bound or exported variables
    ///   - Bound or exported functions
    static_closure: Closure,
    // A module loaded into memory is uninitialized. Only after it has been
    // evaluated (and imports and exports resolved) is it initialized.
    initialized: bool,
    imports: ModuleImports,
    exports: ModuleExports,
}

impl InnerLoadedModule {
    fn empty(name: String) -> Self {
        Self {
            name,
            functions: vec![],
            static_closure: Closure::new_static(),
            initialized: false,
            imports: ModuleImports::new(),
            exports: ModuleExports::new(),
        }
    }
}

// Opaque wrapper around a reference-counted loaded unit.
#[derive(Clone)]
pub struct LoadedModule(Rc<RefCell<InnerLoadedModule>>);

type WeakLoadedModule = Weak<RefCell<InnerLoadedModule>>;

impl LoadedModule {
    pub fn empty(name: String) -> Self {
        Self(Rc::new(RefCell::new(InnerLoadedModule::empty(name))))
    }

    pub fn name(&self) -> String {
        self.0.borrow().name.clone()
    }

    pub fn main(&self) -> LoadedFunction {
        self.0.borrow().functions[0].clone()
    }

    pub fn static_closure(&self) -> Closure {
        self.0.borrow().static_closure.clone()
    }

    /// Should only be called by the REPL!
    pub fn override_static_closure(&self, static_closure: Closure) {
        self.0.borrow_mut().static_closure = static_closure;
    }

    pub fn function(&self, id: u16) -> LoadedFunction {
        self.0
            .borrow()
            .functions
            .iter()
            .find(|&function| function.id() == id)
            .expect("Function not found")
            .clone()
    }

    pub fn get_named_exports(&self) -> HashMap<String, Option<Value>> {
        self.0.borrow().exports.exports.to_owned()
    }

    // Used by bootstrapping: see `prelude.rs`.
    pub fn add_named_export<N: Into<String>>(&self, name: N, value: Value) {
        self.0
            .borrow_mut()
            .exports
            .exports
            .insert(name.into(), Some(value));
    }

    pub fn get_constant<N: AsRef<str>>(&self, name: N) -> Value {
        self.0.borrow().imports.get_import(name.as_ref())
    }

    pub fn set_import<N: Into<String>>(&self, name: N, value: Value) {
        self.0.borrow_mut().imports.set_import(name, value)
    }
}

struct ModuleImports {
    // Imports are resolved from `None`s into values at the beginning of
    // module initialization.
    imports: HashMap<String, Option<Value>>,
}

impl ModuleImports {
    fn new() -> Self {
        Self {
            imports: HashMap::new(),
        }
    }

    fn get_import(&self, name: &str) -> Value {
        // First look for the entry in the map.
        let import = self
            .imports
            .get(name)
            .expect(&format!("Import not found: {}", name));

        // Then check whether or not it's initialized.
        import
            .clone()
            .expect(&format!("Uninitialized import: {}", name))
    }

    fn set_import<N: Into<String>>(&mut self, name: N, value: Value) {
        self.imports.insert(name.into(), Some(value));
    }
}

pub struct ModuleExports {
    // Exports will start out as `None`s and are then filled in once the module
    // is initialized.
    pub exports: HashMap<String, Option<Value>>,
}

impl ModuleExports {
    fn new() -> Self {
        Self {
            exports: HashMap::new(),
        }
    }
}

#[derive(Clone)]
struct InnerLoadedFunction {
    module: WeakLoadedModule,
    id: u16,
    bytecode: BytecodeFunction,
}

/// Handle to a loaded function.
#[derive(Clone)]
pub struct LoadedFunction(Rc<InnerLoadedFunction>);

impl LoadedFunction {
    fn new(module: WeakLoadedModule, function: bytecode::layout::Function) -> Self {
        Self(Rc::new(InnerLoadedFunction {
            module,
            id: function.id,
            bytecode: BytecodeFunction::new(function),
        }))
    }

    pub fn id(&self) -> u16 {
        self.0.id
    }

    /// Returns a string indicating the base-name of the module it was defined
    /// in and its own name.
    pub fn qualified_name(&self) -> String {
        let module_basename = Path::new(&self.module().name())
            .file_name()
            .and_then(|os_str| os_str.to_str())
            .unwrap_or("(unknown)")
            .to_owned();
        let own_name = self.0.bytecode.name();
        format!("{}:{}", module_basename, own_name)
    }

    pub fn bytecode(&self) -> BytecodeFunction {
        self.0.bytecode.clone()
    }

    /// Returns whether or not this function binds/captures its environment
    /// when it is created.
    pub fn binds_on_create(&self) -> bool {
        self.0.bytecode.parent_bindings()
    }

    /// Returns a closure suitable for calling the function.
    pub fn build_closure_for_call(&self, parent: Option<Closure>) -> Option<Closure> {
        let bindings = self.0.bytecode.bindings();
        // Whether or not it needs to create bindings (a closure) when called.
        let binds_on_call = !bindings.is_empty() || self.0.bytecode.parent_bindings();
        if binds_on_call {
            let bindings = if bindings.is_empty() {
                None
            } else {
                Some(bindings)
            };
            Some(Closure::new(bindings, parent))
        } else {
            None
        }
    }

    pub fn module(&self) -> LoadedModule {
        let module = self.0.module.upgrade().expect("Module has been dropped");
        LoadedModule(module)
    }
}

pub struct InnerBytecodeFunction {
    function: bytecode::layout::Function,
}

impl InnerBytecodeFunction {
    pub fn name(&self) -> &str {
        &self.function.name
    }

    #[inline]
    pub fn registers(&self) -> u8 {
        self.function.registers
    }

    #[inline]
    pub fn locals(&self) -> u8 {
        self.function.locals
    }

    #[inline]
    pub fn instruction(&self, instruction_address: usize) -> bytecode::layout::Instruction {
        self.function.instructions[instruction_address].clone()
    }

    pub fn locals_names(&self) -> Vec<String> {
        self.function.locals_names.clone()
    }

    pub fn has_bindings(&self) -> bool {
        !self.function.bindings.is_empty()
    }

    pub fn bindings(&self) -> HashSet<String> {
        self.function.bindings.clone()
    }

    pub fn parent_bindings(&self) -> bool {
        self.function.parent_bindings
    }
}

#[derive(Clone)]
pub struct BytecodeFunction(Rc<InnerBytecodeFunction>);

impl BytecodeFunction {
    pub fn new(function: bytecode::layout::Function) -> Self {
        Self(Rc::new(InnerBytecodeFunction { function }))
    }
}

impl Deref for BytecodeFunction {
    type Target = InnerBytecodeFunction;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<|endoftext|>


<commit_before>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicBool, Ordering};
use std::time::Duration;
use rand::{Rng, XorShiftRng};
use crossbeam;
use crossbeam::sync::MsQueue;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta<T: Rng>(
  field: &mut Field,
  depth: u32,
  last_pos: Pos,
  player: Player,
  trajectories_pruning: &TrajectoriesPruning,
  mut alpha: i32,
  beta: i32,
  empty_board: &mut Vec<u32>,
  rng: &mut T,
  should_stop: &AtomicBool
) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.moves();
  if moves.is_empty() {
    return field.score(player);
  }
  for &pos in moves {
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(
      field,
      enemy,
      depth - 1,
      empty_board,
      rng,
      trajectories_pruning,
      pos,
      should_stop
    );
    let mut cur_estimation = -alpha_beta(
      field,
      depth - 1,
      pos,
      enemy,
      &next_trajectories_pruning,
      -alpha - 1,
      -alpha,
      empty_board,
      rng,
      should_stop
    );
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(
        field,
        depth - 1,
        pos,
        enemy,
        &next_trajectories_pruning,
        -beta,
        -cur_estimation,
        empty_board,
        rng,
        should_stop
      );
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel<T: Rng>(
  field: &mut Field,
  player: Player,
  depth: u32,
  alpha: i32,
  beta: i32,
  trajectories_pruning: &TrajectoriesPruning,
  rng: &mut T,
  best_move: &mut Option<Pos>,
  should_stop: &AtomicBool
) -> i32 {
  info!(
    target: MINIMAX_STR,
    "Starting parellel alpha beta with depth {}, player {} and beta {}.",
    depth,
    player,
    beta
  );
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.moves();
  debug!(
    target: MINIMAX_STR,
    "Moves in consideration: {:?}.",
    moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>()
  );
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let queue = MsQueue::new();
  if let Some(best_pos) = *best_move {
    queue.push(best_pos);
    for &pos in moves.iter().filter(|&&pos| pos != best_pos) {
      queue.push(pos);
    }
  } else {
    for &pos in moves.iter() {
      queue.push(pos);
    }
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let best_moves = MsQueue::new();
  crossbeam::scope(|scope| {
    for _ in 0 .. threads_count {
      let xor_shift_rng = rng.gen::<XorShiftRng>();
      scope.spawn(|| {
        let mut local_field = field.clone();
        let mut local_rng = xor_shift_rng;
        let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
        let mut local_best_move = 0;
        let mut local_alpha = alpha;
        let enemy = player.next();
        while let Some(pos) = queue.try_pop() {
          if should_stop.load(Ordering::Relaxed) {
            break;
          }
          local_field.put_point(pos, player);
          let next_trajectories_pruning = TrajectoriesPruning::from_last(
            &mut local_field,
            enemy,
            depth - 1,
            &mut local_empty_board,
            &mut local_rng,
            trajectories_pruning,
            pos,
            should_stop
          );
          if should_stop.load(Ordering::Relaxed) {
            break;
          }
          let cur_alpha = atomic_alpha.load(Ordering::Relaxed) as i32;
          if cur_alpha >= beta {
            break;
          }
          let mut cur_estimation = -alpha_beta(
            &mut local_field,
            depth - 1,
            pos,
            enemy,
            &next_trajectories_pruning,
            -cur_alpha - 1,
            -cur_alpha,
            &mut local_empty_board,
            &mut local_rng,
            should_stop
          );
          if cur_estimation > cur_alpha && !should_stop.load(Ordering::Relaxed) {
            cur_estimation = -alpha_beta(
              &mut local_field,
              depth - 1,
              pos,
              enemy,
              &next_trajectories_pruning,
              -beta,
              -cur_estimation,
              &mut local_empty_board,
              &mut local_rng,
              should_stop
            );
          }
          debug!(
            target: MINIMAX_STR,
            "Estimation for move ({}, {}) is {}, alpha is {}, beta is {}.",
            field.to_x(pos),
            field.to_y(pos),
            cur_estimation,
            cur_alpha,
            beta
          );
          local_field.undo();
          if cur_estimation > cur_alpha {
            local_alpha = cur_estimation;
            local_best_move = pos;
          }
          loop {
            let last_alpha = atomic_alpha.load(Ordering::Relaxed);
            if cur_estimation <= last_alpha as i32 || atomic_alpha.compare_and_swap(
              last_alpha,
              cur_estimation as isize,
              Ordering::Relaxed
            ) == last_alpha {
              break;
            }
          }
        }
        if local_best_move != 0 && local_alpha == atomic_alpha.load(Ordering::Relaxed) as i32 {
          best_moves.push((local_best_move, local_alpha));
        }
      });
    }
  });
  let mut result = 0;
  let best_alpha = atomic_alpha.load(Ordering::Relaxed) as i32;
  while let Some((pos, pos_alpha)) = best_moves.try_pop() {
    if pos_alpha == best_alpha {
      result = pos;
      break;
    }
  }
  if result == 0 {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  } else {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax<T: Rng>(field: &mut Field, player: Player, rng: &mut T, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(
    field,
    player,
    depth,
    &mut empty_board,
    rng,
    &should_stop
  );
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(
    field,
    player,
    depth,
    i32::min_value() + 1,
    i32::max_value(),
    &trajectories_pruning,
    rng,
    &mut best_move,
    &should_stop
  );
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_and_swap_exists(
    field,
    depth - 1,
    &mut empty_board,
    rng,
    &trajectories_pruning,
    &should_stop
  );
  info!(
    target: MINIMAX_STR,
    "Calculating of enemy estimation with upper bound {}. Player is {}",
    -estimation + 1,
    enemy
  );
  // Check if we could lose something if we don't make the current best move.
  // If we couldn't that means that the current best move is just a random move.
  if -alpha_beta_parallel(
    field,
    enemy,
    depth - 1,
    -estimation,
    -estimation + 1,
    &enemy_trajectories_pruning,
    rng,
    &mut enemy_best_move,
    &should_stop
  ) < estimation {
    info!(
      target: MINIMAX_STR,
      "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.",
      best_move.map(|pos| (field.to_x(pos), field.to_y(pos))),
      estimation
    );
    best_move
  } else {
    info!(
      target: MINIMAX_STR,
      "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.",
      estimation
    );
    None
  }
}

pub fn minimax_with_time<T: Rng>(field: &mut Field, player: Player, rng: &mut T, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  crossbeam::scope(|scope| {
    scope.spawn(|| {
      thread::sleep(Duration::from_millis(time as u64));
      debug!(target: MINIMAX_STR, "Time-out!");
      should_stop.store(true, Ordering::Relaxed);
    });
    let enemy = player.next();
    let mut depth = 1;
    let mut best_move = None;
    let mut cur_best_move = None;
    let mut enemy_best_move = None;
    let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
    let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board, rng, &should_stop);
    while !should_stop.load(Ordering::Relaxed) {
      let estimation = alpha_beta_parallel(
        field,
        player,
        depth,
        i32::min_value() + 1,
        i32::max_value(),
        &trajectories_pruning,
        rng,
        &mut cur_best_move,
        &should_stop
      );
      if should_stop.load(Ordering::Relaxed) {
        // If we found the best move on the previous iteration then the current best move can't be worse than that move.
        // Otherwise it's possible that the current best move is just a random move.
        if !best_move.is_none() {
          best_move = cur_best_move;
        }
        break;
      }
      let enemy_trajectories_pruning = TrajectoriesPruning::dec_and_swap_exists(
        field,
        depth - 1,
        &mut empty_board,
        rng,
        &trajectories_pruning,
        &should_stop
      );
      if should_stop.load(Ordering::Relaxed) {
        // See previous comment.
        if !best_move.is_none() {
          best_move = cur_best_move;
        }
        break;
      }
      // Check if we could lose something if we don't make the current best move.
      // If we couldn't that means that the current best move is just a random move.
      // If we found the best move on previous iteration then likely the current best move is also the best one.
      best_move = if !best_move.is_none() || -alpha_beta_parallel(
        field,
        enemy,
        depth - 1,
        -estimation,
        -estimation + 1,
        &enemy_trajectories_pruning,
        rng,
        &mut enemy_best_move,
        &should_stop
      ) < estimation {
        cur_best_move
      } else {
        None
      };
      if should_stop.load(Ordering::Relaxed) {
        break;
      }
      depth += 1;
      trajectories_pruning = TrajectoriesPruning::inc_exists(
        field,
        player,
        depth,
        &mut empty_board,
        rng,
        &trajectories_pruning,
        &should_stop
      );
    }
    best_move
  })
}
<commit_msg>Minimax fix.<commit_after>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicBool, Ordering};
use std::time::Duration;
use rand::{Rng, XorShiftRng};
use crossbeam;
use crossbeam::sync::MsQueue;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta<T: Rng>(
  field: &mut Field,
  depth: u32,
  last_pos: Pos,
  player: Player,
  trajectories_pruning: &TrajectoriesPruning,
  mut alpha: i32,
  beta: i32,
  empty_board: &mut Vec<u32>,
  rng: &mut T,
  should_stop: &AtomicBool
) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.moves();
  if moves.is_empty() {
    return field.score(player);
  }
  for &pos in moves {
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(
      field,
      enemy,
      depth - 1,
      empty_board,
      rng,
      trajectories_pruning,
      pos,
      should_stop
    );
    let mut cur_estimation = -alpha_beta(
      field,
      depth - 1,
      pos,
      enemy,
      &next_trajectories_pruning,
      -alpha - 1,
      -alpha,
      empty_board,
      rng,
      should_stop
    );
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(
        field,
        depth - 1,
        pos,
        enemy,
        &next_trajectories_pruning,
        -beta,
        -cur_estimation,
        empty_board,
        rng,
        should_stop
      );
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel<T: Rng>(
  field: &mut Field,
  player: Player,
  depth: u32,
  alpha: i32,
  beta: i32,
  trajectories_pruning: &TrajectoriesPruning,
  rng: &mut T,
  best_move: &mut Option<Pos>,
  should_stop: &AtomicBool
) -> i32 {
  info!(
    target: MINIMAX_STR,
    "Starting parellel alpha beta with depth {}, player {} and beta {}.",
    depth,
    player,
    beta
  );
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.moves();
  debug!(
    target: MINIMAX_STR,
    "Moves in consideration: {:?}.",
    moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>()
  );
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let queue = MsQueue::new();
  if let Some(best_pos) = *best_move {
    queue.push(best_pos);
    for &pos in moves.iter().filter(|&&pos| pos != best_pos) {
      queue.push(pos);
    }
  } else {
    for &pos in moves.iter() {
      queue.push(pos);
    }
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let best_moves = MsQueue::new();
  crossbeam::scope(|scope| {
    for _ in 0 .. threads_count {
      let xor_shift_rng = rng.gen::<XorShiftRng>();
      scope.spawn(|| {
        let mut local_field = field.clone();
        let mut local_rng = xor_shift_rng;
        let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
        let mut local_best_move = 0;
        let mut local_alpha = alpha;
        let enemy = player.next();
        while let Some(pos) = queue.try_pop() {
          if should_stop.load(Ordering::Relaxed) {
            break;
          }
          local_field.put_point(pos, player);
          let next_trajectories_pruning = TrajectoriesPruning::from_last(
            &mut local_field,
            enemy,
            depth - 1,
            &mut local_empty_board,
            &mut local_rng,
            trajectories_pruning,
            pos,
            should_stop
          );
          if should_stop.load(Ordering::Relaxed) {
            break;
          }
          let cur_alpha = atomic_alpha.load(Ordering::Relaxed) as i32;
          if cur_alpha >= beta {
            break;
          }
          let mut cur_estimation = -alpha_beta(
            &mut local_field,
            depth - 1,
            pos,
            enemy,
            &next_trajectories_pruning,
            -cur_alpha - 1,
            -cur_alpha,
            &mut local_empty_board,
            &mut local_rng,
            should_stop
          );
          if should_stop.load(Ordering::Relaxed) {
            break;
          }
          if cur_estimation > cur_alpha {
            cur_estimation = -alpha_beta(
              &mut local_field,
              depth - 1,
              pos,
              enemy,
              &next_trajectories_pruning,
              -beta,
              -cur_estimation,
              &mut local_empty_board,
              &mut local_rng,
              should_stop
            );
          }
          // We should check it before the best move assignment because it's possible
          // that current estimation is higher than real in case of time out.
          if should_stop.load(Ordering::Relaxed) {
            break;
          }
          debug!(
            target: MINIMAX_STR,
            "Estimation for move ({}, {}) is {}, alpha is {}, beta is {}.",
            field.to_x(pos),
            field.to_y(pos),
            cur_estimation,
            cur_alpha,
            beta
          );
          local_field.undo();
          if cur_estimation > cur_alpha {
            local_alpha = cur_estimation;
            local_best_move = pos;
          }
          loop {
            let last_alpha = atomic_alpha.load(Ordering::Relaxed);
            if cur_estimation <= last_alpha as i32 || atomic_alpha.compare_and_swap(
              last_alpha,
              cur_estimation as isize,
              Ordering::Relaxed
            ) == last_alpha {
              break;
            }
          }
        }
        if local_best_move != 0 && local_alpha == atomic_alpha.load(Ordering::Relaxed) as i32 {
          best_moves.push((local_best_move, local_alpha));
        }
      });
    }
  });
  let mut result = 0;
  let best_alpha = atomic_alpha.load(Ordering::Relaxed) as i32;
  while let Some((pos, pos_alpha)) = best_moves.try_pop() {
    if pos_alpha == best_alpha {
      result = pos;
      break;
    }
  }
  if result == 0 {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  } else {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax<T: Rng>(field: &mut Field, player: Player, rng: &mut T, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(
    field,
    player,
    depth,
    &mut empty_board,
    rng,
    &should_stop
  );
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(
    field,
    player,
    depth,
    i32::min_value() + 1,
    i32::max_value(),
    &trajectories_pruning,
    rng,
    &mut best_move,
    &should_stop
  );
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_and_swap_exists(
    field,
    depth - 1,
    &mut empty_board,
    rng,
    &trajectories_pruning,
    &should_stop
  );
  info!(
    target: MINIMAX_STR,
    "Calculating of enemy estimation with upper bound {}. Player is {}",
    -estimation + 1,
    enemy
  );
  // Check if we could lose something if we don't make the current best move.
  // If we couldn't that means that the current best move is just a random move.
  if -alpha_beta_parallel(
    field,
    enemy,
    depth - 1,
    -estimation,
    -estimation + 1,
    &enemy_trajectories_pruning,
    rng,
    &mut enemy_best_move,
    &should_stop
  ) < estimation {
    info!(
      target: MINIMAX_STR,
      "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.",
      best_move.map(|pos| (field.to_x(pos), field.to_y(pos))),
      estimation
    );
    best_move
  } else {
    info!(
      target: MINIMAX_STR,
      "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.",
      estimation
    );
    None
  }
}

pub fn minimax_with_time<T: Rng>(field: &mut Field, player: Player, rng: &mut T, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  crossbeam::scope(|scope| {
    scope.spawn(|| {
      thread::sleep(Duration::from_millis(time as u64));
      debug!(target: MINIMAX_STR, "Time-out!");
      should_stop.store(true, Ordering::Relaxed);
    });
    let enemy = player.next();
    let mut depth = 1;
    let mut best_move = None;
    let mut cur_best_move = None;
    let mut enemy_best_move = None;
    let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
    let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board, rng, &should_stop);
    while !should_stop.load(Ordering::Relaxed) {
      let estimation = alpha_beta_parallel(
        field,
        player,
        depth,
        i32::min_value() + 1,
        i32::max_value(),
        &trajectories_pruning,
        rng,
        &mut cur_best_move,
        &should_stop
      );
      if should_stop.load(Ordering::Relaxed) {
        // If we found the best move on the previous iteration then the current best move can't be worse than that move.
        // Otherwise it's possible that the current best move is just a random move.
        if !best_move.is_none() {
          best_move = cur_best_move;
        }
        break;
      }
      let enemy_trajectories_pruning = TrajectoriesPruning::dec_and_swap_exists(
        field,
        depth - 1,
        &mut empty_board,
        rng,
        &trajectories_pruning,
        &should_stop
      );
      if should_stop.load(Ordering::Relaxed) {
        // See previous comment.
        if !best_move.is_none() {
          best_move = cur_best_move;
        }
        break;
      }
      // Check if we could lose something if we don't make the current best move.
      // If we couldn't that means that the current best move is just a random move.
      // If we found the best move on previous iteration then likely the current best move is also the best one.
      best_move = if !best_move.is_none() || -alpha_beta_parallel(
        field,
        enemy,
        depth - 1,
        -estimation,
        -estimation + 1,
        &enemy_trajectories_pruning,
        rng,
        &mut enemy_best_move,
        &should_stop
      ) < estimation {
        cur_best_move
      } else {
        None
      };
      if should_stop.load(Ordering::Relaxed) {
        break;
      }
      depth += 1;
      trajectories_pruning = TrajectoriesPruning::inc_exists(
        field,
        player,
        depth,
        &mut empty_board,
        rng,
        &trajectories_pruning,
        &should_stop
      );
    }
    best_move
  })
}
<|endoftext|>


<commit_before>use std::fmt;
use std::mem;
use std::ptr;
use std::slice;
use std::iter;
use std::io::{self, Write};
use std::cmp;
use std::rt::heap;
use std::cell::Cell;
use std::marker::PhantomData;
use std::intrinsics;

// A chunk in the vector
struct Chunk<T> {
    // Previous chunk
    prev: *mut Chunk<T>,
    // Next chunk
    next: *mut Chunk<T>,
    // Count of items
    len: usize,
    // Capacity
    cap: usize,
    // Items follow in memory
    items: [T; 0]
}

pub struct MonoVec<T> {
    head: Cell<*mut Chunk<T>>,
    tail: Cell<*mut Chunk<T>>,
    _ph: PhantomData<T>
}

unsafe impl<T: Send> Send for MonoVec<T> {}

impl<T> Chunk<T> {
    fn array_size(len: usize) -> usize {
        len.checked_mul(mem::size_of::<T>()).unwrap()
    }

    fn mem_size(len: usize) -> usize {
        mem::size_of::<Self>().checked_add(Self::array_size(len)).unwrap()
    }

    fn new(cap: usize) -> *mut Self {
        unsafe {
            let res = heap::allocate(Self::mem_size(cap),
                                     mem::align_of::<Self>()) as *mut Self;
            ptr::write(&mut (*res).prev, ptr::null_mut());
            ptr::write(&mut (*res).next, ptr::null_mut());
            ptr::write(&mut (*res).len, 0);
            ptr::write(&mut (*res).cap, cap);
            res
        }
    }
}

impl<T> MonoVec<T> {
    pub fn new() -> Self {
        Self::with_capacity(8)
    }

    pub fn with_capacity(cap: usize) -> Self {
        let head = Chunk::new(cmp::max(cap, 1));
        MonoVec {
            head: Cell::new(head),
            tail: Cell::new(head),
            _ph: PhantomData
        }
    }

    // FIXME: track total len in header to make this O(1)?
    pub fn len(&self) -> usize {
        let mut len = 0;
        let mut cur = self.head.get();

        while !cur.is_null() {
            unsafe {
                len += (*cur).len;
                cur = (*cur).next;
            }
        }
        len
    }

    // Reserves space for at least `len` more contiguous elements, returning
    // a pointer to the space and the available capacity (which may be > `len`)
    #[inline(never)]
    pub fn reserve(&self, len: usize) -> (*mut T, usize) {
        unsafe {
            let tail = self.tail.get();
            let cap = (*tail).cap;
            if cap - (*tail).len < len {
                // Grow capacity exponentially to amortize cost of insertions
                let mut new_cap = cap.checked_mul(2).unwrap();
                while new_cap < len {
                    new_cap = new_cap.checked_mul(2).unwrap();
                }
                let new = Chunk::new(new_cap);

                (*new).prev = tail;
                (*tail).next = new;
                self.tail.set(new);
            }

            let tail = self.tail.get();
            let ptr = (*tail).items.as_mut_ptr().offset((*tail).len as isize);
            let cap = (*tail).cap - (*tail).len;
            (ptr, cap)
        }
    }

    // Adds to length of curent chunk.  Usually used after
    // writing into reserved space.
    pub unsafe fn add_len(&self, len: usize) {
        let tail = self.tail.get();
        (*tail).len += len;
    }

    // Shrinks length of allocation at (ptr, ptr + old_len) if possible
    pub unsafe fn shrink_len(&self, ptr: *mut T, old_len: usize, new_len: usize) {
        let tail = self.tail.get();
        if ptr.offset(old_len as isize) == (*tail).items.as_mut_ptr().offset((*tail).len as isize) {
            (*tail).len = (*tail).len - old_len + new_len;
        }
    }

    #[inline]
    pub fn push(&self, elem: T) -> &T {
        let (ptr, _) = self.reserve(1);
        unsafe {
            ptr::write(ptr, elem);
            self.add_len(1);
            &*ptr
        }
    }

    pub fn extend_as_slice<E: IntoIterator<Item=T>>(&self, elems: E) -> &[T]
            where E::IntoIter: ExactSizeIterator {
        let iter = elems.into_iter();
        let len = iter.len();
        let (ptr, _) = self.reserve(len);
        let mut cur = ptr;
        unsafe {
            for elem in iter {
                ptr::write(cur, elem);
                cur = cur.offset(1);
            }
            self.add_len(len);
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn chunks(&self) -> Chunks<T> {
        Chunks {
            start: self.head.get(),
            end: self.tail.get(),
            _ph: PhantomData
        }
    }

    pub fn items(&self) -> Items<T> {
        #[inline(always)]
        fn id<T>(x: T) -> T { x }
        Items(self.chunks().flat_map(id))
    }
}

impl<'a, T: 'a> IntoIterator for &'a MonoVec<T> {
    type Item = &'a T;
    type IntoIter = Items<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.items()
    }
}

impl io::Write for MonoVec<u8> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let (ptr, len) = self.reserve(0);
        let len = cmp::min(len, buf.len());
        if len != 0 {
            unsafe {
                ptr::copy_nonoverlapping(buf.as_ptr(), ptr, len);
            }
        }
        Ok(len)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl<T> Drop for MonoVec<T> {
    fn drop(&mut self) {
        unsafe {
            let mut chunk = self.head.get();
            let mut next;
            while !chunk.is_null() {
                next = (*chunk).next;
                if intrinsics::needs_drop::<T>() {
                    let mut cur = (*chunk).items.as_mut_ptr();
                    let end = cur.offset((*chunk).len as isize);
                    while cur < end {
                        intrinsics::drop_in_place(cur);
                        cur = cur.offset(1);
                    }
                }
                heap::deallocate(chunk as *mut u8,
                                 (*chunk).cap * mem::size_of::<T>(),
                                 mem::min_align_of::<T>());
                chunk = next;
            }
        }
    }
}

pub struct Chunks<'a, T: 'a> {
    start: *mut Chunk<T>,
    end: *mut Chunk<T>,
    _ph: PhantomData<&'a [T]>
}

impl<'a, T> Iterator for Chunks<'a, T> {
    type Item = &'a [T];

    fn next(&mut self) -> Option<&'a [T]> {
        let chunk = self.start;
        if chunk.is_null() {
            None
        } else {
            unsafe {
                if chunk == self.end {
                    self.start = ptr::null_mut();
                    self.end = ptr::null_mut()
                } else {
                    self.start = (*chunk).next
                }
                Some(slice::from_raw_parts((*chunk).items.as_ptr(), (*chunk).len))
            }
        }
    }
}

unsafe impl <'a, T:Send> Send for Chunks<'a, T> {}
// FIXME: need to double check that this holds.
// It certainly doesn't for the vec itself.
unsafe impl <'a, T:Sync> Sync for Chunks<'a, T> {}

impl<'a, T> DoubleEndedIterator for Chunks<'a, T> {
    fn next_back(&mut self) -> Option<&'a [T]> {
        let chunk = self.end;
        if chunk.is_null() {
            None
        } else {
            unsafe {
                if chunk == self.start {
                    self.start = ptr::null_mut();
                    self.end = ptr::null_mut()
                } else {
                    self.end = (*chunk).prev
                }
                Some(slice::from_raw_parts((*chunk).items.as_ptr(), (*chunk).len))
            }
        }
    }
}

// Wrapper to hide ugly adapter type
pub struct Items<'a, T: 'a>(iter::FlatMap<Chunks<'a, T>, &'a [T], fn(&'a [T]) -> &'a [T]>);

impl<'a, T: 'a> Iterator for Items<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> {
        self.0.next()
    }
}

impl<'a, T: 'a> DoubleEndedIterator for Items<'a, T> {
    fn next_back(&mut self) -> Option<&'a T> {
        self.0.next_back()
    }
}

impl<T: fmt::Debug> fmt::Debug for MonoVec<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut need_comma = false;
        try!(write!(f, "["));
        for elem in self {
            if need_comma {
                try!(write!(f, ", "));
            }
            try!(elem.fmt(f));
            need_comma = true;
        }
        write!(f, "]")
    }
}
<commit_msg>Deal with OOM<commit_after>use std::fmt;
use std::mem;
use std::ptr;
use std::slice;
use std::iter;
use std::io::{self, Write};
use std::cmp;
use std::rt::heap;
use std::cell::Cell;
use std::marker::PhantomData;
use std::intrinsics;

// A chunk in the vector
struct Chunk<T> {
    // Previous chunk
    prev: *mut Chunk<T>,
    // Next chunk
    next: *mut Chunk<T>,
    // Count of items
    len: usize,
    // Capacity
    cap: usize,
    // Items follow in memory
    items: [T; 0]
}

pub struct MonoVec<T> {
    head: Cell<*mut Chunk<T>>,
    tail: Cell<*mut Chunk<T>>,
    _ph: PhantomData<T>
}

unsafe impl<T: Send> Send for MonoVec<T> {}

impl<T> Chunk<T> {
    fn array_size(len: usize) -> usize {
        len.checked_mul(mem::size_of::<T>()).unwrap()
    }

    fn mem_size(len: usize) -> usize {
        mem::size_of::<Self>().checked_add(Self::array_size(len)).unwrap()
    }

    fn new(cap: usize) -> *mut Self {
        unsafe {
            let res = heap::allocate(Self::mem_size(cap),
                                     mem::align_of::<Self>()) as *mut Self;
            if res.is_null() {
                panic!("MonoVec: failed to allocate chunk!")
            }
            ptr::write(&mut (*res).prev, ptr::null_mut());
            ptr::write(&mut (*res).next, ptr::null_mut());
            ptr::write(&mut (*res).len, 0);
            ptr::write(&mut (*res).cap, cap);
            res
        }
    }
}

impl<T> MonoVec<T> {
    pub fn new() -> Self {
        Self::with_capacity(8)
    }

    pub fn with_capacity(cap: usize) -> Self {
        let head = Chunk::new(cmp::max(cap, 1));
        MonoVec {
            head: Cell::new(head),
            tail: Cell::new(head),
            _ph: PhantomData
        }
    }

    // FIXME: track total len in header to make this O(1)?
    pub fn len(&self) -> usize {
        let mut len = 0;
        let mut cur = self.head.get();

        while !cur.is_null() {
            unsafe {
                len += (*cur).len;
                cur = (*cur).next;
            }
        }
        len
    }

    // Reserves space for at least `len` more contiguous elements, returning
    // a pointer to the space and the available capacity (which may be > `len`)
    #[inline(never)]
    pub fn reserve(&self, len: usize) -> (*mut T, usize) {
        unsafe {
            let tail = self.tail.get();
            let cap = (*tail).cap;
            if cap - (*tail).len < len {
                // Grow capacity exponentially to amortize cost of insertions
                let mut new_cap = cap.checked_mul(2).unwrap();
                while new_cap < len {
                    new_cap = new_cap.checked_mul(2).unwrap();
                }
                let new = Chunk::new(new_cap);

                (*new).prev = tail;
                (*tail).next = new;
                self.tail.set(new);
            }

            let tail = self.tail.get();
            let ptr = (*tail).items.as_mut_ptr().offset((*tail).len as isize);
            let cap = (*tail).cap - (*tail).len;
            (ptr, cap)
        }
    }

    // Adds to length of curent chunk.  Usually used after
    // writing into reserved space.
    pub unsafe fn add_len(&self, len: usize) {
        let tail = self.tail.get();
        (*tail).len += len;
    }

    // Shrinks length of allocation at (ptr, ptr + old_len) if possible
    pub unsafe fn shrink_len(&self, ptr: *mut T, old_len: usize, new_len: usize) {
        let tail = self.tail.get();
        if ptr.offset(old_len as isize) == (*tail).items.as_mut_ptr().offset((*tail).len as isize) {
            (*tail).len = (*tail).len - old_len + new_len;
        }
    }

    #[inline]
    pub fn push(&self, elem: T) -> &T {
        let (ptr, _) = self.reserve(1);
        unsafe {
            ptr::write(ptr, elem);
            self.add_len(1);
            &*ptr
        }
    }

    pub fn extend_as_slice<E: IntoIterator<Item=T>>(&self, elems: E) -> &[T]
            where E::IntoIter: ExactSizeIterator {
        let iter = elems.into_iter();
        let len = iter.len();
        let (ptr, _) = self.reserve(len);
        let mut cur = ptr;
        unsafe {
            for elem in iter {
                ptr::write(cur, elem);
                cur = cur.offset(1);
            }
            self.add_len(len);
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn chunks(&self) -> Chunks<T> {
        Chunks {
            start: self.head.get(),
            end: self.tail.get(),
            _ph: PhantomData
        }
    }

    pub fn items(&self) -> Items<T> {
        #[inline(always)]
        fn id<T>(x: T) -> T { x }
        Items(self.chunks().flat_map(id))
    }
}

impl<'a, T: 'a> IntoIterator for &'a MonoVec<T> {
    type Item = &'a T;
    type IntoIter = Items<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.items()
    }
}

impl io::Write for MonoVec<u8> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let (ptr, len) = self.reserve(0);
        let len = cmp::min(len, buf.len());
        if len != 0 {
            unsafe {
                ptr::copy_nonoverlapping(buf.as_ptr(), ptr, len);
            }
        }
        Ok(len)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl<T> Drop for MonoVec<T> {
    fn drop(&mut self) {
        unsafe {
            let mut chunk = self.head.get();
            let mut next;
            while !chunk.is_null() {
                next = (*chunk).next;
                if intrinsics::needs_drop::<T>() {
                    let mut cur = (*chunk).items.as_mut_ptr();
                    let end = cur.offset((*chunk).len as isize);
                    while cur < end {
                        intrinsics::drop_in_place(cur);
                        cur = cur.offset(1);
                    }
                }
                heap::deallocate(chunk as *mut u8,
                                 (*chunk).cap * mem::size_of::<T>(),
                                 mem::min_align_of::<T>());
                chunk = next;
            }
        }
    }
}

pub struct Chunks<'a, T: 'a> {
    start: *mut Chunk<T>,
    end: *mut Chunk<T>,
    _ph: PhantomData<&'a [T]>
}

impl<'a, T> Iterator for Chunks<'a, T> {
    type Item = &'a [T];

    fn next(&mut self) -> Option<&'a [T]> {
        let chunk = self.start;
        if chunk.is_null() {
            None
        } else {
            unsafe {
                if chunk == self.end {
                    self.start = ptr::null_mut();
                    self.end = ptr::null_mut()
                } else {
                    self.start = (*chunk).next
                }
                Some(slice::from_raw_parts((*chunk).items.as_ptr(), (*chunk).len))
            }
        }
    }
}

unsafe impl <'a, T:Send> Send for Chunks<'a, T> {}
// FIXME: need to double check that this holds.
// It certainly doesn't for the vec itself.
unsafe impl <'a, T:Sync> Sync for Chunks<'a, T> {}

impl<'a, T> DoubleEndedIterator for Chunks<'a, T> {
    fn next_back(&mut self) -> Option<&'a [T]> {
        let chunk = self.end;
        if chunk.is_null() {
            None
        } else {
            unsafe {
                if chunk == self.start {
                    self.start = ptr::null_mut();
                    self.end = ptr::null_mut()
                } else {
                    self.end = (*chunk).prev
                }
                Some(slice::from_raw_parts((*chunk).items.as_ptr(), (*chunk).len))
            }
        }
    }
}

// Wrapper to hide ugly adapter type
pub struct Items<'a, T: 'a>(iter::FlatMap<Chunks<'a, T>, &'a [T], fn(&'a [T]) -> &'a [T]>);

impl<'a, T: 'a> Iterator for Items<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> {
        self.0.next()
    }
}

impl<'a, T: 'a> DoubleEndedIterator for Items<'a, T> {
    fn next_back(&mut self) -> Option<&'a T> {
        self.0.next_back()
    }
}

impl<T: fmt::Debug> fmt::Debug for MonoVec<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut need_comma = false;
        try!(write!(f, "["));
        for elem in self {
            if need_comma {
                try!(write!(f, ", "));
            }
            try!(elem.fmt(f));
            need_comma = true;
        }
        write!(f, "]")
    }
}
<|endoftext|>


<commit_before>use clap::{App as Clapp, Arg as Clarg};
use std::io::{self, Write};
use std::str::FromStr;
use std::fmt::Arguments as FormatArguments;
use std::path::PathBuf;
use std::error::Error;
use yaml_file_handler::Yaml;
use yaml_file_handler::yaml_handler::FileHandler as YamlFileHandler;

#[derive(Debug)]
pub struct Options {
	pub port: u16,
	pub ssl: Option<(String, String)>,
}

impl Options {
	/// Parses commandline arguments into an [`Options`](#) instance
	///
	/// Optionally reads from a config file in [YAML](http://yaml.org) format, however commandline arguments take preference thereover.
	/// The config file format is non-trivial, see `"example/config.yml"`.
	pub fn parse() -> Options {
		const PORT_USAGE: &'static str = "-p --port [port] 'Specifies the port the server will run on, will prompt if not specified'";
		const SSL_USAGE: &'static str = "--ssl [ssl] 'Sets SSL cert and key in the format: \"CERT_PATH;CERT_KEY_PATH\"'";
		const USAGE: &'static str = "-c --config=[conf] 'Sets config file to load, values will be overriden by commandline args'";

		let matches = Clapp::new("chattium-oxide-server").version(env!("CARGO_PKG_VERSION"))
		                                                 .author("nabijaczleweli <nabijaczleweli@gmail.com>")
		                                                 .about("Chat server for chattium-oxide-client")
		                                                 .args_from_usage(USAGE)
		                                                 .arg(Clarg::from_usage(PORT_USAGE).validator(|val| {
		                                                 	match u16::from_str(&*&val) {
		                                                 		Ok(_) => Ok(()),
		                                                 		Err(error) => Err(error.description().to_string()),
		                                                 	}
		                                                 }))
		                                                 .arg(Clarg::from_usage(SSL_USAGE).validator(|val| {
		                                                 	if val.chars().find(|c| *c == ';').is_some() {
		                                                 		if val.len() >= 3 {  // Naive, replace with something better, maybe?
		                                                 			Ok(())
		                                                 		} else {
		                                                 			Err(String::from("the value must contain 2 paths"))
		                                                 		}
		                                                 	} else {
		                                                 		Err(String::from("the value must contain the separator"))
		                                                 	}
		                                                 }))
		                                                 .get_matches();
		let mut port: Option<u16> = None;
		let mut ssl_cert: Option<String> = None;
		let mut ssl_key: Option<String> = None;
		if let Some(conf) = matches.value_of("conf") {
			let mut yaml = YamlFileHandler::new();
			if yaml.add_files(vec![conf]) {
				if let Some(yaml) = yaml.read_all_files().as_ref().map(|all| {
					let mut b = PathBuf::from(conf);
					b.set_extension("");
					&all[b.file_name().unwrap().to_str().unwrap()]
				}) {
					if let Some(map) = yaml["ssl"].as_hash() {
						ssl_cert = if let Some(cert) = map.get(&Yaml::String("cert".to_string())) {cert.as_str().map(|c| c.to_string())} else {None};
						ssl_key  = if let Some(key)  = map.get(&Yaml::String("key".to_string()))  {key .as_str().map(|k| k.to_string())} else {None};
					}
					port = yaml["port"].as_i64().map(|p| p as u16);
				}
			}
		}

		if let Some(cport) = matches.value_of("port") {port = Some(u16::from_str(cport).unwrap())}  // Validated using Arg::validator
		if let Some(cssl) = matches.value_of("ssl") {
			let mut paths = cssl.split(';');
			ssl_cert = Some(paths.next().unwrap().to_string());
			ssl_key  = Some(paths.next().unwrap().to_string());
		}

		if port.is_none() {
			let mut tport: Option<u16> = None;
			while tport.is_none() {
				match read_prompted(format_args!("No port specified.\nPlease type in the port now: ")) {
					Ok(rport) =>
						match rport {
							Some(rport) =>
								match u16::from_str(&*&rport) {
									Ok(port) => tport = Some(port),
									Err(error) => println!("Invalid port value: {}", error),
								},
							None => (),
						},
					Err(error) => println!("Couldn't read port: {}", error),
				}
			}
			port = tport;
		}

		assert!(port.is_some());
		Options{
			port: port.unwrap(),
			ssl: match (ssl_cert, ssl_key) {
				(Some(cert), Some(key)) => Some((cert, key)),
				(None, Some(key)) => {
					println!("SSL key specified (\"{}\"), but certificate not specified", key);
					None
				},
				(Some(cert), None) => {
					println!("SSL key not specified, but certificate specified (\"{}\")", cert);
					None
				},
				(None, None) => None,
			},
		}
	}
}

fn read_prompted(prompt: FormatArguments) -> io::Result<Option<String>> {
	try!(io::stdout().write_fmt(prompt));
	try!(io::stdout().flush());

	let mut obuf = String::new();
	try!(io::stdin().read_line(&mut obuf));  // Don't match on this directly, because it returns with "\r\n"
	let obuf = obuf.trim();
	Ok(match obuf.len() {
		0 => None,
		_ => Some(obuf.trim().to_string()),
	})
}
<commit_msg>Adjust code for clap update<commit_after>use clap::{App as Clapp, Arg as Clarg};
use std::io::{self, Write};
use std::str::FromStr;
use std::fmt::Arguments as FormatArguments;
use std::path::PathBuf;
use std::error::Error;
use yaml_file_handler::Yaml;
use yaml_file_handler::yaml_handler::FileHandler as YamlFileHandler;

#[derive(Debug)]
pub struct Options {
	pub port: u16,
	pub ssl: Option<(String, String)>,
}

impl Options {
	/// Parses commandline arguments into an [`Options`](#) instance
	///
	/// Optionally reads from a config file in [YAML](http://yaml.org) format, however commandline arguments take preference thereover.
	/// The config file format is non-trivial, see `"example/config.yml"`.
	pub fn parse() -> Options {
		const PORT_USAGE: &'static str = "-p --port [PORT]          'Specifies the port the server will run on, will prompt if not specified'";
		const USAGE:      &'static str = "--ssl [SSL_CERT;SSL_KEY]  'Sets SSL cert and key to use
		                                  -c --config=[CONFIG_FILE] 'Sets config file to load, values will be overriden by commandline args'";

		let matches = Clapp::new("chattium-oxide-server").version(env!("CARGO_PKG_VERSION"))
		                                                 .author("nabijaczleweli <nabijaczleweli@gmail.com>")
		                                                 .about("Chat server for chattium-oxide-client")
		                                                 .args_from_usage(USAGE)
		                                                 .arg(Clarg::from_usage(PORT_USAGE).validator(|val| {
		                                                 	match u16::from_str(&*&val) {
		                                                 		Ok(_) => Ok(()),
		                                                 		Err(error) => Err(error.description().to_string()),
		                                                 	}
		                                                 }))
		                                                 .get_matches();
		let mut port    : Option<u16>    = None;
		let mut ssl_cert: Option<String> = None;
		let mut ssl_key : Option<String> = None;

		if let Some(config) = matches.value_of("config") {
			let mut yaml = YamlFileHandler::new();
			if yaml.add_files(vec![config]) {
				if let Some(yaml) = yaml.read_all_files().as_ref().map(|all| {
					let mut b = PathBuf::from(config);
					b.set_extension("");
					&all[b.file_name().unwrap().to_str().unwrap()]
				}) {
					if let Some(map) = yaml["ssl"].as_hash() {
						ssl_cert = if let Some(cert) = map.get(&Yaml::String("cert".to_string())) {cert.as_str().map(|c| c.to_string())} else {None};
						ssl_key  = if let Some(key)  = map.get(&Yaml::String("key".to_string()))  {key .as_str().map(|k| k.to_string())} else {None};
					}
					port = yaml["port"].as_i64().map(|p| p as u16);
				}
			}
		}

		if let Some(cport) = matches.value_of("port") {port = Some(u16::from_str(cport).unwrap())}  // Validated using Arg::validator
		if let Some(cssl) = matches.values_of("ssl") {
			let cssl = cssl.collect::<Vec<_>>().join(";");
			let mut paths = cssl.split(';');
			ssl_cert = Some(paths.next().unwrap().to_string());
			ssl_key  = Some(paths.next().unwrap().to_string());
		}

		if port.is_none() {
			let mut tport: Option<u16> = None;
			while tport.is_none() {
				match read_prompted(format_args!("No port specified.\nPlease type in the port now: ")) {
					Ok(rport) =>
						match rport {
							Some(rport) =>
								match u16::from_str(&*&rport) {
									Ok(port) => tport = Some(port),
									Err(error) => println!("Invalid port value: {}", error),
								},
							None => (),
						},
					Err(error) => println!("Couldn't read port: {}", error),
				}
			}
			port = tport;
		}

		assert!(port.is_some());
		Options{
			port: port.unwrap(),
			ssl: match (ssl_cert, ssl_key) {
				(Some(cert), Some(key)) => Some((cert, key)),
				(None, Some(key)) => {
					println!("SSL key specified (\"{}\"), but certificate not specified", key);
					None
				},
				(Some(cert), None) => {
					println!("SSL key not specified, but certificate specified (\"{}\")", cert);
					None
				},
				(None, None) => None,
			},
		}
	}
}

fn read_prompted(prompt: FormatArguments) -> io::Result<Option<String>> {
	try!(io::stdout().write_fmt(prompt));
	try!(io::stdout().flush());

	let mut obuf = String::new();
	try!(io::stdin().read_line(&mut obuf));  // Don't match on this directly, because it returns with "\r\n"
	let obuf = obuf.trim();
	Ok(match obuf.len() {
		0 => None,
		_ => Some(obuf.trim().to_string()),
	})
}
<|endoftext|>


<commit_before>#[cfg(not(feature = "no_std"))]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(not(feature = "no_std"))]
use std::cell::UnsafeCell;
#[cfg(not(feature = "no_std"))]
use std::ops::{Deref, DerefMut};

#[cfg(feature = "no_std")]
use core::prelude::*;

#[cfg(feature = "no_std")]
use core::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(feature = "no_std")]
use core::cell::UnsafeCell;
#[cfg(feature = "no_std")]
use core::ops::{Deref, DerefMut};

/// A reader-writer lock
///
/// This type of lock allows a number of readers or at most one writer at any
/// point in time. The write portion of this lock typically allows modification
/// of the underlying data (exclusive access) and the read portion of this lock
/// typically allows for read-only access (shared access).
///
/// The type parameter `T` represents the data that this lock protects. It is
/// required that `T` satisfies `Send` to be shared across tasks and `Sync` to
/// allow concurrent access through readers. The RAII guards returned from the
/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
/// to allow access to the contained of the lock.
///
/// Based on
/// https://jfdube.wordpress.com/2014/01/03/implementing-a-recursive-read-write-spinlock/
///
pub struct RwLock<T>
{
    lock: AtomicUsize,
    data: UnsafeCell<T>,
}

/// A guard to which the protected data can be read
///
/// When the guard falls out of scope it will decrement the read count,
/// potentially releasing the lock.
pub struct RwLockReadGuard<'a, T:'a>
{
    lock: &'a AtomicUsize,
    data: &'a T,
}

/// A guard to which the protected data can be written
///
/// When the guard falls out of scope it will release the lock.
pub struct RwLockWriteGuard<'a, T:'a>
{
    lock: &'a AtomicUsize,
    data: &'a mut T,
}

unsafe impl<T> Sync for RwLock<T> {}
unsafe impl<T:'static+Send> Send for RwLock<T> {}

/// A RwLock which may be used statically.
///
/// ```
/// use spin::{StaticRwLock, STATIC_RWLOCK_INIT};
///
/// static SPRWLCK: StaticRwLock = STATIC_RWLOCK_INIT;
///
/// fn demo() {
///     let lock = SPRWLCK.read();
///     // do something with lock
///     drop(lock);
/// }
/// ```
#[cfg(feature = "no_std")]
pub type StaticRwLock = RwLock<()>;

/// A initializer for StaticRwLock, containing no data.
#[cfg(feature = "no_std")]
pub const STATIC_RWLOCK_INIT: StaticRwLock = RwLock {
    lock: ATOMIC_USIZE_INIT,
    data: UnsafeCell { value: () },
};

#[cfg(feature = "no_std")]
const USIZE_MSB: usize = ::core::isize::MIN as usize;
#[cfg(not(feature = "no_std"))]
const USIZE_MSB: usize = ::std::isize::MIN as usize;

impl<T> RwLock<T>
{
    /// Creates a new spinlock wrapping the supplied data.
    #[inline]
    pub fn new(user_data: T) -> RwLock<T>
    {
        RwLock
        {
            lock: ATOMIC_USIZE_INIT,
            data: UnsafeCell::new(user_data),
        }
    }

    /// Locks this rwlock with shared read access, blocking the current thread
    /// until it can be acquired.
    ///
    /// The calling thread will be blocked until there are no more writers which
    /// hold the lock. There may be other readers currently inside the lock when
    /// this method returns. This method does not provide any guarantees with
    /// respect to the ordering of whether contentious readers or writers will
    /// acquire the lock first.
    ///
    /// Returns an RAII guard which will release this thread's shared access
    /// once it is dropped.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0us);
    /// {
    ///     let mut data = mylock.read();
    ///     // The lock is now locked and the data can be read
    ///     println!("{}", *data);
    ///     // The lock is dropped
    /// }
    /// ```
    #[inline]
    pub fn read<'a>(&'a self) -> RwLockReadGuard<'a, T>
    {
        // (funny do-while loop)
        while {
            // Old value, with write bit unset
            let mut old;

            // Wait for for writer to go away before doing expensive atomic ops
            // (funny do-while loop)
            while {
                old = self.lock.load(Ordering::Relaxed);
                old & USIZE_MSB != 0
            } {}

            // unset write bit
            old &= !USIZE_MSB;

            let new = old + 1;
            debug_assert!(new != (!USIZE_MSB) & (-1));

            self.lock.compare_and_swap(old, new, Ordering::SeqCst) != old
        } {}
        RwLockReadGuard {
            lock: &self.lock,
            data: unsafe { & *self.data.get() },
        }
    }

    /// Attempt to acquire this lock with shared read access.
    ///
    /// This function will never block and will return immediately if `read`
    /// would otherwise succeed. Returns `Some` of an RAII guard which will
    /// release the shared access of this thread when dropped, or `None` if the
    /// access could not be granted. This method does not provide any
    /// guarantees with respect to the ordering of whether contentious readers
    /// or writers will acquire the lock first.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0us);
    /// {
    ///     match mylock.try_read() {
    ///         Some(data) => {
    ///             // The lock is now locked and the data can be read
    ///             println!("{}", *data);
    ///             // The lock is dropped
    ///         },
    ///         None => (), // no cigar
    ///     };
    /// }
    /// ```
    #[inline]
    pub fn try_read(&self) -> Option<RwLockReadGuard<T>>
    {
        // Old value, with write bit unset
        let old = (!USIZE_MSB) & self.lock.load(Ordering::Relaxed);

        let new = old + 1;
        debug_assert!(new != (!USIZE_MSB) & (-1));
        if self.lock.compare_and_swap(old,
                                      new,
                                      Ordering::SeqCst) == old
        {
            Some(RwLockReadGuard {
                lock: &self.lock,
                data: unsafe { & *self.data.get() },
            })
        } else {
            None
        }
    }

    /// Lock this rwlock with exclusive write access, blocking the current
    /// thread until it can be acquired.
    ///
    /// This function will not return while other writers or other readers
    /// currently have access to the lock.
    ///
    /// Returns an RAII guard which will drop the write access of this rwlock
    /// when dropped.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0us);
    /// {
    ///     let mut data = mylock.write();
    ///     // The lock is now locked and the data can be written
    ///     *data += 1;
    ///     // The lock is dropped
    /// }
    /// ```
    #[inline]
    pub fn write<'a>(&'a self) -> RwLockWriteGuard<'a, T>
    {
        loop
        {
            // Old value, with write bit unset.
            let old = (!USIZE_MSB) & self.lock.load(Ordering::Relaxed);
            // Old value, with write bit set.
            let new = USIZE_MSB | old;
            if self.lock.compare_and_swap(old,
                                          new,
                                          Ordering::SeqCst) == old
            {
                // Wait for readers to go away, then lock is ours.
                while self.lock.load(Ordering::Relaxed) != USIZE_MSB { }
                break
            }
        }
        RwLockWriteGuard {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() },
        }
    }

    /// Attempt to lock this rwlock with exclusive write access.
    ///
    /// This function does not ever block, and it will return `None` if a call
    /// to `write` would otherwise block. If successful, an RAII guard is
    /// returned.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0us);
    /// {
    ///     match mylock.try_write() {
    ///         Some(mut data) => {
    ///             // The lock is now locked and the data can be written
    ///             *data += 1;
    ///             // The lock is implicitly dropped
    ///         },
    ///         None => (), // no cigar
    ///     };
    /// }
    /// ```
    #[inline]
    pub fn try_write(&self) -> Option<RwLockWriteGuard<T>>
    {
        if self.lock.compare_and_swap(0,
                                      USIZE_MSB,
                                      Ordering::SeqCst) == 0
        {
            Some(RwLockWriteGuard {
                lock: &self.lock,
                data: unsafe { &mut *self.data.get() },
            })
        } else {
            None
        }
    }
}

impl<'rwlock, T> Deref for RwLockReadGuard<'rwlock, T> {
    type Target = T;

    fn deref(&self) -> &T { self.data }
}

impl<'rwlock, T> Deref for RwLockWriteGuard<'rwlock, T> {
    type Target = T;

    fn deref(&self) -> &T { self.data }
}

impl<'rwlock, T> DerefMut for RwLockWriteGuard<'rwlock, T> {
    fn deref_mut(&mut self) -> &mut T { self.data }
}

impl<'rwlock, T> Drop for RwLockReadGuard<'rwlock, T> {
    fn drop(&mut self) {
        debug_assert!(self.lock.load(Ordering::Relaxed) & (!USIZE_MSB) > 0);
        self.lock.fetch_sub(1, Ordering::SeqCst);
    }
}

impl<'rwlock, T> Drop for RwLockWriteGuard<'rwlock, T> {
    fn drop(&mut self) {
        debug_assert_eq!(self.lock.load(Ordering::Relaxed), USIZE_MSB);
        self.lock.store(0, Ordering::Relaxed);
    }
}
<commit_msg>Remove us suffix from some constants<commit_after>#[cfg(not(feature = "no_std"))]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(not(feature = "no_std"))]
use std::cell::UnsafeCell;
#[cfg(not(feature = "no_std"))]
use std::ops::{Deref, DerefMut};

#[cfg(feature = "no_std")]
use core::prelude::*;

#[cfg(feature = "no_std")]
use core::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(feature = "no_std")]
use core::cell::UnsafeCell;
#[cfg(feature = "no_std")]
use core::ops::{Deref, DerefMut};

/// A reader-writer lock
///
/// This type of lock allows a number of readers or at most one writer at any
/// point in time. The write portion of this lock typically allows modification
/// of the underlying data (exclusive access) and the read portion of this lock
/// typically allows for read-only access (shared access).
///
/// The type parameter `T` represents the data that this lock protects. It is
/// required that `T` satisfies `Send` to be shared across tasks and `Sync` to
/// allow concurrent access through readers. The RAII guards returned from the
/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
/// to allow access to the contained of the lock.
///
/// Based on
/// https://jfdube.wordpress.com/2014/01/03/implementing-a-recursive-read-write-spinlock/
///
pub struct RwLock<T>
{
    lock: AtomicUsize,
    data: UnsafeCell<T>,
}

/// A guard to which the protected data can be read
///
/// When the guard falls out of scope it will decrement the read count,
/// potentially releasing the lock.
pub struct RwLockReadGuard<'a, T:'a>
{
    lock: &'a AtomicUsize,
    data: &'a T,
}

/// A guard to which the protected data can be written
///
/// When the guard falls out of scope it will release the lock.
pub struct RwLockWriteGuard<'a, T:'a>
{
    lock: &'a AtomicUsize,
    data: &'a mut T,
}

unsafe impl<T> Sync for RwLock<T> {}
unsafe impl<T:'static+Send> Send for RwLock<T> {}

/// A RwLock which may be used statically.
///
/// ```
/// use spin::{StaticRwLock, STATIC_RWLOCK_INIT};
///
/// static SPRWLCK: StaticRwLock = STATIC_RWLOCK_INIT;
///
/// fn demo() {
///     let lock = SPRWLCK.read();
///     // do something with lock
///     drop(lock);
/// }
/// ```
#[cfg(feature = "no_std")]
pub type StaticRwLock = RwLock<()>;

/// A initializer for StaticRwLock, containing no data.
#[cfg(feature = "no_std")]
pub const STATIC_RWLOCK_INIT: StaticRwLock = RwLock {
    lock: ATOMIC_USIZE_INIT,
    data: UnsafeCell { value: () },
};

#[cfg(feature = "no_std")]
const USIZE_MSB: usize = ::core::isize::MIN as usize;
#[cfg(not(feature = "no_std"))]
const USIZE_MSB: usize = ::std::isize::MIN as usize;

impl<T> RwLock<T>
{
    /// Creates a new spinlock wrapping the supplied data.
    #[inline]
    pub fn new(user_data: T) -> RwLock<T>
    {
        RwLock
        {
            lock: ATOMIC_USIZE_INIT,
            data: UnsafeCell::new(user_data),
        }
    }

    /// Locks this rwlock with shared read access, blocking the current thread
    /// until it can be acquired.
    ///
    /// The calling thread will be blocked until there are no more writers which
    /// hold the lock. There may be other readers currently inside the lock when
    /// this method returns. This method does not provide any guarantees with
    /// respect to the ordering of whether contentious readers or writers will
    /// acquire the lock first.
    ///
    /// Returns an RAII guard which will release this thread's shared access
    /// once it is dropped.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0);
    /// {
    ///     let mut data = mylock.read();
    ///     // The lock is now locked and the data can be read
    ///     println!("{}", *data);
    ///     // The lock is dropped
    /// }
    /// ```
    #[inline]
    pub fn read<'a>(&'a self) -> RwLockReadGuard<'a, T>
    {
        // (funny do-while loop)
        while {
            // Old value, with write bit unset
            let mut old;

            // Wait for for writer to go away before doing expensive atomic ops
            // (funny do-while loop)
            while {
                old = self.lock.load(Ordering::Relaxed);
                old & USIZE_MSB != 0
            } {}

            // unset write bit
            old &= !USIZE_MSB;

            let new = old + 1;
            debug_assert!(new != (!USIZE_MSB) & (-1));

            self.lock.compare_and_swap(old, new, Ordering::SeqCst) != old
        } {}
        RwLockReadGuard {
            lock: &self.lock,
            data: unsafe { & *self.data.get() },
        }
    }

    /// Attempt to acquire this lock with shared read access.
    ///
    /// This function will never block and will return immediately if `read`
    /// would otherwise succeed. Returns `Some` of an RAII guard which will
    /// release the shared access of this thread when dropped, or `None` if the
    /// access could not be granted. This method does not provide any
    /// guarantees with respect to the ordering of whether contentious readers
    /// or writers will acquire the lock first.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0);
    /// {
    ///     match mylock.try_read() {
    ///         Some(data) => {
    ///             // The lock is now locked and the data can be read
    ///             println!("{}", *data);
    ///             // The lock is dropped
    ///         },
    ///         None => (), // no cigar
    ///     };
    /// }
    /// ```
    #[inline]
    pub fn try_read(&self) -> Option<RwLockReadGuard<T>>
    {
        // Old value, with write bit unset
        let old = (!USIZE_MSB) & self.lock.load(Ordering::Relaxed);

        let new = old + 1;
        debug_assert!(new != (!USIZE_MSB) & (-1));
        if self.lock.compare_and_swap(old,
                                      new,
                                      Ordering::SeqCst) == old
        {
            Some(RwLockReadGuard {
                lock: &self.lock,
                data: unsafe { & *self.data.get() },
            })
        } else {
            None
        }
    }

    /// Lock this rwlock with exclusive write access, blocking the current
    /// thread until it can be acquired.
    ///
    /// This function will not return while other writers or other readers
    /// currently have access to the lock.
    ///
    /// Returns an RAII guard which will drop the write access of this rwlock
    /// when dropped.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0);
    /// {
    ///     let mut data = mylock.write();
    ///     // The lock is now locked and the data can be written
    ///     *data += 1;
    ///     // The lock is dropped
    /// }
    /// ```
    #[inline]
    pub fn write<'a>(&'a self) -> RwLockWriteGuard<'a, T>
    {
        loop
        {
            // Old value, with write bit unset.
            let old = (!USIZE_MSB) & self.lock.load(Ordering::Relaxed);
            // Old value, with write bit set.
            let new = USIZE_MSB | old;
            if self.lock.compare_and_swap(old,
                                          new,
                                          Ordering::SeqCst) == old
            {
                // Wait for readers to go away, then lock is ours.
                while self.lock.load(Ordering::Relaxed) != USIZE_MSB { }
                break
            }
        }
        RwLockWriteGuard {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() },
        }
    }

    /// Attempt to lock this rwlock with exclusive write access.
    ///
    /// This function does not ever block, and it will return `None` if a call
    /// to `write` would otherwise block. If successful, an RAII guard is
    /// returned.
    ///
    /// ```
    /// let mylock = spin::RwLock::new(0);
    /// {
    ///     match mylock.try_write() {
    ///         Some(mut data) => {
    ///             // The lock is now locked and the data can be written
    ///             *data += 1;
    ///             // The lock is implicitly dropped
    ///         },
    ///         None => (), // no cigar
    ///     };
    /// }
    /// ```
    #[inline]
    pub fn try_write(&self) -> Option<RwLockWriteGuard<T>>
    {
        if self.lock.compare_and_swap(0,
                                      USIZE_MSB,
                                      Ordering::SeqCst) == 0
        {
            Some(RwLockWriteGuard {
                lock: &self.lock,
                data: unsafe { &mut *self.data.get() },
            })
        } else {
            None
        }
    }
}

impl<'rwlock, T> Deref for RwLockReadGuard<'rwlock, T> {
    type Target = T;

    fn deref(&self) -> &T { self.data }
}

impl<'rwlock, T> Deref for RwLockWriteGuard<'rwlock, T> {
    type Target = T;

    fn deref(&self) -> &T { self.data }
}

impl<'rwlock, T> DerefMut for RwLockWriteGuard<'rwlock, T> {
    fn deref_mut(&mut self) -> &mut T { self.data }
}

impl<'rwlock, T> Drop for RwLockReadGuard<'rwlock, T> {
    fn drop(&mut self) {
        debug_assert!(self.lock.load(Ordering::Relaxed) & (!USIZE_MSB) > 0);
        self.lock.fetch_sub(1, Ordering::SeqCst);
    }
}

impl<'rwlock, T> Drop for RwLockWriteGuard<'rwlock, T> {
    fn drop(&mut self) {
        debug_assert_eq!(self.lock.load(Ordering::Relaxed), USIZE_MSB);
        self.lock.store(0, Ordering::Relaxed);
    }
}
<|endoftext|>