<commit_before>use std::f64;
use std::i64;
use rand::Rng;
use error::StatsError;
use function::{factorial, gamma};
use result::Result;
use super::{Distribution, Univariate, Discrete};

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Poisson {
    lambda: f64,
}

impl Poisson {
    pub fn new(lambda: f64) -> Result<Poisson> {
        if lambda.is_nan() || lambda < 0.0 {
            Err(StatsError::BadParams)
        } else {
            Ok(Poisson { lambda: lambda })
        }
    }

    pub fn lambda(&self) -> f64 {
        self.lambda
    }
}

impl Distribution for Poisson {
    fn sample<R: Rng>(&self, r: &mut R) -> f64 {
        sample_unchecked(r, self.lambda)
    }
}

impl Univariate for Poisson {
    fn mean(&self) -> f64 {
        self.lambda
    }

    fn variance(&self) -> f64 {
        self.lambda
    }

    fn std_dev(&self) -> f64 {
        self.variance().sqrt()
    }

    fn entropy(&self) -> f64 {
        0.5 * (2.0 * f64::consts::PI * f64::consts::E * self.lambda).ln() -
        1.0 / (12.0 * self.lambda) - 1.0 / (24.0 * self.lambda * self.lambda) -
        19.0 / (360.0 * self.lambda * self.lambda * self.lambda)
    }

    fn skewness(&self) -> f64 {
        1.0 / self.lambda.sqrt()
    }

    fn median(&self) -> f64 {
        (self.lambda + 1.0 / 3.0 - 0.02 / self.lambda).floor()
    }

    fn cdf(&self, x: f64) -> f64 {
        1.0 - gamma::gamma_lr(x + 1.0, self.lambda).unwrap()
    }
}

impl Discrete for Poisson {
    fn mode(&self) -> i64 {
        self.lambda.floor() as i64
    }

    fn min(&self) -> i64 {
        0
    }

    fn max(&self) -> i64 {
        i64::MAX
    }

    fn pmf(&self, x: i64) -> f64 {
        if x < 0 {
            panic!("{}", StatsError::ArgNotNegative("x"));
        }
        (-self.lambda + x as f64 * self.lambda.ln() - factorial::ln_factorial(x as u64)).exp()
    }

    fn ln_pmf(&self, x: i64) -> f64 {
        if x < 0 {
            panic!("{}", StatsError::ArgNotNegative("x"));
        }
        -self.lambda + x as f64 * self.lambda.ln() - factorial::ln_factorial(x as u64)
    }
}

/// Generates one sample from the Poisson distribution either by
/// Knuth's method if lambda < 30.0 or Rejection method PA by
/// A. C. Atkinson from the Journal of the Royal Statistical Society
/// Series C (Applied Statistics) Vol. 28 No. 1. (1979) pp. 29 - 35
/// otherwise
fn sample_unchecked<R: Rng>(r: &mut R, lambda: f64) -> f64 {
    if lambda < 30.0 {
        let limit = (-lambda).exp();
        let mut count = 0.0;
        let mut product = r.next_f64();
        while product >= limit {
            count += 1.0;
            product *= r.next_f64();
        }
        count
    } else {
        let c = 0.767 - 3.36 / lambda;
        let beta = f64::consts::PI / (3.0 * lambda).sqrt();
        let alpha = beta * lambda;
        let k = c.ln() - lambda - beta.ln();

        loop {
            let u = r.next_f64();
            let x = (alpha - ((1.0 - u) / u).ln()) / beta;
            let n = (x + 0.5).floor();
            if n < 0.0 {
                continue;
            }

            let v = r.next_f64();
            let y = alpha - beta * x;
            let temp = 1.0 + y.exp();
            let lhs = y + (v / (temp * temp)).ln();
            let rhs = k + n * lambda.ln() - factorial::ln_factorial(n as u64);
            if lhs <= rhs {
                return n;
            }
        }
    }
}
<commit_msg>begin tests for poisson distribution<commit_after>use std::f64;
use std::i64;
use rand::Rng;
use error::StatsError;
use function::{factorial, gamma};
use result::Result;
use super::{Distribution, Univariate, Discrete};

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Poisson {
    lambda: f64,
}

impl Poisson {
    pub fn new(lambda: f64) -> Result<Poisson> {
        if lambda.is_nan() || lambda <= 0.0 {
            Err(StatsError::BadParams)
        } else {
            Ok(Poisson { lambda: lambda })
        }
    }

    pub fn lambda(&self) -> f64 {
        self.lambda
    }
}

impl Distribution for Poisson {
    fn sample<R: Rng>(&self, r: &mut R) -> f64 {
        sample_unchecked(r, self.lambda)
    }
}

impl Univariate for Poisson {
    fn mean(&self) -> f64 {
        self.lambda
    }

    fn variance(&self) -> f64 {
        self.lambda
    }

    fn std_dev(&self) -> f64 {
        self.variance().sqrt()
    }

    fn entropy(&self) -> f64 {
        0.5 * (2.0 * f64::consts::PI * f64::consts::E * self.lambda).ln() -
        1.0 / (12.0 * self.lambda) - 1.0 / (24.0 * self.lambda * self.lambda) -
        19.0 / (360.0 * self.lambda * self.lambda * self.lambda)
    }

    fn skewness(&self) -> f64 {
        1.0 / self.lambda.sqrt()
    }

    fn median(&self) -> f64 {
        (self.lambda + 1.0 / 3.0 - 0.02 / self.lambda).floor()
    }

    fn cdf(&self, x: f64) -> f64 {
        1.0 - gamma::gamma_lr(x + 1.0, self.lambda).unwrap()
    }
}

impl Discrete for Poisson {
    fn mode(&self) -> i64 {
        self.lambda.floor() as i64
    }

    fn min(&self) -> i64 {
        0
    }

    fn max(&self) -> i64 {
        i64::MAX
    }

    fn pmf(&self, x: i64) -> f64 {
        if x < 0 {
            panic!("{}", StatsError::ArgNotNegative("x"));
        }
        (-self.lambda + x as f64 * self.lambda.ln() - factorial::ln_factorial(x as u64)).exp()
    }

    fn ln_pmf(&self, x: i64) -> f64 {
        if x < 0 {
            panic!("{}", StatsError::ArgNotNegative("x"));
        }
        -self.lambda + x as f64 * self.lambda.ln() - factorial::ln_factorial(x as u64)
    }
}

/// Generates one sample from the Poisson distribution either by
/// Knuth's method if lambda < 30.0 or Rejection method PA by
/// A. C. Atkinson from the Journal of the Royal Statistical Society
/// Series C (Applied Statistics) Vol. 28 No. 1. (1979) pp. 29 - 35
/// otherwise
fn sample_unchecked<R: Rng>(r: &mut R, lambda: f64) -> f64 {
    if lambda < 30.0 {
        let limit = (-lambda).exp();
        let mut count = 0.0;
        let mut product = r.next_f64();
        while product >= limit {
            count += 1.0;
            product *= r.next_f64();
        }
        count
    } else {
        let c = 0.767 - 3.36 / lambda;
        let beta = f64::consts::PI / (3.0 * lambda).sqrt();
        let alpha = beta * lambda;
        let k = c.ln() - lambda - beta.ln();

        loop {
            let u = r.next_f64();
            let x = (alpha - ((1.0 - u) / u).ln()) / beta;
            let n = (x + 0.5).floor();
            if n < 0.0 {
                continue;
            }

            let v = r.next_f64();
            let y = alpha - beta * x;
            let temp = 1.0 + y.exp();
            let lhs = y + (v / (temp * temp)).ln();
            let rhs = k + n * lambda.ln() - factorial::ln_factorial(n as u64);
            if lhs <= rhs {
                return n;
            }
        }
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
#[cfg(test)]
mod test {
    use std::cmp::PartialEq;
    use std::fmt::Debug;
    use std::f64;
    use distribution::{Univariate, Discrete};
    use prec;
    use super::Poisson;

    fn try_create(lambda: f64) -> Poisson {
        let n = Poisson::new(lambda);
        assert!(n.is_ok());
        n.unwrap()
    }

    fn create_case(lambda: f64) {
        let n = try_create(lambda);
        assert_eq!(lambda, n.lambda());
    }

    fn bad_create_case(lambda: f64) {
        let n = Poisson::new(lambda);
        assert!(n.is_err());
    }

    fn test_case<T, F>(lambda: f64, expected: T, eval: F)
        where T: PartialEq + Debug,
              F: Fn(Poisson) -> T
    {

        let n = try_create(lambda);
        let x = eval(n);
        assert_eq!(expected, x);
    }

    fn test_almost<F>(lambda: f64, expected: f64, acc: f64, eval: F)
        where F: Fn(Poisson) -> f64
    {

        let n = try_create(lambda);
        let x = eval(n);
        assert!(prec::almost_eq(expected, x, acc));
    }

    #[test]
    fn test_create() {
        create_case(1.5);
        create_case(5.4);
        create_case(10.8);
    }

    #[test]
    fn test_bad_create() {
        bad_create_case(f64::NAN);
        bad_create_case(-1.5);
        bad_create_case(0.0);
    }

    #[test]
    fn test_mean() {
        test_case(1.5, 1.5, |x| x.mean());
        test_case(5.4, 5.4, |x| x.mean());
        test_case(10.8, 10.8, |x| x.mean());
    }

    #[test]
    fn test_variance() {
        test_case(1.5, 1.5, |x| x.variance());
        test_case(5.4, 5.4, |x| x.variance());
        test_case(10.8, 10.8, |x| x.variance());
    }

    #[test]
    fn test_std_dev() {
        test_case(1.5, (1.5f64).sqrt(), |x| x.std_dev());
        test_case(5.4, (5.4f64).sqrt(), |x| x.std_dev());
        test_case(10.8, (10.8f64).sqrt(), |x| x.std_dev());
    }
}
<|endoftext|>


<commit_before>/// Maximum order of the fixed predictors permitted by the format.
pub const MAX_FIXED_ORDER: usize = 4;

/// Maximum LPC order permitted by the format.
pub const MAX_LPC_ORDER: usize   = 32;

/// A single channel of audio data.
pub struct SubFrame {
  /// Data containing one of the four different types of subframes.
  pub data: Data,
  /// Number of wasted bits within each sample.
  pub wasted_bits: u32,
}

/// General enum that holds all the different subframe data types.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// A single value that represents a constant subframe.
  Constant(i32),
  /// An uncompressed suframe.
  Verbatim(Vec<i32>),
  /// Fixed linear prediction subframe.
  Fixed(Fixed),
  /// FIR linear prediction subframe.
  LPC(LPC),
}

/// Fixed linear prediction subframe.
#[derive(Debug, PartialEq, Eq)]
pub struct Fixed {
  pub entropy_coding_method: EntropyCodingMethod,
  pub order: u8,
  pub warmup: [i32; MAX_FIXED_ORDER],
  pub residual: Vec<i32>,
}

/// Finite Impulse Response (FIR) linear prediction subframe.
#[derive(Debug, PartialEq, Eq)]
pub struct LPC {
  pub entropy_coding_method: EntropyCodingMethod,
  pub order: u8,
  pub qlp_coeff_precision: u8,
  pub quantization_level: i8,
  pub qlp_coefficients: [i32; MAX_LPC_ORDER],
  pub warmup: [i32; MAX_LPC_ORDER],
  pub residual: Vec<i32>,
}

/// Header for the entropy coding method.
#[derive(Debug, PartialEq, Eq)]
pub struct EntropyCodingMethod {
  pub method_type: CodingMethod,
  pub data: PartitionedRice,
}

/// The available entropy coding methods.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CodingMethod {
  PartitionedRice,
  PartitionedRice2,
}

/// Header for a Rice partitioned residual.
#[derive(Debug, PartialEq, Eq)]
pub struct PartitionedRice {
  pub order: u32,
  pub contents: PartitionedRiceContents,
}

/// Contents of a Rice partitioned residual.
#[derive(Debug, PartialEq, Eq)]
pub struct PartitionedRiceContents {
  pub parameters: Vec<u32>,
  pub raw_bits: Vec<u32>,
  pub capacity_by_order: u32,
}

impl PartitionedRiceContents {
  pub fn new(capacity_by_order: u32) -> PartitionedRiceContents {
    let capacity       = 2_usize.pow(capacity_by_order);
    let mut parameters = Vec::with_capacity(capacity);
    let mut raw_bits   = Vec::with_capacity(capacity);

    unsafe {
      parameters.set_len(capacity);
      raw_bits.set_len(capacity);
    }

    PartitionedRiceContents {
      parameters: parameters,
      raw_bits: raw_bits,
      capacity_by_order: capacity_by_order,
    }
  }
}
<commit_msg>Add member documentation for `Fixed` struct<commit_after>/// Maximum order of the fixed predictors permitted by the format.
pub const MAX_FIXED_ORDER: usize = 4;

/// Maximum LPC order permitted by the format.
pub const MAX_LPC_ORDER: usize   = 32;

/// A single channel of audio data.
pub struct SubFrame {
  /// Data containing one of the four different types of subframes.
  pub data: Data,
  /// Number of wasted bits within each sample.
  pub wasted_bits: u32,
}

/// General enum that holds all the different subframe data types.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// A single value that represents a constant subframe.
  Constant(i32),
  /// An uncompressed suframe.
  Verbatim(Vec<i32>),
  /// Fixed linear prediction subframe.
  Fixed(Fixed),
  /// FIR linear prediction subframe.
  LPC(LPC),
}

/// Fixed linear prediction subframe.
#[derive(Debug, PartialEq, Eq)]
pub struct Fixed {
  /// Residual coding method.
  pub entropy_coding_method: EntropyCodingMethod,
  /// Polynomial order.
  pub order: u8,
  /// Samples used to warm up, or prime, the predictor.
  pub warmup: [i32; MAX_FIXED_ORDER],
  /// Remaining samples after the warm up samples.
  pub residual: Vec<i32>,
}

/// Finite Impulse Response (FIR) linear prediction subframe.
#[derive(Debug, PartialEq, Eq)]
pub struct LPC {
  pub entropy_coding_method: EntropyCodingMethod,
  pub order: u8,
  pub qlp_coeff_precision: u8,
  pub quantization_level: i8,
  pub qlp_coefficients: [i32; MAX_LPC_ORDER],
  pub warmup: [i32; MAX_LPC_ORDER],
  pub residual: Vec<i32>,
}

/// Header for the entropy coding method.
#[derive(Debug, PartialEq, Eq)]
pub struct EntropyCodingMethod {
  pub method_type: CodingMethod,
  pub data: PartitionedRice,
}

/// The available entropy coding methods.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CodingMethod {
  PartitionedRice,
  PartitionedRice2,
}

/// Header for a Rice partitioned residual.
#[derive(Debug, PartialEq, Eq)]
pub struct PartitionedRice {
  pub order: u32,
  pub contents: PartitionedRiceContents,
}

/// Contents of a Rice partitioned residual.
#[derive(Debug, PartialEq, Eq)]
pub struct PartitionedRiceContents {
  pub parameters: Vec<u32>,
  pub raw_bits: Vec<u32>,
  pub capacity_by_order: u32,
}

impl PartitionedRiceContents {
  pub fn new(capacity_by_order: u32) -> PartitionedRiceContents {
    let capacity       = 2_usize.pow(capacity_by_order);
    let mut parameters = Vec::with_capacity(capacity);
    let mut raw_bits   = Vec::with_capacity(capacity);

    unsafe {
      parameters.set_len(capacity);
      raw_bits.set_len(capacity);
    }

    PartitionedRiceContents {
      parameters: parameters,
      raw_bits: raw_bits,
      capacity_by_order: capacity_by_order,
    }
  }
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;
use std::cmp::{max, min};
use std::f32;
use std::fmt;
use std::sync::Arc;

use driver::cmd::Args;
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::CollectionKind;
use gc::{align_gen, align_gen_down, formatted_size, AllNumbers, GcReason, GEN_SIZE, M};
use mem;
use os::signal::Trap;
use stdlib;
use timer;

const MAX_YOUNG_SIZE: usize = 20 * M;

const INIT_HEAP_SIZE_RATIO: usize = 2;
const INIT_YOUNG_RATIO: usize = 4;
const INIT_SEMI_RATIO: usize = 3;

pub fn init(config: &mut HeapConfig, args: &Args) {
    assert!(config.min_heap_size <= config.max_heap_size);

    let young_size = if let Some(young_size) = args.young_size() {
        min(align_gen(young_size), config.max_heap_size - GEN_SIZE)
    } else if args.young_appel() {
        let max_heap_size = (config.max_heap_size as f64 * 0.9) as usize;
        align_gen(max_heap_size / 2)
    } else {
        unreachable!();
    };

    let young_size = max(young_size, GEN_SIZE);
    let (eden_size, semi_size) = calculate_young_size(args, young_size, 0);

    config.eden_size = eden_size;
    config.semi_size = semi_size;

    let max_old_limit = config.max_heap_size - young_size;
    let min_old_limit = if config.min_heap_size > young_size {
        config.min_heap_size - young_size
    } else {
        0
    };

    let old_limit = align_gen(config.max_heap_size / INIT_HEAP_SIZE_RATIO);
    let old_limit = min(old_limit, max_old_limit);
    let old_limit = max(old_limit, min_old_limit);

    config.old_size = 0;
    config.old_limit = old_limit;
}

fn calculate_young_size(args: &Args, young_size: usize, min_semi_size: usize) -> (usize, usize) {
    let semi_ratio = args.flag_gc_semi_ratio.unwrap_or(INIT_SEMI_RATIO);
    let semi_size = if semi_ratio == 0 {
        0
    } else {
        align_gen(young_size / semi_ratio)
    };

    let semi_size = max(semi_size, min_semi_size);
    let semi_size = max(semi_size, GEN_SIZE);
    let eden_size = young_size - semi_size;

    (eden_size, semi_size)
}

pub fn choose_collection_kind(
    _config: &SharedHeapConfig,
    _args: &Args,
    young: &YoungGen,
) -> CollectionKind {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    return if young_size <= M {
        CollectionKind::Full
    } else {
        CollectionKind::Minor
    };
}

pub fn start(config: &SharedHeapConfig, young: &YoungGen, old: &OldGen, large: &LargeSpace) {
    let mut config = config.lock();

    config.gc_start = timer::timestamp();
    config.start_object_size = object_size(young, old, large);
    config.start_memory_size = memory_size(young, old, large);
}

pub fn stop(
    config: &SharedHeapConfig,
    kind: CollectionKind,
    young: &YoungGen,
    old: &OldGen,
    large: &LargeSpace,
    args: &Args,
    reason: GcReason,
) {
    let mut config = config.lock();

    let gc_end = timer::timestamp();
    config.gc_duration = timer::in_ms(gc_end - config.gc_start);

    assert!(young.eden_active().empty());
    assert!(young.from_active().empty());

    let old_size = old.committed_size() + large.committed_size();
    config.old_size = old_size;

    let max_young_size = if let Some(young_size) = args.young_size() {
        align_gen(young_size)
    } else {
        std::usize::MAX
    };

    let rest = config.max_heap_size - config.old_size;
    let target_young_size = align_gen_down(rest / 2);
    let target_young_size = min(target_young_size, max_young_size);
    let target_young_size = max(target_young_size, GEN_SIZE);

    let to_size = young.to_active().size();
    let min_semi_size = align_gen(mem::page_align(to_size) * 2);

    let (eden_size, semi_size) = calculate_young_size(args, target_young_size, min_semi_size);
    let young_size = eden_size + semi_size;

    if old_size + young_size > config.max_heap_size {
        stdlib::trap(Trap::OOM.int());
    }

    young.set_limit(eden_size, semi_size);
    config.old_limit = config.max_heap_size - young_size;
    assert!(config.old_limit >= old_size);

    config.end_object_size = object_size(young, old, large);
    config.end_memory_size = memory_size(young, old, large);

    assert!(young_size + config.old_limit <= config.max_heap_size);

    match kind {
        CollectionKind::Minor => {
            config.total_minor_collections += 1;
            config.total_minor_pause += config.gc_duration;

            if args.flag_gc_stats {
                config.minor_phases.last_mut().unwrap().total = config.gc_duration;
            }
        }

        CollectionKind::Full => {
            config.total_full_collections += 1;
            config.total_full_pause += config.gc_duration;

            if args.flag_gc_stats {
                config.full_phases.last_mut().unwrap().total = config.gc_duration;
            }
        }
    }

    if args.flag_gc_verbose {
        print(&*config, kind, reason);
    }
}

fn print(config: &HeapConfig, kind: CollectionKind, reason: GcReason) {
    match kind {
        CollectionKind::Minor => {
            println!(
                "GC: {} ({}) {}/{} -> {}/{}; {:.2} ms; {} promoted; {} copied",
                kind,
                reason,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
                formatted_size(config.minor_promoted),
                formatted_size(config.minor_copied),
            );
        }

        CollectionKind::Full => {
            println!(
                "GC: {} ({}) {}/{} -> {}/{}; {:.2} ms",
                kind,
                reason,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
            );
        }
    }
}

fn object_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    young.active_size() + old.active_size() + large.committed_size()
}

fn memory_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    young_size + old.committed_size() + large.committed_size()
}

pub struct HeapConfig {
    min_heap_size: usize,
    max_heap_size: usize,

    pub eden_size: usize,
    pub semi_size: usize,
    pub old_size: usize,
    pub old_limit: usize,

    gc_start: u64,
    gc_duration: f32,

    start_object_size: usize,
    start_memory_size: usize,
    end_object_size: usize,
    end_memory_size: usize,

    pub minor_promoted: usize,
    pub minor_copied: usize,

    pub total_minor_collections: usize,
    pub total_minor_pause: f32,
    pub total_full_collections: usize,
    pub total_full_pause: f32,

    full_phases: Vec<FullCollectorPhases>,
    minor_phases: Vec<MinorCollectorPhases>,
}

impl HeapConfig {
    pub fn new(min_heap_size: usize, max_heap_size: usize) -> HeapConfig {
        assert!(min_heap_size <= max_heap_size);

        HeapConfig {
            min_heap_size: min_heap_size,
            max_heap_size: max_heap_size,

            eden_size: 0,
            semi_size: 0,
            old_size: 0,
            old_limit: 0,

            gc_start: 0,
            gc_duration: 0f32,

            start_object_size: 0,
            start_memory_size: 0,
            end_object_size: 0,
            end_memory_size: 0,

            minor_promoted: 0,
            minor_copied: 0,

            total_minor_collections: 0,
            total_minor_pause: 0f32,
            total_full_collections: 0,
            total_full_pause: 0f32,

            full_phases: Vec::new(),
            minor_phases: Vec::new(),
        }
    }

    pub fn add_full(&mut self, phases: FullCollectorPhases) {
        self.full_phases.push(phases);
    }

    pub fn add_minor(&mut self, phases: MinorCollectorPhases) {
        self.minor_phases.push(phases);
    }

    pub fn full_marking(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.marking).collect();
        calculate_numbers(&values)
    }

    pub fn full_marking_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.marking).collect())
    }

    pub fn full_compute_forward(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.compute_forward).collect();
        calculate_numbers(&values)
    }

    pub fn full_compute_forward_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.compute_forward).collect())
    }

    pub fn full_update_refs(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.update_refs).collect();
        calculate_numbers(&values)
    }

    pub fn full_update_refs_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.update_refs).collect())
    }

    pub fn full_relocate(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.relocate).collect();
        calculate_numbers(&values)
    }

    pub fn full_relocate_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.relocate).collect())
    }

    pub fn full_reset_cards(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.reset_cards).collect();
        calculate_numbers(&values)
    }

    pub fn full_reset_cards_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.reset_cards).collect())
    }

    pub fn full_total(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.total).collect();
        calculate_numbers(&values)
    }

    pub fn full_total_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.total).collect())
    }

    pub fn minor_roots(&self) -> Numbers {
        let values: Vec<_> = self.minor_phases.iter().map(|x| x.roots).collect();
        calculate_numbers(&values)
    }

    pub fn minor_roots_all(&self) -> AllNumbers {
        AllNumbers(self.minor_phases.iter().map(|x| x.roots).collect())
    }

    pub fn minor_tracing(&self) -> Numbers {
        let values: Vec<_> = self.minor_phases.iter().map(|x| x.tracing).collect();
        calculate_numbers(&values)
    }

    pub fn minor_tracing_all(&self) -> AllNumbers {
        AllNumbers(self.minor_phases.iter().map(|x| x.tracing).collect())
    }

    pub fn minor_total(&self) -> Numbers {
        let values: Vec<_> = self.minor_phases.iter().map(|x| x.total).collect();
        calculate_numbers(&values)
    }

    pub fn minor_total_all(&self) -> AllNumbers {
        AllNumbers(self.minor_phases.iter().map(|x| x.total).collect())
    }

    pub fn grow_old(&mut self, size: usize) -> bool {
        if self.old_size + size <= self.old_limit {
            self.old_size += size;
            true
        } else {
            false
        }
    }
}

pub struct Numbers {
    pub avg: f32,
    pub sum: f32,
    pub min: f32,
    pub max: f32,
}

impl Numbers {
    fn zero() -> Numbers {
        Numbers {
            avg: 0f32,
            sum: 0f32,
            min: 0f32,
            max: 0f32,
        }
    }
}

impl fmt::Display for Numbers {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "avg={:<8.1} sum={:<8.1} min={:<8.1} max={:<8.1}",
            self.avg, self.sum, self.min, self.max
        )
    }
}

fn calculate_numbers(data: &[f32]) -> Numbers {
    if data.len() == 0 {
        return Numbers::zero();
    }

    let mut sum = 0f32;
    let mut xmin = data[0];
    let mut xmax = data[0];

    for el in data {
        sum += *el;
        xmin = f32::min(xmin, *el);
        xmax = f32::max(xmax, *el);
    }

    let avg = sum / (data.len() as f32);

    Numbers {
        avg: avg,
        sum: sum,
        min: xmin,
        max: xmax,
    }
}

pub type SharedHeapConfig = Arc<Mutex<HeapConfig>>;

#[derive(Clone)]
pub struct FullCollectorPhases {
    pub marking: f32,
    pub compute_forward: f32,
    pub update_refs: f32,
    pub relocate: f32,
    pub reset_cards: f32,
    pub total: f32,
}

impl FullCollectorPhases {
    pub fn new() -> FullCollectorPhases {
        FullCollectorPhases {
            marking: 0f32,
            compute_forward: 0f32,
            update_refs: 0f32,
            relocate: 0f32,
            reset_cards: 0f32,
            total: 0f32,
        }
    }
}

#[derive(Clone)]
pub struct MinorCollectorPhases {
    pub roots: f32,
    pub tracing: f32,
    pub total: f32,
}

impl MinorCollectorPhases {
    pub fn new() -> MinorCollectorPhases {
        MinorCollectorPhases {
            roots: 0f32,
            tracing: 0f32,
            total: 0f32,
        }
    }
}
<commit_msg>swiper: remove unused constant<commit_after>use parking_lot::Mutex;
use std::cmp::{max, min};
use std::f32;
use std::fmt;
use std::sync::Arc;

use driver::cmd::Args;
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::CollectionKind;
use gc::{align_gen, align_gen_down, formatted_size, AllNumbers, GcReason, GEN_SIZE, M};
use mem;
use os::signal::Trap;
use stdlib;
use timer;

const INIT_HEAP_SIZE_RATIO: usize = 2;
const INIT_YOUNG_RATIO: usize = 4;
const INIT_SEMI_RATIO: usize = 3;

pub fn init(config: &mut HeapConfig, args: &Args) {
    assert!(config.min_heap_size <= config.max_heap_size);

    let young_size = if let Some(young_size) = args.young_size() {
        min(align_gen(young_size), config.max_heap_size - GEN_SIZE)
    } else if args.young_appel() {
        let max_heap_size = (config.max_heap_size as f64 * 0.9) as usize;
        align_gen(max_heap_size / 2)
    } else {
        unreachable!();
    };

    let young_size = max(young_size, GEN_SIZE);
    let (eden_size, semi_size) = calculate_young_size(args, young_size, 0);

    config.eden_size = eden_size;
    config.semi_size = semi_size;

    let max_old_limit = config.max_heap_size - young_size;
    let min_old_limit = if config.min_heap_size > young_size {
        config.min_heap_size - young_size
    } else {
        0
    };

    let old_limit = align_gen(config.max_heap_size / INIT_HEAP_SIZE_RATIO);
    let old_limit = min(old_limit, max_old_limit);
    let old_limit = max(old_limit, min_old_limit);

    config.old_size = 0;
    config.old_limit = old_limit;
}

fn calculate_young_size(args: &Args, young_size: usize, min_semi_size: usize) -> (usize, usize) {
    let semi_ratio = args.flag_gc_semi_ratio.unwrap_or(INIT_SEMI_RATIO);
    let semi_size = if semi_ratio == 0 {
        0
    } else {
        align_gen(young_size / semi_ratio)
    };

    let semi_size = max(semi_size, min_semi_size);
    let semi_size = max(semi_size, GEN_SIZE);
    let eden_size = young_size - semi_size;

    (eden_size, semi_size)
}

pub fn choose_collection_kind(
    _config: &SharedHeapConfig,
    _args: &Args,
    young: &YoungGen,
) -> CollectionKind {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    return if young_size <= M {
        CollectionKind::Full
    } else {
        CollectionKind::Minor
    };
}

pub fn start(config: &SharedHeapConfig, young: &YoungGen, old: &OldGen, large: &LargeSpace) {
    let mut config = config.lock();

    config.gc_start = timer::timestamp();
    config.start_object_size = object_size(young, old, large);
    config.start_memory_size = memory_size(young, old, large);
}

pub fn stop(
    config: &SharedHeapConfig,
    kind: CollectionKind,
    young: &YoungGen,
    old: &OldGen,
    large: &LargeSpace,
    args: &Args,
    reason: GcReason,
) {
    let mut config = config.lock();

    let gc_end = timer::timestamp();
    config.gc_duration = timer::in_ms(gc_end - config.gc_start);

    assert!(young.eden_active().empty());
    assert!(young.from_active().empty());

    let old_size = old.committed_size() + large.committed_size();
    config.old_size = old_size;

    let max_young_size = if let Some(young_size) = args.young_size() {
        align_gen(young_size)
    } else {
        std::usize::MAX
    };

    let rest = config.max_heap_size - config.old_size;
    let target_young_size = align_gen_down(rest / 2);
    let target_young_size = min(target_young_size, max_young_size);
    let target_young_size = max(target_young_size, GEN_SIZE);

    let to_size = young.to_active().size();
    let min_semi_size = align_gen(mem::page_align(to_size) * 2);

    let (eden_size, semi_size) = calculate_young_size(args, target_young_size, min_semi_size);
    let young_size = eden_size + semi_size;

    if old_size + young_size > config.max_heap_size {
        stdlib::trap(Trap::OOM.int());
    }

    young.set_limit(eden_size, semi_size);
    config.old_limit = config.max_heap_size - young_size;
    assert!(config.old_limit >= old_size);

    config.end_object_size = object_size(young, old, large);
    config.end_memory_size = memory_size(young, old, large);

    assert!(young_size + config.old_limit <= config.max_heap_size);

    match kind {
        CollectionKind::Minor => {
            config.total_minor_collections += 1;
            config.total_minor_pause += config.gc_duration;

            if args.flag_gc_stats {
                config.minor_phases.last_mut().unwrap().total = config.gc_duration;
            }
        }

        CollectionKind::Full => {
            config.total_full_collections += 1;
            config.total_full_pause += config.gc_duration;

            if args.flag_gc_stats {
                config.full_phases.last_mut().unwrap().total = config.gc_duration;
            }
        }
    }

    if args.flag_gc_verbose {
        print(&*config, kind, reason);
    }
}

fn print(config: &HeapConfig, kind: CollectionKind, reason: GcReason) {
    match kind {
        CollectionKind::Minor => {
            println!(
                "GC: {} ({}) {}/{} -> {}/{}; {:.2} ms; {} promoted; {} copied",
                kind,
                reason,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
                formatted_size(config.minor_promoted),
                formatted_size(config.minor_copied),
            );
        }

        CollectionKind::Full => {
            println!(
                "GC: {} ({}) {}/{} -> {}/{}; {:.2} ms",
                kind,
                reason,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
            );
        }
    }
}

fn object_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    young.active_size() + old.active_size() + large.committed_size()
}

fn memory_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    young_size + old.committed_size() + large.committed_size()
}

pub struct HeapConfig {
    min_heap_size: usize,
    max_heap_size: usize,

    pub eden_size: usize,
    pub semi_size: usize,
    pub old_size: usize,
    pub old_limit: usize,

    gc_start: u64,
    gc_duration: f32,

    start_object_size: usize,
    start_memory_size: usize,
    end_object_size: usize,
    end_memory_size: usize,

    pub minor_promoted: usize,
    pub minor_copied: usize,

    pub total_minor_collections: usize,
    pub total_minor_pause: f32,
    pub total_full_collections: usize,
    pub total_full_pause: f32,

    full_phases: Vec<FullCollectorPhases>,
    minor_phases: Vec<MinorCollectorPhases>,
}

impl HeapConfig {
    pub fn new(min_heap_size: usize, max_heap_size: usize) -> HeapConfig {
        assert!(min_heap_size <= max_heap_size);

        HeapConfig {
            min_heap_size: min_heap_size,
            max_heap_size: max_heap_size,

            eden_size: 0,
            semi_size: 0,
            old_size: 0,
            old_limit: 0,

            gc_start: 0,
            gc_duration: 0f32,

            start_object_size: 0,
            start_memory_size: 0,
            end_object_size: 0,
            end_memory_size: 0,

            minor_promoted: 0,
            minor_copied: 0,

            total_minor_collections: 0,
            total_minor_pause: 0f32,
            total_full_collections: 0,
            total_full_pause: 0f32,

            full_phases: Vec::new(),
            minor_phases: Vec::new(),
        }
    }

    pub fn add_full(&mut self, phases: FullCollectorPhases) {
        self.full_phases.push(phases);
    }

    pub fn add_minor(&mut self, phases: MinorCollectorPhases) {
        self.minor_phases.push(phases);
    }

    pub fn full_marking(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.marking).collect();
        calculate_numbers(&values)
    }

    pub fn full_marking_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.marking).collect())
    }

    pub fn full_compute_forward(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.compute_forward).collect();
        calculate_numbers(&values)
    }

    pub fn full_compute_forward_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.compute_forward).collect())
    }

    pub fn full_update_refs(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.update_refs).collect();
        calculate_numbers(&values)
    }

    pub fn full_update_refs_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.update_refs).collect())
    }

    pub fn full_relocate(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.relocate).collect();
        calculate_numbers(&values)
    }

    pub fn full_relocate_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.relocate).collect())
    }

    pub fn full_reset_cards(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.reset_cards).collect();
        calculate_numbers(&values)
    }

    pub fn full_reset_cards_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.reset_cards).collect())
    }

    pub fn full_total(&self) -> Numbers {
        let values: Vec<_> = self.full_phases.iter().map(|x| x.total).collect();
        calculate_numbers(&values)
    }

    pub fn full_total_all(&self) -> AllNumbers {
        AllNumbers(self.full_phases.iter().map(|x| x.total).collect())
    }

    pub fn minor_roots(&self) -> Numbers {
        let values: Vec<_> = self.minor_phases.iter().map(|x| x.roots).collect();
        calculate_numbers(&values)
    }

    pub fn minor_roots_all(&self) -> AllNumbers {
        AllNumbers(self.minor_phases.iter().map(|x| x.roots).collect())
    }

    pub fn minor_tracing(&self) -> Numbers {
        let values: Vec<_> = self.minor_phases.iter().map(|x| x.tracing).collect();
        calculate_numbers(&values)
    }

    pub fn minor_tracing_all(&self) -> AllNumbers {
        AllNumbers(self.minor_phases.iter().map(|x| x.tracing).collect())
    }

    pub fn minor_total(&self) -> Numbers {
        let values: Vec<_> = self.minor_phases.iter().map(|x| x.total).collect();
        calculate_numbers(&values)
    }

    pub fn minor_total_all(&self) -> AllNumbers {
        AllNumbers(self.minor_phases.iter().map(|x| x.total).collect())
    }

    pub fn grow_old(&mut self, size: usize) -> bool {
        if self.old_size + size <= self.old_limit {
            self.old_size += size;
            true
        } else {
            false
        }
    }
}

pub struct Numbers {
    pub avg: f32,
    pub sum: f32,
    pub min: f32,
    pub max: f32,
}

impl Numbers {
    fn zero() -> Numbers {
        Numbers {
            avg: 0f32,
            sum: 0f32,
            min: 0f32,
            max: 0f32,
        }
    }
}

impl fmt::Display for Numbers {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "avg={:<8.1} sum={:<8.1} min={:<8.1} max={:<8.1}",
            self.avg, self.sum, self.min, self.max
        )
    }
}

fn calculate_numbers(data: &[f32]) -> Numbers {
    if data.len() == 0 {
        return Numbers::zero();
    }

    let mut sum = 0f32;
    let mut xmin = data[0];
    let mut xmax = data[0];

    for el in data {
        sum += *el;
        xmin = f32::min(xmin, *el);
        xmax = f32::max(xmax, *el);
    }

    let avg = sum / (data.len() as f32);

    Numbers {
        avg: avg,
        sum: sum,
        min: xmin,
        max: xmax,
    }
}

pub type SharedHeapConfig = Arc<Mutex<HeapConfig>>;

#[derive(Clone)]
pub struct FullCollectorPhases {
    pub marking: f32,
    pub compute_forward: f32,
    pub update_refs: f32,
    pub relocate: f32,
    pub reset_cards: f32,
    pub total: f32,
}

impl FullCollectorPhases {
    pub fn new() -> FullCollectorPhases {
        FullCollectorPhases {
            marking: 0f32,
            compute_forward: 0f32,
            update_refs: 0f32,
            relocate: 0f32,
            reset_cards: 0f32,
            total: 0f32,
        }
    }
}

#[derive(Clone)]
pub struct MinorCollectorPhases {
    pub roots: f32,
    pub tracing: f32,
    pub total: f32,
}

impl MinorCollectorPhases {
    pub fn new() -> MinorCollectorPhases {
        MinorCollectorPhases {
            roots: 0f32,
            tracing: 0f32,
            total: 0f32,
        }
    }
}
<|endoftext|>


<commit_before>/// Calculates the tetrahedron (four) coordinates
///
/// # Output
///
/// * `zeta` -- the tetrahedron coordinates (len = 4)
///
/// # Input
///
/// * `xa,xb,xc,xd` -- corners (each has len = 3)
/// * `xp` -- the point to calculate the coordinates (len = 3)
///
/// # Panics
///
/// This function will panic if the array sizes are incorrect
pub fn tetrahedron_coords(zeta: &mut [f64], xa: &[f64], xb: &[f64], xc: &[f64], xd: &[f64], xp: &[f64]) {
    let (x1, y1, z1) = (xa[0], xa[1], xa[2]);
    let (x2, y2, z2) = (xb[0], xb[1], xb[2]);
    let (x3, y3, z3) = (xc[0], xc[1], xc[2]);
    let (x4, y4, z4) = (xd[0], xd[1], xd[2]);
    let (x, y, z) = (xp[0], xp[1], xp[2]);
    let v6 = (x2 - x1) * ((y2 - y3) * (z3 - z4) - (y3 - y4) * (z2 - z3))
        + (x3 - x2) * ((y3 - y4) * (z1 - z2) - (y1 - y2) * (z3 - z4))
        + (x4 - x3) * ((y1 - y2) * (z2 - z3) - (y2 - y3) * (z1 - z2));
    zeta[0] = ((x2 * (y3 * z4 - y4 * z3) + x3 * (y4 * z2 - y2 * z4) + x4 * (y2 * z3 - y3 * z2))
        + x * ((y4 - y2) * (z3 - z2) - (y3 - y2) * (z4 - z2))
        + y * ((x3 - x2) * (z4 - z2) - (x4 - x2) * (z3 - z2))
        + z * ((x4 - x2) * (y3 - y2) - (x3 - x2) * (y4 - y2)))
        / v6;
    zeta[1] = ((x1 * (y4 * z3 - y3 * z4) + x3 * (y1 * z4 - y4 * z1) + x4 * (y3 * z1 - y1 * z3))
        + x * ((y3 - y1) * (z4 - z3) - (y3 - y4) * (z1 - z3))
        + y * ((x4 - x3) * (z3 - z1) - (x1 - x3) * (z3 - z4))
        + z * ((x3 - x1) * (y4 - y3) - (x3 - x4) * (y1 - y3)))
        / v6;
    zeta[2] = ((x1 * (y2 * z4 - y4 * z2) + x2 * (y4 * z1 - y1 * z4) + x4 * (y1 * z2 - y2 * z1))
        + x * ((y2 - y4) * (z1 - z4) - (y1 - y4) * (z2 - z4))
        + y * ((x1 - x4) * (z2 - z4) - (x2 - x4) * (z1 - z4))
        + z * ((x2 - x4) * (y1 - y4) - (x1 - x4) * (y2 - y4)))
        / v6;
    zeta[3] = ((x1 * (y3 * z2 - y2 * z3) + x2 * (y1 * z3 - y3 * z1) + x3 * (y2 * z1 - y1 * z2))
        + x * ((y1 - y3) * (z2 - z1) - (y1 - y2) * (z3 - z1))
        + y * ((x2 - x1) * (z1 - z3) - (x3 - x1) * (z1 - z2))
        + z * ((x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1)))
        / v6;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::tetrahedron_coords;
    use crate::util::ONE_BY_3;
    use russell_chk::assert_vec_approx_eq;

    #[test]
    fn tet_nat_coords_works() {
        let xa = &[0.0, 0.0, 0.0];
        let xb = &[1.0, 0.0, 0.0];
        let xc = &[0.0, 1.0, 0.0];
        let xd = &[0.0, 0.0, 1.0];
        let mut zeta = vec![0.0; 4];
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 0.0]);
        assert_eq!(zeta, &[1.0, 0.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 0.0, 0.0]);
        assert_eq!(zeta, &[0.0, 1.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 1.0, 0.0]);
        assert_eq!(zeta, &[0.0, 0.0, 1.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 1.0]);
        assert_eq!(zeta, &[0.0, 0.0, 0.0, 1.0]);

        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.0]);
        assert_eq!(zeta, &[0.0, 0.5, 0.5, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.5]);
        assert_eq!(zeta, &[-0.5, 0.5, 0.5, 0.5]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 1.0, 1.0]);
        assert_eq!(zeta, &[-2.0, 1.0, 1.0, 1.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[ONE_BY_3, ONE_BY_3, ONE_BY_3]);
        assert_vec_approx_eq!(zeta, &[0.0, ONE_BY_3, ONE_BY_3, ONE_BY_3], 1e-15);
    }
}
<commit_msg>Impl in_tetrahedron<commit_after>/// Calculates the tetrahedron (four) coordinates
///
/// # Output
///
/// * `zeta` -- the tetrahedron coordinates (len = 4)
///
/// # Input
///
/// * `xa,xb,xc,xd` -- corners (each has len = 3)
/// * `xp` -- the point to calculate the coordinates (len = 3)
///
/// # Panics
///
/// This function will panic if the array sizes are incorrect
pub fn tetrahedron_coords(zeta: &mut [f64], xa: &[f64], xb: &[f64], xc: &[f64], xd: &[f64], xp: &[f64]) {
    let (x1, y1, z1) = (xa[0], xa[1], xa[2]);
    let (x2, y2, z2) = (xb[0], xb[1], xb[2]);
    let (x3, y3, z3) = (xc[0], xc[1], xc[2]);
    let (x4, y4, z4) = (xd[0], xd[1], xd[2]);
    let (x, y, z) = (xp[0], xp[1], xp[2]);
    let v6 = (x2 - x1) * ((y2 - y3) * (z3 - z4) - (y3 - y4) * (z2 - z3))
        + (x3 - x2) * ((y3 - y4) * (z1 - z2) - (y1 - y2) * (z3 - z4))
        + (x4 - x3) * ((y1 - y2) * (z2 - z3) - (y2 - y3) * (z1 - z2));
    zeta[0] = ((x2 * (y3 * z4 - y4 * z3) + x3 * (y4 * z2 - y2 * z4) + x4 * (y2 * z3 - y3 * z2))
        + x * ((y4 - y2) * (z3 - z2) - (y3 - y2) * (z4 - z2))
        + y * ((x3 - x2) * (z4 - z2) - (x4 - x2) * (z3 - z2))
        + z * ((x4 - x2) * (y3 - y2) - (x3 - x2) * (y4 - y2)))
        / v6;
    zeta[1] = ((x1 * (y4 * z3 - y3 * z4) + x3 * (y1 * z4 - y4 * z1) + x4 * (y3 * z1 - y1 * z3))
        + x * ((y3 - y1) * (z4 - z3) - (y3 - y4) * (z1 - z3))
        + y * ((x4 - x3) * (z3 - z1) - (x1 - x3) * (z3 - z4))
        + z * ((x3 - x1) * (y4 - y3) - (x3 - x4) * (y1 - y3)))
        / v6;
    zeta[2] = ((x1 * (y2 * z4 - y4 * z2) + x2 * (y4 * z1 - y1 * z4) + x4 * (y1 * z2 - y2 * z1))
        + x * ((y2 - y4) * (z1 - z4) - (y1 - y4) * (z2 - z4))
        + y * ((x1 - x4) * (z2 - z4) - (x2 - x4) * (z1 - z4))
        + z * ((x2 - x4) * (y1 - y4) - (x1 - x4) * (y2 - y4)))
        / v6;
    zeta[3] = ((x1 * (y3 * z2 - y2 * z3) + x2 * (y1 * z3 - y3 * z1) + x3 * (y2 * z1 - y1 * z2))
        + x * ((y1 - y3) * (z2 - z1) - (y1 - y2) * (z3 - z1))
        + y * ((x2 - x1) * (z1 - z3) - (x3 - x1) * (z1 - z2))
        + z * ((x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1)))
        / v6;
}

/// Indicates if a point is inside a tetrahedron by looking at its tetrahedron coordinates (zeta)
///
/// Note: the point is inside (or on a face) if all zeta are positive (or zero)
#[inline]
pub fn in_tetrahedron(zeta: &[f64]) -> bool {
    for v in zeta {
        if *v < 0.0 {
            return false;
        }
    }
    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{in_tetrahedron, tetrahedron_coords};
    use crate::util::ONE_BY_3;
    use russell_chk::assert_vec_approx_eq;

    #[test]
    fn tetrahedron_coords_works() {
        let xa = &[0.0, 0.0, 0.0];
        let xb = &[1.0, 0.0, 0.0];
        let xc = &[0.0, 1.0, 0.0];
        let xd = &[0.0, 0.0, 1.0];
        let mut zeta = vec![0.0; 4];
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 0.0]);
        assert_eq!(zeta, &[1.0, 0.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 0.0, 0.0]);
        assert_eq!(zeta, &[0.0, 1.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 1.0, 0.0]);
        assert_eq!(zeta, &[0.0, 0.0, 1.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 1.0]);
        assert_eq!(zeta, &[0.0, 0.0, 0.0, 1.0]);

        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.0]);
        assert_eq!(zeta, &[0.0, 0.5, 0.5, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.5]);
        assert_eq!(zeta, &[-0.5, 0.5, 0.5, 0.5]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 1.0, 1.0]);
        assert_eq!(zeta, &[-2.0, 1.0, 1.0, 1.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[ONE_BY_3, ONE_BY_3, ONE_BY_3]);
        assert_vec_approx_eq!(zeta, &[0.0, ONE_BY_3, ONE_BY_3, ONE_BY_3], 1e-15);
    }

    #[test]
    fn in_tetrahedron_works() {
        assert_eq!(in_tetrahedron(&[-1.0, 0.0, 0.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, -1.0, 0.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, -1.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, 0.0, -1.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, 0.0, 0.0]), true);
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap};
use std::thread;
use std::sync::{Arc};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

use crossbeam::sync::{MsQueue};
use glium::glutin::{WindowBuilder, CursorState, get_primary_monitor};
use glium::{DisplayBuild};
use mioco;
use mioco::{Mioco, Config};
use mioco::sync::{Mutex, RwLock};
use mioco::sync::mpsc::{Receiver, Sender, channel};
use time;

use input::{InputContext, InputFrame};
use physics::{PhysicsContext, PhysicsFrame};
use render::{RenderContext, RenderFrame, RenderProcessor, RenderToken};
use scheduler::{BalancingScheduler};

// TODO: try to remove Arc dependency
//
// job should have priority
// job should have estimated length
// 		s.t. the gl thread only does short, high priority tasks
//

// --- THREADING MODEL ---
// n worker threads where n is the number of cores on the system
// 1 lightweight event processing / frame_kicking / gl thread (TODO: minimize gl tasks to actual gl calls)
//

unsafe impl Send for Context {}
unsafe impl Sync for Context {}

pub fn init() {
	const INPUT_FREQUENCY: u64 = 120;
	const RENDER_FREQUENCY: u64 = 60;

	let window_size = get_primary_monitor().get_dimensions();
	let window_size = (window_size.0/2, window_size.1/2); // FIXME: macbook scaling bs
	let aspect_ratio = (window_size.0 as f64) / (window_size.1 as f64);

	let glium_context = WindowBuilder::new()
		.with_dimensions(window_size.0, window_size.1)
		.with_title(format!("SG"))
		.with_depth_buffer(24)
		.with_decorations(false)
		.build_glium().unwrap();

	glium_context.get_window().unwrap().set_cursor_state(CursorState::Grab).ok();

	let q = Arc::new(MsQueue::new());

	let (render_tokens_sender, render_tokens_receiver) = channel::<RenderToken>();
	let context = Arc::new(
		Context {
			exit: AtomicBool::new(false),
			input_senders: MsQueue::new(),
			last_physics_frame: RwLock::new(Arc::new(PhysicsFrame::frame_zero(aspect_ratio))),
			render_tokens_length: AtomicUsize::new(0),
			physics_continuations: Arc::new(Mutex::new(HashMap::new())),

			input:   InputContext  ::new(),
			physics: PhysicsContext::new(),
			render:  RenderContext ::new(q.clone(), window_size),
		}
	);

	{
		let context = context.clone();
		thread::spawn(move || { spawn_coroutines(context, render_tokens_receiver); });
	}

	let mut render_processor = RenderProcessor::new(q, glium_context);
	let mut last_input_time = time::precise_time_ns() / 1_000_000;
	let mut last_render_time = last_input_time;

	// TODO: may need to be refactored to handle system events more frequently/(lower max latency)
	//
	while let Some(events) = render_processor.handle_system_events() {
		if context.exit.load(Ordering::Relaxed) { break }

		context.input.post_input_events(events);

		let time = time::precise_time_ns() / 1_000_000;

		while last_input_time < time {
			if let Some(sender) = context.input_senders.try_pop() {
				sender.send(()).unwrap();
				last_input_time += 1_000 / INPUT_FREQUENCY;
			} else {
				break;
			}
		}

		while last_render_time < time {
			const FIF_RENDER: usize = 1;
			let length = context.render_tokens_length.load(Ordering::Acquire);
			if length < FIF_RENDER {
				context.render_tokens_length.fetch_add(1, Ordering::Release);
				render_tokens_sender.send(RenderToken).unwrap();
				last_render_time += 1_000 / RENDER_FREQUENCY;
			}
		}

		render_processor.handle_render_commands();

		thread::yield_now();
	}
}

struct Continuation {
	id: u64,
	req_count: AtomicUsize,
	reqs: MsQueue<Arc<Result>>,
}

enum Result { InputFrame(Arc<InputFrame>), PhysicsFrame(Arc<PhysicsFrame>) }

pub struct Context {
	exit: AtomicBool,
	input_senders: MsQueue<Sender<()>>,
	last_physics_frame: RwLock<Arc<PhysicsFrame>>,
	physics_continuations: Arc<Mutex<HashMap<u64, Arc<Continuation>>>>,
	render_tokens_length: AtomicUsize,

	pub input: InputContext,
	pub physics: PhysicsContext,
	pub render: RenderContext,
}

impl Context {
	pub fn input_signal(&self) -> Receiver<()> {
		let (sender, receiver) = channel();
		self.input_senders.push(sender);
		receiver
	}
}

fn input_entry(context: Arc<Context>, coroutine: Arc<Continuation>) {
	let start_signal = context.input_signal();
	start_signal.recv().unwrap();

	let last_input_frame = {
		match *coroutine.reqs.pop() {
			Result::InputFrame(ref data) => data.clone(),
			_ => unreachable!()
		}
	};

	let input_frame = InputFrame::new(context.clone(), last_input_frame);
	let result = Arc::new(Result::InputFrame(Arc::new(input_frame)));

	{
		let q = MsQueue::new();
		q.push(result.clone());
		let context = context.clone();
		let continuation = Arc::new(Continuation {
			id:        coroutine.id + 1,
			req_count: AtomicUsize::new(0),
			reqs:      q,
		});
		mioco::spawn(move|| input_entry(context, continuation));
	}
	{
		let continuation = {
			let mut map = context.physics_continuations.lock().unwrap();
			map.entry(coroutine.id).or_insert(Arc::new(Continuation {
				id:        coroutine.id,
				req_count: AtomicUsize::new(2),
				reqs:      MsQueue::new(),
			})).clone()
		};
		continuation.reqs.push(result.clone());
		let req_count = continuation.req_count.fetch_sub(1, Ordering::Relaxed);
		if req_count == 1 {
			let context = context.clone();
			let continuation = context.physics_continuations.lock().unwrap().remove(&coroutine.id).unwrap();

			mioco::spawn(move||physics_entry(context, continuation));
		}
	}
}

fn physics_entry(context: Arc<Context>, coroutine: Arc<Continuation>) {
	let (last_input_frame, last_physics_frame) = {
		let mut input_frame = Arc::new(InputFrame::frame_zero());
		let mut physics_frame = Arc::new(PhysicsFrame::frame_zero(context.render.aspect_ratio()));

		for _ in 0..2 {
			match *coroutine.reqs.pop() {
				Result::InputFrame(ref data) => input_frame = data.clone(),
				Result::PhysicsFrame(ref data) => physics_frame = data.clone(),
			};
		}
		(input_frame, physics_frame)
	};

	let physics_frame = Arc::new(PhysicsFrame::new(context.clone(), last_physics_frame, last_input_frame));
	let result = Arc::new(Result::PhysicsFrame(physics_frame.clone()));

	{ // TODO: do something better (that doesnt potentially block the sender)
		let latest_frame = context.last_physics_frame.read().unwrap().clone();
		if physics_frame.frame_counter > latest_frame.frame_counter {
			let mut reference = context.last_physics_frame.write().unwrap();
			*reference = physics_frame.clone();
		}
	}

	{
		let continuation = {
			let mut map = context.physics_continuations.lock().unwrap();
			map.entry(coroutine.id + 1).or_insert(Arc::new(Continuation {
				id:        coroutine.id + 1,
				req_count: AtomicUsize::new(2),
				reqs:      MsQueue::new(),
			})).clone()
		};
		continuation.reqs.push(result.clone());
		let req_count = continuation.req_count.fetch_sub(1, Ordering::Relaxed);
		if req_count == 1 {
		let context = context.clone();
		let continuation = context.physics_continuations.lock().unwrap().remove(&(coroutine.id + 1)).unwrap();
			mioco::spawn(move||physics_entry(context, continuation));
		}
	}
}

fn render_entry(context: Arc<Context>, render_tokens: Receiver<RenderToken>) {
	while !context.exit.load(Ordering::Relaxed) {
		render_tokens.recv().unwrap();

		let physics_frame = context.last_physics_frame.read().unwrap().clone();

		// TODO: don't render the same physics_frame twice

		let render_frame = {
			let context = context.clone();
			RenderFrame::new(context, physics_frame)
		};

		context.render.q.push(render_frame);
		context.render_tokens_length.fetch_sub(1, Ordering::Release);
	}
}

fn spawn_coroutines(context: Arc<Context>, render_tokens: Receiver<RenderToken>) {
	const NUM_THREADS: usize = 4;

	let mut config = Config::new();
	config.set_thread_num(NUM_THREADS);
	config.set_scheduler(Box::new(BalancingScheduler::new(NUM_THREADS)));
	config.set_catch_panics(false);

	{
		let result = Arc::new(Result::PhysicsFrame(Arc::new(PhysicsFrame::frame_zero(context.render.aspect_ratio()))));
		let q = MsQueue::new();
		q.push(result);
			context.physics_continuations.lock().unwrap().insert(1, Arc::new(Continuation {
			id: 1,
			req_count: AtomicUsize::new(1),
			reqs: q,
		}));
	}

	Mioco::new_configured(config).start(move|| {
		{
			let result = Arc::new(Result::InputFrame(Arc::new(InputFrame::frame_zero())));
			let q = MsQueue::new();
			q.push(result);
			let continuation = Arc::new(Continuation {
				id:        1,
				req_count: AtomicUsize::new(0),
				reqs:      q,
			});
			let context = context.clone();
			mioco::spawn(move||input_entry(context, continuation));
		}
		{ // for each render frame in flight
			let context = context.clone();
			mioco::spawn(move||render_entry(context, render_tokens));
		}
	}).unwrap();
}

<commit_msg>Remove unnecessary initialization<commit_after>use std::collections::{HashMap};
use std::mem;
use std::thread;
use std::sync::{Arc};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

use crossbeam::sync::{MsQueue};
use glium::glutin::{WindowBuilder, CursorState, get_primary_monitor};
use glium::{DisplayBuild};
use mioco;
use mioco::{Mioco, Config};
use mioco::sync::{Mutex, RwLock};
use mioco::sync::mpsc::{Receiver, Sender, channel};
use time;

use input::{InputContext, InputFrame};
use physics::{PhysicsContext, PhysicsFrame};
use render::{RenderContext, RenderFrame, RenderProcessor, RenderToken};
use scheduler::{BalancingScheduler};

// TODO: try to remove Arc dependency
//
// job should have priority
// job should have estimated length
// 		s.t. the gl thread only does short, high priority tasks
//

// --- THREADING MODEL ---
// n worker threads where n is the number of cores on the system
// 1 lightweight event processing / frame_kicking / gl thread (TODO: minimize gl tasks to actual gl calls)
//

unsafe impl Send for Context {}
unsafe impl Sync for Context {}

pub fn init() {
	const INPUT_FREQUENCY: u64 = 120;
	const RENDER_FREQUENCY: u64 = 60;

	let window_size = get_primary_monitor().get_dimensions();
	let window_size = (window_size.0/2, window_size.1/2); // FIXME: macbook scaling bs
	let aspect_ratio = (window_size.0 as f64) / (window_size.1 as f64);

	let glium_context = WindowBuilder::new()
		.with_dimensions(window_size.0, window_size.1)
		.with_title(format!("SG"))
		.with_depth_buffer(24)
		.with_decorations(false)
		.build_glium().unwrap();

	glium_context.get_window().unwrap().set_cursor_state(CursorState::Grab).ok();

	let q = Arc::new(MsQueue::new());

	let (render_tokens_sender, render_tokens_receiver) = channel::<RenderToken>();

	let physics_zero = RwLock::new(Arc::new(PhysicsFrame::frame_zero(aspect_ratio)));

	let context = Arc::new(
		Context {
			exit: AtomicBool::new(false),
			input_senders: MsQueue::new(),
			last_physics_frame: physics_zero,
			render_tokens_length: AtomicUsize::new(0),
			physics_continuations: Arc::new(Mutex::new(HashMap::new())),

			input:   InputContext  ::new(),
			physics: PhysicsContext::new(),
			render:  RenderContext ::new(q.clone(), window_size),
		}
	);

	{
		let context = context.clone();
		thread::spawn(move || { spawn_coroutines(context, render_tokens_receiver); });
	}

	let mut render_processor = RenderProcessor::new(q, glium_context);
	let mut last_input_time = time::precise_time_ns() / 1_000_000;
	let mut last_render_time = last_input_time;

	// TODO: may need to be refactored to handle system events more frequently/(lower max latency)
	//
	while let Some(events) = render_processor.handle_system_events() {
		if context.exit.load(Ordering::Relaxed) { break }

		context.input.post_input_events(events);

		let time = time::precise_time_ns() / 1_000_000;

		while last_input_time < time {
			if let Some(sender) = context.input_senders.try_pop() {
				sender.send(()).unwrap();
				last_input_time += 1_000 / INPUT_FREQUENCY;
			} else {
				break;
			}
		}

		while last_render_time < time {
			const FIF_RENDER: usize = 1;
			let length = context.render_tokens_length.load(Ordering::Acquire);
			if length < FIF_RENDER {
				context.render_tokens_length.fetch_add(1, Ordering::Release);
				render_tokens_sender.send(RenderToken).unwrap();
				last_render_time += 1_000 / RENDER_FREQUENCY;
			}
		}

		render_processor.handle_render_commands();

		thread::yield_now();
	}
}

struct Continuation {
	id: u64,
	req_count: AtomicUsize,
	reqs: MsQueue<Arc<Result>>,
}

enum Result { InputFrame(Arc<InputFrame>), PhysicsFrame(Arc<PhysicsFrame>) }

pub struct Context {
	exit: AtomicBool,
	input_senders: MsQueue<Sender<()>>,
	last_physics_frame: RwLock<Arc<PhysicsFrame>>,
	physics_continuations: Arc<Mutex<HashMap<u64, Arc<Continuation>>>>,
	render_tokens_length: AtomicUsize,

	pub input: InputContext,
	pub physics: PhysicsContext,
	pub render: RenderContext,
}

impl Context {
	pub fn input_signal(&self) -> Receiver<()> {
		let (sender, receiver) = channel();
		self.input_senders.push(sender);
		receiver
	}
}

fn input_entry(context: Arc<Context>, coroutine: Arc<Continuation>) {
	let start_signal = context.input_signal();
	start_signal.recv().unwrap();

	let last_input_frame = {
		match *coroutine.reqs.pop() {
			Result::InputFrame(ref data) => data.clone(),
			_ => unreachable!()
		}
	};

	let input_frame = InputFrame::new(context.clone(), last_input_frame);
	let result = Arc::new(Result::InputFrame(Arc::new(input_frame)));

	{
		let q = MsQueue::new();
		q.push(result.clone());
		let context = context.clone();
		let continuation = Arc::new(Continuation {
			id:        coroutine.id + 1,
			req_count: AtomicUsize::new(0),
			reqs:      q,
		});
		mioco::spawn(move|| input_entry(context, continuation));
	}
	{
		let continuation = {
			let mut map = context.physics_continuations.lock().unwrap();
			map.entry(coroutine.id).or_insert(Arc::new(Continuation {
				id:        coroutine.id,
				req_count: AtomicUsize::new(2),
				reqs:      MsQueue::new(),
			})).clone()
		};
		continuation.reqs.push(result.clone());
		let req_count = continuation.req_count.fetch_sub(1, Ordering::Relaxed);
		if req_count == 1 {
			let context = context.clone();
			let continuation = context.physics_continuations.lock().unwrap().remove(&coroutine.id).unwrap();

			mioco::spawn(move||physics_entry(context, continuation));
		}
	}
}

fn physics_entry(context: Arc<Context>, coroutine: Arc<Continuation>) {
	let (last_input_frame, last_physics_frame) = unsafe {
		let mut input_frame   = mem::uninitialized();
		let mut physics_frame = mem::uninitialized();
		for _ in 0..2 {
			match *coroutine.reqs.pop() {
				Result::InputFrame(ref data) => input_frame = data.clone(),
				Result::PhysicsFrame(ref data) => physics_frame = data.clone(),
			};
		}
		(input_frame, physics_frame)
	};

	let physics_frame = Arc::new(PhysicsFrame::new(context.clone(), last_physics_frame, last_input_frame));
	let result = Arc::new(Result::PhysicsFrame(physics_frame.clone()));

	{ // TODO: do something better (that doesnt potentially block the sender)
		let latest_frame = context.last_physics_frame.read().unwrap().clone();
		if physics_frame.frame_counter > latest_frame.frame_counter {
			let mut reference = context.last_physics_frame.write().unwrap();
			*reference = physics_frame.clone();
		}
	}

	{
		let continuation = {
			let mut map = context.physics_continuations.lock().unwrap();
			map.entry(coroutine.id + 1).or_insert(Arc::new(Continuation {
				id:        coroutine.id + 1,
				req_count: AtomicUsize::new(2),
				reqs:      MsQueue::new(),
			})).clone()
		};
		continuation.reqs.push(result.clone());
		let req_count = continuation.req_count.fetch_sub(1, Ordering::Relaxed);
		if req_count == 1 {
		let context = context.clone();
		let continuation = context.physics_continuations.lock().unwrap().remove(&(coroutine.id + 1)).unwrap();
			mioco::spawn(move||physics_entry(context, continuation));
		}
	}
}

fn render_entry(context: Arc<Context>, render_tokens: Receiver<RenderToken>) {
	while !context.exit.load(Ordering::Relaxed) {
		render_tokens.recv().unwrap();

		let physics_frame = context.last_physics_frame.read().unwrap().clone();

		// TODO: don't render the same physics_frame twice

		let render_frame = {
			let context = context.clone();
			RenderFrame::new(context, physics_frame)
		};

		context.render.q.push(render_frame);
		context.render_tokens_length.fetch_sub(1, Ordering::Release);
	}
}

fn spawn_coroutines(context: Arc<Context>, render_tokens: Receiver<RenderToken>) {
	const NUM_THREADS: usize = 4;

	let mut config = Config::new();
	config.set_thread_num(NUM_THREADS);
	config.set_scheduler(Box::new(BalancingScheduler::new(NUM_THREADS)));
	config.set_catch_panics(false);

	{
		let arc_physics_zero = context.last_physics_frame.read().unwrap().clone();
		let result = Arc::new(Result::PhysicsFrame(arc_physics_zero));
		let q = MsQueue::new();
		q.push(result);
			context.physics_continuations.lock().unwrap().insert(1, Arc::new(Continuation {
			id: 1,
			req_count: AtomicUsize::new(1),
			reqs: q,
		}));
	}

	Mioco::new_configured(config).start(move|| {
		{
			let result = Arc::new(Result::InputFrame(Arc::new(InputFrame::frame_zero())));
			let q = MsQueue::new();
			q.push(result);
			let continuation = Arc::new(Continuation {
				id:        1,
				req_count: AtomicUsize::new(0),
				reqs:      q,
			});
			let context = context.clone();
			mioco::spawn(move||input_entry(context, continuation));
		}
		{ // for each render frame in flight
			let context = context.clone();
			mioco::spawn(move||render_entry(context, render_tokens));
		}
	}).unwrap();
}

<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Worker that runs in a virtio-video thread.

use std::collections::VecDeque;

use base::{error, Event, WaitContext};
use vm_memory::GuestMemory;

use crate::virtio::queue::{DescriptorChain, Queue};
use crate::virtio::resource_bridge::ResourceRequestSocket;
use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
use crate::virtio::video::command::{QueueType, VideoCmd};
use crate::virtio::video::device::{
    AsyncCmdResponse, Device, Token, VideoCmdResponseType, VideoEvtResponseType,
};
use crate::virtio::video::event::{self, EvtType, VideoEvt};
use crate::virtio::video::response::{self, Response};
use crate::virtio::video::{Error, Result};
use crate::virtio::{Interrupt, Reader, Writer};

pub struct Worker {
    pub interrupt: Interrupt,
    pub mem: GuestMemory,
    pub cmd_evt: Event,
    pub event_evt: Event,
    pub kill_evt: Event,
    pub resource_bridge: ResourceRequestSocket,
}

/// Pair of a descriptor chain and a response to be written.
type WritableResp = (DescriptorChain, response::CmdResponse);

impl Worker {
    /// Writes responses into the command queue.
    fn write_responses(
        &self,
        cmd_queue: &mut Queue,
        responses: &mut VecDeque<WritableResp>,
    ) -> Result<()> {
        if responses.is_empty() {
            return Ok(());
        }
        while let Some((desc, response)) = responses.pop_front() {
            let desc_index = desc.index;
            let mut writer =
                Writer::new(self.mem.clone(), desc).map_err(Error::InvalidDescriptorChain)?;
            if let Err(e) = response.write(&mut writer) {
                error!(
                    "failed to write a command response for {:?}: {}",
                    response, e
                );
            }
            cmd_queue.add_used(&self.mem, desc_index, writer.bytes_written() as u32);
        }
        self.interrupt.signal_used_queue(cmd_queue.vector);
        Ok(())
    }

    /// Writes a `VideoEvt` into the event queue.
    fn write_event(&self, event_queue: &mut Queue, event: event::VideoEvt) -> Result<()> {
        let desc = event_queue
            .peek(&self.mem)
            .ok_or_else(|| Error::DescriptorNotAvailable)?;
        event_queue.pop_peeked(&self.mem);

        let desc_index = desc.index;
        let mut writer =
            Writer::new(self.mem.clone(), desc).map_err(Error::InvalidDescriptorChain)?;
        event
            .write(&mut writer)
            .map_err(|error| Error::WriteEventFailure { event, error })?;
        event_queue.add_used(&self.mem, desc_index, writer.bytes_written() as u32);
        self.interrupt.signal_used_queue(event_queue.vector);
        Ok(())
    }

    /// Handles a `DescriptorChain` value sent via the command queue and returns a `VecDeque`
    /// of `WritableResp` to be sent to the guest.
    fn handle_command_desc<T: Device>(
        &self,
        device: &mut T,
        wait_ctx: &WaitContext<Token>,
        desc_map: &mut AsyncCmdDescMap,
        desc: DescriptorChain,
    ) -> Result<VecDeque<WritableResp>> {
        let mut responses: VecDeque<WritableResp> = Default::default();
        let mut reader =
            Reader::new(self.mem.clone(), desc.clone()).map_err(Error::InvalidDescriptorChain)?;

        let cmd = VideoCmd::from_reader(&mut reader).map_err(Error::ReadFailure)?;

        // If a destruction command comes, cancel pending requests.
        // TODO(b/161774071): Allow `process_cmd` to return multiple responses and move this
        // into encoder/decoder.
        let async_responses = match cmd {
            VideoCmd::ResourceDestroyAll {
                stream_id,
                queue_type,
            } => desc_map.create_cancellation_responses(&stream_id, Some(queue_type), None),
            VideoCmd::StreamDestroy { stream_id } => {
                desc_map.create_cancellation_responses(&stream_id, None, None)
            }
            VideoCmd::QueueClear {
                stream_id,
                queue_type: QueueType::Output,
            } => {
                // TODO(b/153406792): Due to a workaround for a limitation in the VDA api,
                // clearing the output queue doesn't go through the same Async path as clearing
                // the input queue. However, we still need to cancel the pending resources.
                desc_map.create_cancellation_responses(&stream_id, Some(QueueType::Output), None)
            }
            _ => Default::default(),
        };
        for async_response in async_responses {
            let AsyncCmdResponse {
                tag,
                response: cmd_result,
            } = async_response;
            let destroy_desc = desc_map
                .remove(&tag)
                .ok_or_else(|| Error::UnexpectedResponse(tag))?;
            let destroy_response = match cmd_result {
                Ok(r) => r,
                Err(e) => {
                    error!("returning async error response: {}", &e);
                    e.into()
                }
            };
            responses.push_back((destroy_desc, destroy_response));
        }

        // Process the command by the device.
        let process_cmd_response = device.process_cmd(cmd, &wait_ctx, &self.resource_bridge);
        match process_cmd_response {
            Ok(VideoCmdResponseType::Sync(r)) => {
                responses.push_back((desc, r));
            }
            Ok(VideoCmdResponseType::Async(tag)) => {
                // If the command expects an asynchronous response,
                // store `desc` to use it after the back-end device notifies the
                // completion.
                desc_map.insert(tag, desc);
            }
            Err(e) => {
                error!("returning error response: {}", &e);
                responses.push_back((desc, e.into()));
            }
        }

        Ok(responses)
    }

    /// Handles each command in the command queue.
    fn handle_command_queue<T: Device>(
        &self,
        cmd_queue: &mut Queue,
        device: &mut T,
        wait_ctx: &WaitContext<Token>,
        desc_map: &mut AsyncCmdDescMap,
    ) -> Result<()> {
        let _ = self.cmd_evt.read();
        while let Some(desc) = cmd_queue.pop(&self.mem) {
            let mut resps = self.handle_command_desc(device, wait_ctx, desc_map, desc)?;
            self.write_responses(cmd_queue, &mut resps)?;
        }
        Ok(())
    }

    /// Handles an event notified via an event.
    fn handle_event<T: Device>(
        &self,
        cmd_queue: &mut Queue,
        event_queue: &mut Queue,
        device: &mut T,
        desc_map: &mut AsyncCmdDescMap,
        stream_id: u32,
    ) -> Result<()> {
        let mut responses: VecDeque<WritableResp> = Default::default();
        if let Some(event_responses) = device.process_event(desc_map, stream_id) {
            for event_response in event_responses {
                match event_response {
                    VideoEvtResponseType::AsyncCmd(async_response) => {
                        let AsyncCmdResponse {
                            tag,
                            response: cmd_result,
                        } = async_response;
                        let desc = desc_map
                            .remove(&tag)
                            .ok_or_else(|| Error::UnexpectedResponse(tag))?;
                        let cmd_response = match cmd_result {
                            Ok(r) => r,
                            Err(e) => {
                                error!("returning async error response: {}", &e);
                                e.into()
                            }
                        };
                        responses.push_back((desc, cmd_response));
                    }
                    VideoEvtResponseType::Event(evt) => {
                        self.write_event(event_queue, evt)?;
                    }
                }
            }
        }

        if let Err(e) = self.write_responses(cmd_queue, &mut responses) {
            error!("Failed to write event responses: {:?}", e);
            // Ignore result of write_event for a fatal error.
            let _ = self.write_event(
                event_queue,
                VideoEvt {
                    typ: EvtType::Error,
                    stream_id,
                },
            );
            return Err(e);
        }

        Ok(())
    }

    pub fn run<T: Device>(
        &mut self,
        mut cmd_queue: Queue,
        mut event_queue: Queue,
        mut device: T,
    ) -> Result<()> {
        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
            (&self.cmd_evt, Token::CmdQueue),
            (&self.event_evt, Token::EventQueue),
            (&self.kill_evt, Token::Kill),
            (self.interrupt.get_resample_evt(), Token::InterruptResample),
        ])
        .map_err(Error::WaitContextCreationFailed)?;

        // Stores descriptors in which responses for asynchronous commands will be written.
        let mut desc_map: AsyncCmdDescMap = Default::default();

        loop {
            let wait_events = wait_ctx.wait().map_err(Error::WaitError)?;

            for wait_event in wait_events.iter().filter(|e| e.is_readable) {
                match wait_event.token {
                    Token::CmdQueue => {
                        self.handle_command_queue(
                            &mut cmd_queue,
                            &mut device,
                            &wait_ctx,
                            &mut desc_map,
                        )?;
                    }
                    Token::EventQueue => {
                        let _ = self.event_evt.read();
                    }
                    Token::Event { id } => {
                        self.handle_event(
                            &mut cmd_queue,
                            &mut event_queue,
                            &mut device,
                            &mut desc_map,
                            id,
                        )?;
                    }
                    Token::InterruptResample => {
                        self.interrupt.interrupt_resample();
                    }
                    Token::Kill => return Ok(()),
                }
            }
        }
    }
}
<commit_msg>devices/video/decoder: fix drain+clear queue issue<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Worker that runs in a virtio-video thread.

use std::collections::VecDeque;

use base::{error, info, Event, WaitContext};
use vm_memory::GuestMemory;

use crate::virtio::queue::{DescriptorChain, Queue};
use crate::virtio::resource_bridge::ResourceRequestSocket;
use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
use crate::virtio::video::command::{QueueType, VideoCmd};
use crate::virtio::video::device::{
    AsyncCmdResponse, AsyncCmdTag, Device, Token, VideoCmdResponseType, VideoEvtResponseType,
};
use crate::virtio::video::event::{self, EvtType, VideoEvt};
use crate::virtio::video::response::{self, Response};
use crate::virtio::video::{Error, Result};
use crate::virtio::{Interrupt, Reader, Writer};

pub struct Worker {
    pub interrupt: Interrupt,
    pub mem: GuestMemory,
    pub cmd_evt: Event,
    pub event_evt: Event,
    pub kill_evt: Event,
    pub resource_bridge: ResourceRequestSocket,
}

/// Pair of a descriptor chain and a response to be written.
type WritableResp = (DescriptorChain, response::CmdResponse);

impl Worker {
    /// Writes responses into the command queue.
    fn write_responses(
        &self,
        cmd_queue: &mut Queue,
        responses: &mut VecDeque<WritableResp>,
    ) -> Result<()> {
        if responses.is_empty() {
            return Ok(());
        }
        while let Some((desc, response)) = responses.pop_front() {
            let desc_index = desc.index;
            let mut writer =
                Writer::new(self.mem.clone(), desc).map_err(Error::InvalidDescriptorChain)?;
            if let Err(e) = response.write(&mut writer) {
                error!(
                    "failed to write a command response for {:?}: {}",
                    response, e
                );
            }
            cmd_queue.add_used(&self.mem, desc_index, writer.bytes_written() as u32);
        }
        self.interrupt.signal_used_queue(cmd_queue.vector);
        Ok(())
    }

    /// Writes a `VideoEvt` into the event queue.
    fn write_event(&self, event_queue: &mut Queue, event: event::VideoEvt) -> Result<()> {
        let desc = event_queue
            .peek(&self.mem)
            .ok_or_else(|| Error::DescriptorNotAvailable)?;
        event_queue.pop_peeked(&self.mem);

        let desc_index = desc.index;
        let mut writer =
            Writer::new(self.mem.clone(), desc).map_err(Error::InvalidDescriptorChain)?;
        event
            .write(&mut writer)
            .map_err(|error| Error::WriteEventFailure { event, error })?;
        event_queue.add_used(&self.mem, desc_index, writer.bytes_written() as u32);
        self.interrupt.signal_used_queue(event_queue.vector);
        Ok(())
    }

    /// Handles a `DescriptorChain` value sent via the command queue and returns a `VecDeque`
    /// of `WritableResp` to be sent to the guest.
    fn handle_command_desc<T: Device>(
        &self,
        device: &mut T,
        wait_ctx: &WaitContext<Token>,
        desc_map: &mut AsyncCmdDescMap,
        desc: DescriptorChain,
    ) -> Result<VecDeque<WritableResp>> {
        let mut responses: VecDeque<WritableResp> = Default::default();
        let mut reader =
            Reader::new(self.mem.clone(), desc.clone()).map_err(Error::InvalidDescriptorChain)?;

        let cmd = VideoCmd::from_reader(&mut reader).map_err(Error::ReadFailure)?;

        // If a destruction command comes, cancel pending requests.
        // TODO(b/161774071): Allow `process_cmd` to return multiple responses and move this
        // into encoder/decoder.
        let async_responses = match cmd {
            VideoCmd::ResourceDestroyAll {
                stream_id,
                queue_type,
            } => desc_map.create_cancellation_responses(&stream_id, Some(queue_type), None),
            VideoCmd::StreamDestroy { stream_id } => {
                desc_map.create_cancellation_responses(&stream_id, None, None)
            }
            VideoCmd::QueueClear {
                stream_id,
                queue_type: QueueType::Output,
            } => {
                // TODO(b/153406792): Due to a workaround for a limitation in the VDA api,
                // clearing the output queue doesn't go through the same Async path as clearing
                // the input queue. However, we still need to cancel the pending resources.
                desc_map.create_cancellation_responses(&stream_id, Some(QueueType::Output), None)
            }
            _ => Default::default(),
        };
        for async_response in async_responses {
            let AsyncCmdResponse {
                tag,
                response: cmd_result,
            } = async_response;
            let destroy_desc = desc_map
                .remove(&tag)
                .ok_or_else(|| Error::UnexpectedResponse(tag))?;
            let destroy_response = match cmd_result {
                Ok(r) => r,
                Err(e) => {
                    error!("returning async error response: {}", &e);
                    e.into()
                }
            };
            responses.push_back((destroy_desc, destroy_response));
        }

        // Process the command by the device.
        let process_cmd_response = device.process_cmd(cmd, &wait_ctx, &self.resource_bridge);
        match process_cmd_response {
            Ok(VideoCmdResponseType::Sync(r)) => {
                responses.push_back((desc, r));
            }
            Ok(VideoCmdResponseType::Async(tag)) => {
                // If the command expects an asynchronous response,
                // store `desc` to use it after the back-end device notifies the
                // completion.
                desc_map.insert(tag, desc);
            }
            Err(e) => {
                error!("returning error response: {}", &e);
                responses.push_back((desc, e.into()));
            }
        }

        Ok(responses)
    }

    /// Handles each command in the command queue.
    fn handle_command_queue<T: Device>(
        &self,
        cmd_queue: &mut Queue,
        device: &mut T,
        wait_ctx: &WaitContext<Token>,
        desc_map: &mut AsyncCmdDescMap,
    ) -> Result<()> {
        let _ = self.cmd_evt.read();
        while let Some(desc) = cmd_queue.pop(&self.mem) {
            let mut resps = self.handle_command_desc(device, wait_ctx, desc_map, desc)?;
            self.write_responses(cmd_queue, &mut resps)?;
        }
        Ok(())
    }

    /// Handles an event notified via an event.
    fn handle_event<T: Device>(
        &self,
        cmd_queue: &mut Queue,
        event_queue: &mut Queue,
        device: &mut T,
        desc_map: &mut AsyncCmdDescMap,
        stream_id: u32,
    ) -> Result<()> {
        let mut responses: VecDeque<WritableResp> = Default::default();
        if let Some(event_responses) = device.process_event(desc_map, stream_id) {
            for event_response in event_responses {
                match event_response {
                    VideoEvtResponseType::AsyncCmd(async_response) => {
                        let AsyncCmdResponse {
                            tag,
                            response: cmd_result,
                        } = async_response;
                        match desc_map.remove(&tag) {
                            Some(desc) => {
                                let cmd_response = match cmd_result {
                                    Ok(r) => r,
                                    Err(e) => {
                                        error!("returning async error response: {}", &e);
                                        e.into()
                                    }
                                };
                                responses.push_back((desc, cmd_response))
                            }
                            None => match tag {
                                // TODO(b/153406792): Drain is cancelled by clearing either of the
                                // stream's queues. To work around a limitation in the VDA api, the
                                // output queue is cleared synchronously without going through VDA.
                                // Because of this, the cancellation response from VDA for the
                                // input queue might fail to find the drain's AsyncCmdTag.
                                AsyncCmdTag::Drain { stream_id: _ } => {
                                    info!("ignoring unknown drain response");
                                }
                                _ => return Err(Error::UnexpectedResponse(tag)),
                            },
                        }
                    }
                    VideoEvtResponseType::Event(evt) => {
                        self.write_event(event_queue, evt)?;
                    }
                }
            }
        }

        if let Err(e) = self.write_responses(cmd_queue, &mut responses) {
            error!("Failed to write event responses: {:?}", e);
            // Ignore result of write_event for a fatal error.
            let _ = self.write_event(
                event_queue,
                VideoEvt {
                    typ: EvtType::Error,
                    stream_id,
                },
            );
            return Err(e);
        }

        Ok(())
    }

    pub fn run<T: Device>(
        &mut self,
        mut cmd_queue: Queue,
        mut event_queue: Queue,
        mut device: T,
    ) -> Result<()> {
        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
            (&self.cmd_evt, Token::CmdQueue),
            (&self.event_evt, Token::EventQueue),
            (&self.kill_evt, Token::Kill),
            (self.interrupt.get_resample_evt(), Token::InterruptResample),
        ])
        .map_err(Error::WaitContextCreationFailed)?;

        // Stores descriptors in which responses for asynchronous commands will be written.
        let mut desc_map: AsyncCmdDescMap = Default::default();

        loop {
            let wait_events = wait_ctx.wait().map_err(Error::WaitError)?;

            for wait_event in wait_events.iter().filter(|e| e.is_readable) {
                match wait_event.token {
                    Token::CmdQueue => {
                        self.handle_command_queue(
                            &mut cmd_queue,
                            &mut device,
                            &wait_ctx,
                            &mut desc_map,
                        )?;
                    }
                    Token::EventQueue => {
                        let _ = self.event_evt.read();
                    }
                    Token::Event { id } => {
                        self.handle_event(
                            &mut cmd_queue,
                            &mut event_queue,
                            &mut device,
                            &mut desc_map,
                            id,
                        )?;
                    }
                    Token::InterruptResample => {
                        self.interrupt.interrupt_resample();
                    }
                    Token::Kill => return Ok(()),
                }
            }
        }
    }
}
<|endoftext|>