<commit_before>//! # `conc` — An efficient concurrent reclamation system
//!
//! `conc` builds upon hazard pointers to create a extremely performant system for
//! concurrently handling memory. It is more general and convenient — and often also faster — than
//! epoch-based reclamation.
//!
//! ## Why?
//!
//! aturon's [blog post](https://aturon.github.io/blog/2015/08/27/epoch/) explains the issues of
//! concurrent memory handling very well, although it take basis in epoch-based reclamation, which
//! this crate is an alternative for.
//!
//! The gist essentially is that you need to delete objects in most concurrent data structure
//! (otherwise there would be memory leaks), however cannot safely do so, as there is no way to
//! know if another thread is accessing the object in question. This (and other reclamation
//! systems) provides a solution to this problem.
//!
//! ## Usage
//!
//! While the low-level API is available, it is generally sufficient to use the `conc::Option`
//! abstraction. This acts much like familiar Rust APIs. It allows the programmer to concurrently
//! access a value through references, as well as update it, and more. Refer to the respective docs
//! for more information.
//!
//! ## Why not crossbeam/epochs?
//!
//! Epochs and classical hazard pointers are generally faster than this crate, but it doesn't
//! matter how fast it is, it has to be right.
//!
//! The issue with most other and faster solutions is that, if there is a non-trivial amount of
//! threads (say 16) constantly reading/storing some pointer, it will never get to a state, where
//! it can be reclaimed.
//!
//! In other words, given sufficient amount of threads and frequency, the gaps between the
//! reclamation might be very very long, causing very high memory usage, and potentially OOM
//! crashes.
//!
//! These issues are not hypothetical. It happened to me while testing the caching system of TFS.
//! Essentially, the to-be-destroyed garbage accumulated several gigabytes, without ever being open
//! to a collection cycle.
//!
//! It reminds of the MongoDB debate. It might very well be the fastest solution¹, but if it can't
//! even ensure consistency, what is the point?
//!
//! That being said, there are cases where this library is faster than the alternatives.
//! Moreover, there are cases where the other libraries are fine (e.g. if you have a bounded number
//! of thread and a medium-long interval between accesses).
//!
//! ¹If you want a super fast memory reclamation system, you should try NOP™, and not calling
//!  destructors.
//!
//! ## Internals
//!
//! It based on hazard pointers, although there are several differences. The idea is essentially
//! that the system keeps track of some number of "hazards". As long as a hazard protects some
//! object, the object cannot be deleted.
//!
//! Once in a while, a thread performs a garbage collection by scanning the hazards and finding the
//! objects not currently protected by any hazard. These objects are then deleted.
//!
//! To improve performance, we use a layered approach: Both garbage (objects to be deleted
//! eventually) and hazards are cached thread locally. This reduces the amount of atomic operations
//! and cache misses.
//!
//! ## Garbage collection
//!
//! Garbage collection of the concurrently managed object is done automatically between every `n`
//! frees where `n` is chosen from some probability distribution.
//!
//! Note that a garbage collection cycle might not clear all objects. For example, some objects
//! could be protected by hazards. Others might not have been exported from the thread-local cache
//! yet.
//!
//! ## Performance
//!
//! It is worth noting that atomic reads through this library usually requires three atomic CPU
//! instruction, this means that if you are traversing a list or something like that, this library
//! might not be for you.

#![feature(alloc_system, thread_local_state)]
#![deny(missing_docs)]

#[macro_use]
extern crate lazy_static;
extern crate alloc_system;
extern crate parking_lot;
extern crate rand;

mod atomic;
mod garbage;
mod global;
mod guard;
mod hazard;
mod local;
mod mpsc;
pub mod sync;

pub use atomic::Atomic;
pub use guard::Guard;

use std::mem;
use garbage::Garbage;

/// Attempt to collect garbage.
///
/// This function does two things:
///
/// 1. Export garbage from current thread to the global queue.
/// 2. Collect all the garbage and run destructors on the unused items.
///
/// If another thread is currently doing 2., it will be skipped. This makes it different from
/// `conc::gc()`, which will block.
///
/// If 2. fails (that is, another thread is garbage collecting), `Err(())` is returned. Otherwise
/// `Ok(())` is returned.
///
/// # Use case
///
/// Note that it is not necessary to call this manually, it will do so automatically after some
/// time has passed.
///
/// However, it can be nice if you have just trashed a very memory-hungry item in the current
/// thread, and want to attempt to GC it.
///
/// # Other threads
///
/// This cannot collect un-propagated garbage accumulated locally in other threads. This will only
/// attempt to collect the accumulated local and global (propagated) garbage.
pub fn try_gc() -> Result<(), ()> {
    // Export the local garbage to ensure that the garbage of the current thread gets collected.
    local::export_garbage();
    // Run the global GC.
    global::try_gc()
}

/// Collect garbage.
///
/// This function does two things:
///
/// 1. Export garbage from current thread to the global queue.
/// 2. Collect all the garbage and run destructors on the unused items.
///
/// If another thread is currently doing 2., it will block until it can be done. This makes it
/// different from `conc::try_gc()`, which will skip the step.
///
/// # Use case
///
/// This is really only neccesary in one case: If you want to ensure that all the destructors of
/// inactive hazards in the current thread are run. If the destructors hold some special logic, you
/// want to execute, this will force the (inactive) ones to run these destructors and thus execute
/// the logic.
///
/// If you just want to reduce memory usage, you will probably be better off with `conc::try_gc()`.
///
/// # Other threads
///
/// This cannot collect un-propagated garbage accumulated locally in other threads. This will only
/// collect the accumulated local and global (propagated) garbage.
pub fn gc() {
    // Export the local garbage to ensure that the garbage of the current thread gets collected.
    local::export_garbage();
    // Try to garbage collect until it succeeds.
    while let Err(()) = global::try_gc() {}
}

/// Declare a pointer unreachable garbage to be deleted eventually.
///
/// This adds `ptr` to the queue of garbage, which eventually will be destroyed through its
/// destructor given in `dtor`. This is ensured to happen at some point _after_ the last guard
/// protecting the pointer is dropped.
///
/// It is legal for `ptr` to be invalidated by `dtor`, such that accessing it is undefined after
/// `dtor` has been run. This means that `dtor` can safely (there are exceptions, see below) run a
/// destructor of `ptr`'s data.
///
/// # Unreachability criterion
///
/// If you invalidate `ptr` in the destructor, it is extremely important that `ptr` is no longer
/// reachable from any data structure: It should be impossible to create _new_ guard representing
/// `ptr` from now on, as such thing can mean that new guards can be created after it is dropped
/// causing use-after-free.
pub fn add_garbage<T>(ptr: &'static T, dtor: fn(&'static T)) {
    local::add_garbage(unsafe {
        Garbage::new(ptr as *const T as *const u8 as *mut u8, mem::transmute(dtor))
    });
}
<commit_msg>Bring back jemalloc into `conc`.<commit_after>//! # `conc` — An efficient concurrent reclamation system
//!
//! `conc` builds upon hazard pointers to create a extremely performant system for
//! concurrently handling memory. It is more general and convenient — and often also faster — than
//! epoch-based reclamation.
//!
//! ## Why?
//!
//! aturon's [blog post](https://aturon.github.io/blog/2015/08/27/epoch/) explains the issues of
//! concurrent memory handling very well, although it take basis in epoch-based reclamation, which
//! this crate is an alternative for.
//!
//! The gist essentially is that you need to delete objects in most concurrent data structure
//! (otherwise there would be memory leaks), however cannot safely do so, as there is no way to
//! know if another thread is accessing the object in question. This (and other reclamation
//! systems) provides a solution to this problem.
//!
//! ## Usage
//!
//! While the low-level API is available, it is generally sufficient to use the `conc::Option`
//! abstraction. This acts much like familiar Rust APIs. It allows the programmer to concurrently
//! access a value through references, as well as update it, and more. Refer to the respective docs
//! for more information.
//!
//! ## Why not crossbeam/epochs?
//!
//! Epochs and classical hazard pointers are generally faster than this crate, but it doesn't
//! matter how fast it is, it has to be right.
//!
//! The issue with most other and faster solutions is that, if there is a non-trivial amount of
//! threads (say 16) constantly reading/storing some pointer, it will never get to a state, where
//! it can be reclaimed.
//!
//! In other words, given sufficient amount of threads and frequency, the gaps between the
//! reclamation might be very very long, causing very high memory usage, and potentially OOM
//! crashes.
//!
//! These issues are not hypothetical. It happened to me while testing the caching system of TFS.
//! Essentially, the to-be-destroyed garbage accumulated several gigabytes, without ever being open
//! to a collection cycle.
//!
//! It reminds of the MongoDB debate. It might very well be the fastest solution¹, but if it can't
//! even ensure consistency, what is the point?
//!
//! That being said, there are cases where this library is faster than the alternatives.
//! Moreover, there are cases where the other libraries are fine (e.g. if you have a bounded number
//! of thread and a medium-long interval between accesses).
//!
//! ¹If you want a super fast memory reclamation system, you should try NOP™, and not calling
//!  destructors.
//!
//! ## Internals
//!
//! It based on hazard pointers, although there are several differences. The idea is essentially
//! that the system keeps track of some number of "hazards". As long as a hazard protects some
//! object, the object cannot be deleted.
//!
//! Once in a while, a thread performs a garbage collection by scanning the hazards and finding the
//! objects not currently protected by any hazard. These objects are then deleted.
//!
//! To improve performance, we use a layered approach: Both garbage (objects to be deleted
//! eventually) and hazards are cached thread locally. This reduces the amount of atomic operations
//! and cache misses.
//!
//! ## Garbage collection
//!
//! Garbage collection of the concurrently managed object is done automatically between every `n`
//! frees where `n` is chosen from some probability distribution.
//!
//! Note that a garbage collection cycle might not clear all objects. For example, some objects
//! could be protected by hazards. Others might not have been exported from the thread-local cache
//! yet.
//!
//! ## Performance
//!
//! It is worth noting that atomic reads through this library usually requires three atomic CPU
//! instruction, this means that if you are traversing a list or something like that, this library
//! might not be for you.

#![feature(thread_local_state)]
#![deny(missing_docs)]

#[macro_use]
extern crate lazy_static;
extern crate parking_lot;
extern crate rand;

mod atomic;
mod garbage;
mod global;
mod guard;
mod hazard;
mod local;
mod mpsc;
pub mod sync;

pub use atomic::Atomic;
pub use guard::Guard;

use std::mem;
use garbage::Garbage;

/// Attempt to collect garbage.
///
/// This function does two things:
///
/// 1. Export garbage from current thread to the global queue.
/// 2. Collect all the garbage and run destructors on the unused items.
///
/// If another thread is currently doing 2., it will be skipped. This makes it different from
/// `conc::gc()`, which will block.
///
/// If 2. fails (that is, another thread is garbage collecting), `Err(())` is returned. Otherwise
/// `Ok(())` is returned.
///
/// # Use case
///
/// Note that it is not necessary to call this manually, it will do so automatically after some
/// time has passed.
///
/// However, it can be nice if you have just trashed a very memory-hungry item in the current
/// thread, and want to attempt to GC it.
///
/// # Other threads
///
/// This cannot collect un-propagated garbage accumulated locally in other threads. This will only
/// attempt to collect the accumulated local and global (propagated) garbage.
pub fn try_gc() -> Result<(), ()> {
    // Export the local garbage to ensure that the garbage of the current thread gets collected.
    local::export_garbage();
    // Run the global GC.
    global::try_gc()
}

/// Collect garbage.
///
/// This function does two things:
///
/// 1. Export garbage from current thread to the global queue.
/// 2. Collect all the garbage and run destructors on the unused items.
///
/// If another thread is currently doing 2., it will block until it can be done. This makes it
/// different from `conc::try_gc()`, which will skip the step.
///
/// # Use case
///
/// This is really only neccesary in one case: If you want to ensure that all the destructors of
/// inactive hazards in the current thread are run. If the destructors hold some special logic, you
/// want to execute, this will force the (inactive) ones to run these destructors and thus execute
/// the logic.
///
/// If you just want to reduce memory usage, you will probably be better off with `conc::try_gc()`.
///
/// # Other threads
///
/// This cannot collect un-propagated garbage accumulated locally in other threads. This will only
/// collect the accumulated local and global (propagated) garbage.
pub fn gc() {
    // Export the local garbage to ensure that the garbage of the current thread gets collected.
    local::export_garbage();
    // Try to garbage collect until it succeeds.
    while let Err(()) = global::try_gc() {}
}

/// Declare a pointer unreachable garbage to be deleted eventually.
///
/// This adds `ptr` to the queue of garbage, which eventually will be destroyed through its
/// destructor given in `dtor`. This is ensured to happen at some point _after_ the last guard
/// protecting the pointer is dropped.
///
/// It is legal for `ptr` to be invalidated by `dtor`, such that accessing it is undefined after
/// `dtor` has been run. This means that `dtor` can safely (there are exceptions, see below) run a
/// destructor of `ptr`'s data.
///
/// # Unreachability criterion
///
/// If you invalidate `ptr` in the destructor, it is extremely important that `ptr` is no longer
/// reachable from any data structure: It should be impossible to create _new_ guard representing
/// `ptr` from now on, as such thing can mean that new guards can be created after it is dropped
/// causing use-after-free.
pub fn add_garbage<T>(ptr: &'static T, dtor: fn(&'static T)) {
    local::add_garbage(unsafe {
        Garbage::new(ptr as *const T as *const u8 as *mut u8, mem::transmute(dtor))
    });
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{self, Read, Seek, Write};

use super::LogOffset;

use {
    parking_lot::Mutex,
    std::io::{Read, Seek, Write},
};

fn init_mu() -> Mutex<()> {
    Mutex::new(())
}

type MutexInit = fn() -> Mutex<()>;

static GLOBAL_FILE_LOCK: crate::Lazy<Mutex<()>, MutexInit> =
    crate::Lazy::new(init_mu);

fn pread_exact(
    file: &File,
    mut buf: &mut [u8],
    offset: LogOffset,
) -> io::Result<()> {
    let _lock = GLOBAL_FILE_LOCK.lock();

    let mut f = file.try_clone()?;

    let _ = f.seek(io::SeekFrom::Start(offset))?;

    while !buf.is_empty() {
        match f.read(buf) {
            Ok(0) => break,
            Ok(n) => {
                let tmp = buf;
                buf = &mut tmp[n..];
            }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    if !buf.is_empty() {
        Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "failed to fill whole buffer",
        ))
    } else {
        Ok(())
    }
}

fn pwrite_all(
    file: &File,
    mut buf: &[u8],
    offset: LogOffset,
) -> io::Result<()> {
    let _lock = GLOBAL_FILE_LOCK.lock();

    let mut f = file.try_clone()?;

    let _ = f.seek(io::SeekFrom::Start(offset))?;

    while !buf.is_empty() {
        match f.write(buf) {
            Ok(0) => {
                return Err(io::Error::new(
                    io::ErrorKind::WriteZero,
                    "failed to write whole buffer",
                ));
            }
            Ok(n) => buf = &buf[n..],
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}
<commit_msg>Fix function visibility<commit_after>use std::fs::File;
use std::io::{self, Read, Seek, Write};

use parking_lot::Mutex;

use super::LogOffset;

fn init_mu() -> Mutex<()> {
    Mutex::new(())
}

type MutexInit = fn() -> Mutex<()>;

static GLOBAL_FILE_LOCK: crate::Lazy<Mutex<()>, MutexInit> =
    crate::Lazy::new(init_mu);

pub(crate) fn pread_exact(
    file: &File,
    mut buf: &mut [u8],
    offset: LogOffset,
) -> io::Result<()> {
    let _lock = GLOBAL_FILE_LOCK.lock();

    let mut f = file.try_clone()?;

    let _ = f.seek(io::SeekFrom::Start(offset))?;

    while !buf.is_empty() {
        match f.read(buf) {
            Ok(0) => break,
            Ok(n) => {
                let tmp = buf;
                buf = &mut tmp[n..];
            }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    if !buf.is_empty() {
        Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "failed to fill whole buffer",
        ))
    } else {
        Ok(())
    }
}

pub(crate) fn pwrite_all(
    file: &File,
    mut buf: &[u8],
    offset: LogOffset,
) -> io::Result<()> {
    let _lock = GLOBAL_FILE_LOCK.lock();

    let mut f = file.try_clone()?;

    let _ = f.seek(io::SeekFrom::Start(offset))?;

    while !buf.is_empty() {
        match f.write(buf) {
            Ok(0) => {
                return Err(io::Error::new(
                    io::ErrorKind::WriteZero,
                    "failed to write whole buffer",
                ));
            }
            Ok(n) => buf = &buf[n..],
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 2 optional args:
//   square image size, defaults to 80_u
//   output path, default is "" (no output), "-" means stdout
//
//  in the shootout, they use 16000 as image size
//
//  writes pbm image to output path

extern mod std;
use io::WriterUtil;
use std::oldmap::HashMap;

struct cmplx {
    re: f64,
    im: f64
}

impl ops::Mul<cmplx,cmplx> for cmplx {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl ops::Add<cmplx,cmplx> for cmplx {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

struct Line {i: uint, b: ~[u8]}

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx) -> bool
{
    let mut z = cmplx {re: 0f64, im: 0f64};
    let mut i = 0;
    let mut in = true;
    while i < 50 {
        z = z*z + x;
        if cabs(z) >= 4f64 {
            in = false;
            break;
        }
        i += 1;
    }
    in
}

fn fillbyte(x: cmplx, incr: f64) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint) -> Line
{
    let mut crv = ~[];
    let incr = 2f64/(size as f64);
    let y = incr*(i as f64) - 1f64;
    let xincr = 8f64*incr;
    for uint::range(0_u, size/8_u) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5f64, im: y};
        crv.push(fillbyte(x, incr));
    };
    Line {i:i, b:crv}
}

struct Devnull();

impl io::Writer for Devnull {
    fn write(&self, _b: &[const u8]) {}
    fn seek(&self, _i: int, _s: io::SeekStyle) {}
    fn tell(&self) -> uint {0_u}
    fn flush(&self) -> int {0}
    fn get_type(&self) -> io::WriterType { io::File }
}

fn writer(path: ~str, pport: pipes::Port<Line>, size: uint)
{
    let cout: io::Writer = match path {
        ~"" => {
            Devnull as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line(~"P4");
    cout.write_line(fmt!("%u %u", size, size));
    let lines: HashMap<uint, ~[u8]> = HashMap();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = pport.recv();
        if aline.i == done {
            debug!("W %u", aline.i);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                if lines.contains_key(&prev) {
                    debug!("WS %u", prev);
                    cout.write(lines.get(&prev));
                    done += 1_u;
                    lines.remove(&prev);
                    prev += 1_u;
                }
                else {
                    break
                }
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, copy aline.b);    // FIXME: bad for perf
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000"]
    } else {
        args
    };

    let path = if vec::len(args) < 3_u { ~"" }
    else { copy args[2] };  // FIXME: bad for perf

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let (pport, pchan) = pipes::stream();
    let pchan = pipes::SharedChan(pchan);
    for uint::range(0_u, size) |j| {
        let cchan = pchan.clone();
        do task::spawn || { cchan.send(chanmb(j, size)) };
    };
    writer(path, pport, size);
}
<commit_msg>move shootout-mandelbrot to LinearMap, add depth argument<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   output path, default is "" (no output), "-" means stdout
//   depth (max iterations per pixel), defaults to 50_u
//
//  in the shootout, they use 16000 as image size, 50 as depth,
//   and write to stdout:
//
//  ./shootout_mandelbrot 16000 "-" 50 > /tmp/mandel.pbm
//
//  writes pbm image to output path

use io::WriterUtil;
use core::hashmap::linear::LinearMap;

struct cmplx {
    re: f64,
    im: f64
}

impl ops::Mul<cmplx,cmplx> for cmplx {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl ops::Add<cmplx,cmplx> for cmplx {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

struct Line {i: uint, b: ~[u8]}

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx, depth: uint) -> bool
{
    let mut z = x;
    let mut i = 0;
    while i < depth {
        if cabs(z) >= 4_f64 {
            return false;
        }
        z = z*z + x;
        i += 1;
    }
    true
}

fn fillbyte(x: cmplx, incr: f64, depth: uint) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z, depth) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, depth: uint) -> Line
{
    let bsize = size/8_u;
    let mut crv = vec::with_capacity(bsize);
    let incr = 2_f64/(size as f64);
    let y = incr*(i as f64) - 1_f64;
    let xincr = 8_f64*incr;
    for uint::range(0_u, bsize) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5_f64, im: y};
        crv.push(fillbyte(x, incr, depth));
    };
    Line {i:i, b:crv}
}

struct Devnull();

impl io::Writer for Devnull {
    fn write(&self, _b: &[const u8]) {}
    fn seek(&self, _i: int, _s: io::SeekStyle) {}
    fn tell(&self) -> uint {0_u}
    fn flush(&self) -> int {0}
    fn get_type(&self) -> io::WriterType { io::File }
}

fn writer(path: ~str, pport: pipes::Port<Line>, size: uint)
{
    let cout: io::Writer = match path {
        ~"" => {
            Devnull as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line("P4");
    cout.write_line(fmt!("%u %u", size, size));
    let mut lines: LinearMap<uint, Line> = LinearMap::new();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = pport.recv();
        if aline.i == done {
            debug!("W %u", done);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                match lines.pop(&prev) {
                    Some(pl) => {
                        debug!("WS %u", prev);
                        cout.write(pl.b);
                        done += 1_u;
                        prev += 1_u;
                    }
                    None => break
                };
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline);
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"50"]
    } else {
        args
    };

    let depth = if vec::len(args) < 4_u { 50_u }
    else { uint::from_str(args[3]).get() };

    let path = if vec::len(args) < 3_u { ~"" }
    else { copy args[2] };  // FIXME: bad for perf

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let (pport, pchan) = pipes::stream();
    let pchan = pipes::SharedChan(pchan);
    for uint::range(0_u, size) |j| {
        let cchan = pchan.clone();
        do task::spawn { cchan.send(chanmb(j, size, depth)) };
    };
    writer(path, pport, size);
}
<|endoftext|>


<commit_before><commit_msg>formatting<commit_after><|endoftext|>


<commit_before>pub const MASK_OUT_X_EA: u32 = 0b1111_0001_1100_0000; // masks out X and Y register bits, plus mode (????xxx???mmmyyy)
pub const MASK_OUT_EA: u32 = 0b1111111111000000;   // masks out Y register bits, plus mode (??????????mmmyyy)
pub const MASK_OUT_EA_EA: u32 = 0b1111_0000_0000_0000;   // masks out destination(reg (x), mode (d)), source(reg(y) and mode(m)) (????xxxdddmmmyyy)

pub const EA_DATA_REGISTER_DIRECT: u16 =      0b1000_0000_0000;
pub const EA_ADDRESS_REGISTER_DIRECT: u16 =   0b0100_0000_0000;
pub const EA_ADDRESS_REGISTER_INDIRECT: u16 = 0b0010_0000_0000;
pub const EA_ARI_POSTINCREMENT: u16 =         0b0001_0000_0000;
pub const EA_ARI_PREDECREMENT: u16 =          0b0000_1000_0000;
pub const EA_ARI_DISPLACEMENT: u16 =          0b0000_0100_0000;
pub const EA_ARI_INDEX: u16 =                 0b0000_0010_0000;
pub const EA_ABSOLUTE_SHORT: u16 =            0b0000_0001_0000;
pub const EA_ABSOLUTE_LONG: u16 =             0b0000_0000_1000;
pub const EA_IMMEDIATE: u16 =                 0b0000_0000_0100;
pub const EA_PC_DISPLACEMENT: u16 =           0b0000_0000_0010;
pub const EA_PC_INDEX: u16 =                  0b0000_0000_0001;

pub const EA_ALL: u16 = 0xfff;
pub const EA_ALL_EXCEPT_AN: u16 = EA_ALL & !EA_ADDRESS_REGISTER_DIRECT;
pub const EA_ALTERABLE: u16 = EA_DATA_REGISTER_DIRECT
                        | EA_ADDRESS_REGISTER_DIRECT
                        | EA_ADDRESS_REGISTER_INDIRECT
                        | EA_ARI_POSTINCREMENT
                        | EA_ARI_PREDECREMENT
                        | EA_ARI_DISPLACEMENT
                        | EA_ARI_INDEX;
pub const EA_CONTROL: u16 = EA_ADDRESS_REGISTER_INDIRECT
                        | EA_ARI_DISPLACEMENT
                        | EA_ARI_INDEX
                        | EA_ABSOLUTE_SHORT
                        | EA_ABSOLUTE_LONG
                        | EA_PC_DISPLACEMENT
                        | EA_PC_INDEX;
pub const EA_CONTROL_ALTERABLE_OR_PD: u16 = EA_CONTROL & EA_ALTERABLE | EA_ARI_PREDECREMENT;
pub const EA_CONTROL_OR_PI: u16 = EA_CONTROL | EA_ARI_POSTINCREMENT;
pub const EA_DATA: u16 = EA_ALL & !EA_ADDRESS_REGISTER_DIRECT;
pub const EA_DATA_ALTERABLE: u16 = EA_DATA & EA_ALTERABLE;
pub const EA_MEMORY: u16 = EA_ALL & !(EA_DATA_REGISTER_DIRECT | EA_ADDRESS_REGISTER_DIRECT);
pub const EA_MEMORY_ALTERABLE: u16 = EA_MEMORY & EA_ALTERABLE;
#[allow(dead_code)]
pub const EA_NONE: u16 = 0x000;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ea_all_except_an() {
        assert_eq!(EA_ALL_EXCEPT_AN & EA_ADDRESS_REGISTER_DIRECT, 0);
    }
    #[test]
    fn ea_alterable() {
        assert_eq!(EA_ALTERABLE & (EA_IMMEDIATE|EA_PC_DISPLACEMENT|EA_PC_INDEX), 0);
    }
    #[test]
    fn ea_control() {
        assert_eq!(EA_CONTROL, 0x27b);
    }
    #[test]
    fn ea_control_alterable_or_pd() {
        assert_eq!(EA_CONTROL_ALTERABLE_OR_PD & EA_ARI_PREDECREMENT, EA_ARI_PREDECREMENT);
    }
    #[test]
    fn ea_control_or_pi() {
        assert_eq!(EA_CONTROL_OR_PI & EA_ARI_POSTINCREMENT, EA_ARI_POSTINCREMENT);
    }
    #[test]
    fn ea_data() {
        assert_eq!(EA_DATA & (EA_ADDRESS_REGISTER_DIRECT | EA_IMMEDIATE), 0);
    }
    #[test]
    fn ea_data_alterable() {
        assert_eq!(EA_DATA_ALTERABLE, EA_DATA & EA_ALTERABLE);
    }
    #[test]
    fn ea_memory_alterable() {
        assert_eq!(EA_MEMORY_ALTERABLE & (EA_DATA_REGISTER_DIRECT | EA_ADDRESS_REGISTER_DIRECT), 0);
    }
}<commit_msg>Fix EA_ALTERABLE addressing modes<commit_after>pub const MASK_OUT_X_EA: u32 = 0b1111_0001_1100_0000; // masks out X and Y register bits, plus mode (????xxx???mmmyyy)
pub const MASK_OUT_EA: u32 = 0b1111111111000000;   // masks out Y register bits, plus mode (??????????mmmyyy)
pub const MASK_OUT_EA_EA: u32 = 0b1111_0000_0000_0000;   // masks out destination(reg (x), mode (d)), source(reg(y) and mode(m)) (????xxxdddmmmyyy)

pub const EA_DATA_REGISTER_DIRECT: u16 =      0b1000_0000_0000;
pub const EA_ADDRESS_REGISTER_DIRECT: u16 =   0b0100_0000_0000;
pub const EA_ADDRESS_REGISTER_INDIRECT: u16 = 0b0010_0000_0000;
pub const EA_ARI_POSTINCREMENT: u16 =         0b0001_0000_0000;
pub const EA_ARI_PREDECREMENT: u16 =          0b0000_1000_0000;
pub const EA_ARI_DISPLACEMENT: u16 =          0b0000_0100_0000;
pub const EA_ARI_INDEX: u16 =                 0b0000_0010_0000;
pub const EA_ABSOLUTE_SHORT: u16 =            0b0000_0001_0000;
pub const EA_ABSOLUTE_LONG: u16 =             0b0000_0000_1000;
pub const EA_IMMEDIATE: u16 =                 0b0000_0000_0100;
pub const EA_PC_DISPLACEMENT: u16 =           0b0000_0000_0010;
pub const EA_PC_INDEX: u16 =                  0b0000_0000_0001;

pub const EA_ALL: u16 = 0xfff;
pub const EA_ALL_EXCEPT_AN: u16 = EA_ALL & !EA_ADDRESS_REGISTER_DIRECT;
// despite what is claimed in MC68000PRM section 2.3 EFFECTIVE ADDRESSING MODE SUMMARY,
// Absolute Short and Long modes are in fact alterable, as is evident when looking the allowed
// addressing modes of any instruction stating only alterable (or data/memory alterable)
// such as ADDQ.
pub const EA_ALTERABLE: u16 = EA_DATA_REGISTER_DIRECT
                        | EA_ADDRESS_REGISTER_DIRECT
                        | EA_ADDRESS_REGISTER_INDIRECT
                        | EA_ARI_POSTINCREMENT
                        | EA_ARI_PREDECREMENT
                        | EA_ARI_DISPLACEMENT
                        | EA_ARI_INDEX
                        | EA_ABSOLUTE_SHORT
                        | EA_ABSOLUTE_LONG;
pub const EA_CONTROL: u16 = EA_ADDRESS_REGISTER_INDIRECT
                        | EA_ARI_DISPLACEMENT
                        | EA_ARI_INDEX
                        | EA_ABSOLUTE_SHORT
                        | EA_ABSOLUTE_LONG
                        | EA_PC_DISPLACEMENT
                        | EA_PC_INDEX;
pub const EA_CONTROL_ALTERABLE_OR_PD: u16 = EA_CONTROL & EA_ALTERABLE | EA_ARI_PREDECREMENT;
pub const EA_CONTROL_OR_PI: u16 = EA_CONTROL | EA_ARI_POSTINCREMENT;
pub const EA_DATA: u16 = EA_ALL & !EA_ADDRESS_REGISTER_DIRECT;
pub const EA_DATA_ALTERABLE: u16 = EA_DATA & EA_ALTERABLE;
pub const EA_MEMORY: u16 = EA_ALL & !(EA_DATA_REGISTER_DIRECT | EA_ADDRESS_REGISTER_DIRECT);
pub const EA_MEMORY_ALTERABLE: u16 = EA_MEMORY & EA_ALTERABLE;
#[allow(dead_code)]
pub const EA_NONE: u16 = 0x000;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ea_all_except_an() {
        assert_eq!(EA_ALL_EXCEPT_AN & EA_ADDRESS_REGISTER_DIRECT, 0);
    }
    #[test]
    fn ea_alterable() {
        assert_eq!(EA_ALTERABLE & (EA_IMMEDIATE|EA_PC_DISPLACEMENT|EA_PC_INDEX), 0);
    }
    #[test]
    fn ea_control() {
        assert_eq!(EA_CONTROL, 0x27b);
    }
    #[test]
    fn ea_control_alterable_or_pd() {
        assert_eq!(EA_CONTROL_ALTERABLE_OR_PD & EA_ARI_PREDECREMENT, EA_ARI_PREDECREMENT);
    }
    #[test]
    fn ea_control_or_pi() {
        assert_eq!(EA_CONTROL_OR_PI & EA_ARI_POSTINCREMENT, EA_ARI_POSTINCREMENT);
    }
    #[test]
    fn ea_data() {
        assert_eq!(EA_DATA & (EA_ADDRESS_REGISTER_DIRECT | EA_IMMEDIATE), 0);
    }
    #[test]
    fn ea_data_alterable() {
        assert_eq!(EA_DATA_ALTERABLE, EA_DATA & EA_ALTERABLE);
    }
    #[test]
    fn ea_memory_alterable() {
        assert_eq!(EA_MEMORY_ALTERABLE & (EA_DATA_REGISTER_DIRECT | EA_ADDRESS_REGISTER_DIRECT), 0);
    }
}<|endoftext|>


<commit_before><commit_msg>Fix socket leak when closing a service connection managed by the SessionManager<commit_after><|endoftext|>


<commit_before><commit_msg>Implement adc_hl_ss instruction<commit_after><|endoftext|>


<commit_before><commit_msg>Add test for FUTEX_*_BITSET.<commit_after><|endoftext|>


<commit_before><commit_msg>Fix test `prelude_test`, run test under valid features, use new test suite interface<commit_after><|endoftext|>


<commit_before><commit_msg>Do call `projects().list()`<commit_after><|endoftext|>


<commit_before><commit_msg>Extract more methods for profiling<commit_after><|endoftext|>


<commit_before>// Copyright 2016 Revolution Solid & Contributors.
// author(s): carlos-lopez-garces, sysnett
// rust-monster is licensed under an MIT License.

//! GA Selectors
//!
//! A selector represents and performs a method of selection.
//!
//! Selection is the action of choosing solutions (individuals) of the current
//! generation that will create offspring for the next generation.
//!
//! Selectors represent and perform a different method of selection each. The
//! expectation is that the offspring solutions be fitter than their selected
//! parents. For this reason, many of the selectors tend to choose the fitter
//! most of the time. However, many of them acknowledge the need for selecting
//! less fit solutions, too: A genetic operator (crossover, mutation) used on
//! suboptimal solutions may sometimes produce a solution that is fitter than
//! those that could be produced by optimal ones.
//!
//! Available selectors:
//!
//! `GARankSelector`
//! `GAUniformSelector`
//! `GARouletteWheelSelector`
//! `GATournamentSelector`
//!
//! # Examples
use super::ga_core::GASolution;
use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
use super::ga_random::{GARandomCtx};
use std::cmp;

/// Selector Trait
///
/// Interface to Selection Schemes
pub trait GASelector<'a, T: GASolution>
{
    /// Update internal state.
    /// Some selectors implement an empty update().
    fn update(&mut self, population: &mut GAPopulation<T>) {}

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T;
}

///
///
/// Selectors are configured, at the time of creation, with the type of score
/// {RAW, SCALED} they will use to perform selections. The type of score
/// ultimately determines the function that will be invoked on the `GASolution`
/// to obtain the score value of the configured type. `GAScoreTypeBasedSelection`
/// objects provide a unified interface to the different score functions of a
/// `GASolution`. Selectors use these objects to obtain score values of the
/// configured type, without choosin
pub trait GAScoreTypeBasedSelection<T: GASolution>
{
    fn score(&self, individual: &T) -> f32;

    fn population_sort_basis(&self) -> GAPopulationSortBasis;

    fn max_score(&self, population: &GAPopulation<T>) -> f32;

    fn min_score(&self, population: &GAPopulation<T>) -> f32;
}

pub struct GARawScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GARawScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.score()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Raw
    }

    fn max_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.best_by_raw_score())
    }

    fn min_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.worst_by_raw_score())
    }
}

pub struct GAScaledScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GAScaledScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.fitness()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Scaled
    }

    fn max_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.best_by_scaled_score())
    }

    fn min_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.worst_by_scaled_score())
    }
}

// GASolution-s live as long as the population. Lifetime 'a is bound to the
// population borrowed by the 'population' member, as well as to the enclosed
// GASolution-s, because they share the same memory.
pub struct GARankSelector<'a, T: 'a + GASolution>
{
    // Q: How to make this member common to all GASelectors?
    // Q: Will 'mut' make things more complex? When the rank selector is in
    //    scope and borrowing the population, no other objects will be able
    //    to borrow it, not even in non-mut mode according to the rules.
    // Selectors modify populations (they sort them, for instance), so the
    // reference must be 'mut'.
    // population: &'a mut GAPopulation<T>,

    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>
}

impl<'a, T: GASolution> GARankSelector<'a, T>
{
    pub fn new(s: &'a GAScoreTypeBasedSelection<T>) -> GARankSelector<'a, T>
    {
        GARankSelector
        {
            score_selection: s
        }
    }
}

// TODO: DOC.
impl<'a, T: GASolution> GASelector<'a, T> for GARankSelector<'a, T>
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        population.sort();
    }

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        // TODO: Confirm assumption that population has 1 individual at least.
        // Number of individuals that share best score.
        let mut best_count = 1;

        // This is not a move, but a copy.
        let population_sort_basis = self.score_selection.population_sort_basis();

        // TODO: Use best() when implemented.
        // Q: Should individual() (ith_best()) return an optional or
        //    does it guarantee that it will always return something valid?
        //    What if the population is still empty?
        let best_score: f32 = self.score_selection.score(population.individual(0, population_sort_basis));

        // Skip 0th best.
        for i in 1..population.size()
        {
            if self.score_selection.score(population.individual(i, population_sort_basis)) != best_score
            {
                break;
            }

            best_count = best_count + 1;
        }

        // Select any individual from those that share best score.
        population.individual(rng_ctx.gen_range(0, best_count), population_sort_basis)
    }
}

pub struct GAUniformSelector;

impl GAUniformSelector
{
    pub fn new() -> GAUniformSelector
    {
        GAUniformSelector
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GAUniformSelector
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        // Need to sort first, because GAPopulation.individual() draws individuals
        // from the sorted lists.
        population.sort();
    }

    // Select any individual at random.
    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        // Since selection is at random, it doesn't matter where the individual
        // is drawn from, the Raw/score-sorted or the Scaled/fitness-sorted list.
        population.individual(
            rng_ctx.gen_range(0, population.size()),
            GAPopulationSortBasis::Raw)
    }
}

pub struct GARouletteWheelSelector<'a, T: 'a + GASolution>
{
    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>,

    wheel_proportions: Vec<f32>,

    // TODO: Remove if not useful.
    wheel_is_dirty: bool
}

impl<'a, T: GASolution> GARouletteWheelSelector<'a, T>
{
    // TODO: Check s's lifetime.
    pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GARouletteWheelSelector<'a, T>
    {
        // TODO: Comment doesn't look correct.
        // vec![] borrows references (invocation of size() is through *p, or so the
        // compiler says); since p has already been borrowed as a mutable reference
        // (no data races allowed), p.size() can't be passed to vec![].
        let wheel_size = p_size;

        GARouletteWheelSelector
        {
            score_selection: s,
            wheel_proportions: vec![0.0; wheel_size],
            wheel_is_dirty: false
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GARouletteWheelSelector<'a, T>
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        // TODO: Can a population grow? If it can, need to resize the wheel.
        if population.size() != self.wheel_proportions.len()
        {
            self.wheel_proportions.resize(population.size(), 0.0);
        }

        population.sort();

        let wheel_slots = self.wheel_proportions.len();
        let max_score = self.score_selection.max_score(population);
        let min_score = self.score_selection.min_score(population);

        if max_score == min_score
        {
            // Upper bound is excluded.
            for i in 0 .. wheel_slots
            {
                self.wheel_proportions[i] = ((i+1) as f32)/(wheel_slots as f32);
            }
        }
        else if (max_score > 0.0 && min_score >= 0.0)
                 || (max_score <= 0.0 && min_score < 0.0)
        {
            // This is not a move, but a copy.
            let population_sort_basis = self.score_selection.population_sort_basis();

            match population.order()
            {
                GAPopulationSortOrder::HighIsBest
                =>  {
                        self.wheel_proportions[0]
                          = self.score_selection.score(
                              population.individual(0, population_sort_basis));

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = self.score_selection.score(
                                  population.individual(i, population_sort_basis))
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    },
                GAPopulationSortOrder::LowIsBest
                =>  {
                        self.wheel_proportions[0]
                          = -self.score_selection.score(
                               population.individual(0, population_sort_basis))
                            + max_score + min_score;

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = -self.score_selection.score(
                                   population.individual(i, population_sort_basis))
                                + max_score + min_score
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    }
            }
        }
        else
        {
            // TODO: Raise error.
        }
    }

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        // TODO: Cache this value? Or Vec already caches it?
        let wheel_slots = self.wheel_proportions.len();
        let cutoff = rng_ctx.gen::<f32>();
        let mut lower = 0;
        let mut upper = wheel_slots-1;
        let mut i;

        while upper > lower
        {
            i = lower + (upper-lower)/2;

            assert!(i < wheel_slots);

            if self.wheel_proportions[i] > cutoff
            {
                if i > 0
                {
                    upper = i-1;
                }
                else
                {
                    upper = 0;
                }
            }
            else
            {
                lower = i+1;
            }
        }

        lower = cmp::min(wheel_slots-1, lower);

        population.individual(lower, self.score_selection.population_sort_basis())
    }
}

pub struct GATournamentSelector<'a, T: 'a + GASolution>
{
    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>,
    roulette_wheel_selector: GARouletteWheelSelector<'a, T>,
}

impl<'a, T: GASolution> GATournamentSelector<'a, T>
{
    // TODO: Check s's lifetime.
    pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GATournamentSelector<'a, T>
    {
        GATournamentSelector
        {
            score_selection: s,
            roulette_wheel_selector: GARouletteWheelSelector::new(s, p_size)
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GATournamentSelector<'a, T>
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        self.roulette_wheel_selector.update(population);
    }

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        let low_score_individual;
        let high_score_individual;
        let individual1;
        let individual2;

        individual1 = self.roulette_wheel_selector.select(population, rng_ctx);
        individual2 = self.roulette_wheel_selector.select(population, rng_ctx);

        if self.score_selection.score(individual1)
           >= self.score_selection.score(individual2)
        {
            low_score_individual = individual2;
            high_score_individual = individual1;
        }
        else
        {
            low_score_individual = individual1;
            high_score_individual = individual2;
        }

        match population.order()
        {
            GAPopulationSortOrder::HighIsBest => high_score_individual,
            GAPopulationSortOrder::LowIsBest  => low_score_individual
        }
    }
}
<commit_msg>Add a little bit of documentation. Clean up.<commit_after>// Copyright 2016 Revolution Solid & Contributors.
// author(s): carlos-lopez-garces, sysnett
// rust-monster is licensed under an MIT License.

//! GA Selectors
//!
//! A selector represents and performs a method of selection.
//!
//! Selection is the action of choosing solutions (individuals) of the current
//! generation that will create offspring for the next generation.
//!
//! Selectors represent and perform a different method of selection each. The
//! expectation is that the offspring solutions be fitter than their selected
//! parents. For this reason, many of the selectors tend to choose the fitter
//! most of the time. However, many of them acknowledge the need for selecting
//! less fit solutions, too: A genetic operator (crossover, mutation) used on
//! suboptimal solutions may sometimes produce a solution that is fitter than
//! those that could be produced by optimal ones.
//!
//! Available selectors:
//!
//! `GARankSelector`
//! `GAUniformSelector`
//! `GARouletteWheelSelector`
//! `GATournamentSelector`
//!
//! # Examples
use super::ga_core::GASolution;
use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
use super::ga_random::{GARandomCtx};
use std::cmp;

/// Selector trait.
///
/// Selector common interface. Each selector implements a different method
/// of selection and keeps and manages its own internal state.
pub trait GASelector<'a, T: GASolution>
{
    /// Update internal state.
    ///
    /// NOOP default implementation for selectors that don't keep internal state.
    fn update(&mut self, population: &mut GAPopulation<T>) {}

    /// Select an individual from the population.
    ///
    /// Each selector implements a different method of selection. Randomization
    /// is a key aspect of all methods.
    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T;
}

/// Selection score type basis.
///
/// Selectors are configured, at the time of creation, with the type of score
/// {RAW, SCALED} they will use to perform selections. The type of score
/// ultimately determines the function that will be invoked on the `GASolution`
/// to obtain the score value of the configured type. `GAScoreTypeBasedSelection`
/// objects provide a unified interface to the different score functions of a
/// `GASolution`. Selectors use these objects to obtain score values of the
/// configured type, without explicitly choosing between them based on
/// `GAPopulationSortBasis`.
pub trait GAScoreTypeBasedSelection<T: GASolution>
{
    fn score(&self, individual: &T) -> f32;

    fn population_sort_basis(&self) -> GAPopulationSortBasis;

    fn max_score(&self, population: &GAPopulation<T>) -> f32;

    fn min_score(&self, population: &GAPopulation<T>) -> f32;
}

/// Selection based on RAW score.
pub struct GARawScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GARawScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.score()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Raw
    }

    fn max_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.best_by_raw_score())
    }

    fn min_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.worst_by_raw_score())
    }
}

/// Selection based on SCALED score.
pub struct GAScaledScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GAScaledScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.fitness()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Scaled
    }

    fn max_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.best_by_scaled_score())
    }

    fn min_score(&self, population: &GAPopulation<T>) -> f32
    {
        self.score(population.worst_by_scaled_score())
    }
}

/// Rank selector.
///
/// Select the best individual of the population. If more than 1 share the
/// best score, choose 1 among them at random.
pub struct GARankSelector<'a, T: 'a + GASolution>
{
    score_selection: &'a GAScoreTypeBasedSelection<T>
}

impl<'a, T: GASolution> GARankSelector<'a, T>
{
    pub fn new(s: &'a GAScoreTypeBasedSelection<T>) -> GARankSelector<'a, T>
    {
        GARankSelector
        {
            score_selection: s
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GARankSelector<'a, T>
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        population.sort();
    }

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        // TODO: Confirm assumption that population has 1 individual at least.
        // Number of individuals that share best score.
        let mut best_count = 1;

        // This is not a move, but a copy.
        let population_sort_basis = self.score_selection.population_sort_basis();

        // All individuals that share the best score will be considered for selection.
        let best_score: f32 = self.score_selection.max_score(population);

        // Skip 0th best. It is known that it has the best score.
        for i in 1..population.size()
        {
            if self.score_selection.score(population.individual(i, population_sort_basis)) != best_score
            {
                break;
            }

            best_count = best_count + 1;
        }

        // Select any individual from those that share the best score.
        population.individual(rng_ctx.gen_range(0, best_count), population_sort_basis)
    }
}

pub struct GAUniformSelector;

/// Uniform selector.
///
/// Select an individual at random, with equal probability.
impl GAUniformSelector
{
    pub fn new() -> GAUniformSelector
    {
        GAUniformSelector
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GAUniformSelector
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        // Need to sort first, because GAPopulation.individual() draws individuals
        // from the sorted lists.
        population.sort();
    }

    // Select any individual at random.
    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        // Since selection is at random, it doesn't matter where the individual
        // is drawn from, the Raw/score-sorted or the Scaled/fitness-sorted list.
        population.individual(
            rng_ctx.gen_range(0, population.size()),
            GAPopulationSortBasis::Raw)
    }
}

/// Roulette Wheel selector.
pub struct GARouletteWheelSelector<'a, T: 'a + GASolution>
{
    score_selection: &'a GAScoreTypeBasedSelection<T>,

    wheel_proportions: Vec<f32>,
}

impl<'a, T: GASolution> GARouletteWheelSelector<'a, T>
{
    pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GARouletteWheelSelector<'a, T>
    {
        // TODO: Comment doesn't look correct.
        // vec![] borrows references (invocation of size() is through *p, or so the
        // compiler says); since p has already been borrowed as a mutable reference
        // (no data races allowed), p.size() can't be passed to vec![].
        let wheel_size = p_size;

        GARouletteWheelSelector
        {
            score_selection: s,
            wheel_proportions: vec![0.0; wheel_size],
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GARouletteWheelSelector<'a, T>
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        if population.size() != self.wheel_proportions.len()
        {
            self.wheel_proportions.resize(population.size(), 0.0);
        }

        population.sort();

        let wheel_slots = self.wheel_proportions.len();
        let max_score = self.score_selection.max_score(population);
        let min_score = self.score_selection.min_score(population);

        if max_score == min_score
        {
            // Upper bound is excluded.
            for i in 0 .. wheel_slots
            {
                self.wheel_proportions[i] = ((i+1) as f32)/(wheel_slots as f32);
            }
        }
        else if (max_score > 0.0 && min_score >= 0.0)
                 || (max_score <= 0.0 && min_score < 0.0)
        {
            // This is not a move, but a copy.
            let population_sort_basis = self.score_selection.population_sort_basis();

            match population.order()
            {
                GAPopulationSortOrder::HighIsBest
                =>  {
                        self.wheel_proportions[0]
                          = self.score_selection.score(
                              population.individual(0, population_sort_basis));

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = self.score_selection.score(
                                  population.individual(i, population_sort_basis))
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    },
                GAPopulationSortOrder::LowIsBest
                =>  {
                        self.wheel_proportions[0]
                          = -self.score_selection.score(
                               population.individual(0, population_sort_basis))
                            + max_score + min_score;

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = -self.score_selection.score(
                                   population.individual(i, population_sort_basis))
                                + max_score + min_score
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    }
            }
        }
        else
        {
            // TODO: Raise error.
        }
    }

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        let wheel_slots = self.wheel_proportions.len();
        let cutoff = rng_ctx.gen::<f32>();
        let mut lower = 0;
        let mut upper = wheel_slots-1;
        let mut i;

        while upper > lower
        {
            i = lower + (upper-lower)/2;

            assert!(i < wheel_slots);

            if self.wheel_proportions[i] > cutoff
            {
                if i > 0
                {
                    upper = i-1;
                }
                else
                {
                    upper = 0;
                }
            }
            else
            {
                lower = i+1;
            }
        }

        lower = cmp::min(wheel_slots-1, lower);

        population.individual(lower, self.score_selection.population_sort_basis())
    }
}

/// Tournament selector.
///
/// Select 2 individuals using Roulette Wheel selection and select the best of the 2.
pub struct GATournamentSelector<'a, T: 'a + GASolution>
{
    score_selection: &'a GAScoreTypeBasedSelection<T>,
    roulette_wheel_selector: GARouletteWheelSelector<'a, T>,
}

impl<'a, T: GASolution> GATournamentSelector<'a, T>
{
    pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GATournamentSelector<'a, T>
    {
        GATournamentSelector
        {
            score_selection: s,
            roulette_wheel_selector: GARouletteWheelSelector::new(s, p_size)
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GATournamentSelector<'a, T>
{
    fn update(&mut self, population: &mut GAPopulation<T>)
    {
        self.roulette_wheel_selector.update(population);
    }

    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
    {
        let low_score_individual;
        let high_score_individual;
        let individual1;
        let individual2;

        individual1 = self.roulette_wheel_selector.select(population, rng_ctx);
        individual2 = self.roulette_wheel_selector.select(population, rng_ctx);

        if self.score_selection.score(individual1)
           >= self.score_selection.score(individual2)
        {
            low_score_individual = individual2;
            high_score_individual = individual1;
        }
        else
        {
            low_score_individual = individual1;
            high_score_individual = individual2;
        }

        match population.order()
        {
            GAPopulationSortOrder::HighIsBest => high_score_individual,
            GAPopulationSortOrder::LowIsBest  => low_score_individual
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp;

use ctxt::SemContext;

use gc::Address;
use gc::root::IndirectObj;
use gc::swiper::CARD_SIZE;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{Card, CrossingEntry, CrossingMap};
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;

use mem;
use object::Obj;
use os::{self, ProtType};
use timer::{in_ms, Timer};

pub struct MinorCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    young: &'a YoungGen,
    old: &'a OldGen,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,

    free: Address,
    promotion_failed: bool,
}

impl<'a, 'ast> MinorCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [IndirectObj],
    ) -> MinorCollector<'a, 'ast> {
        MinorCollector {
            ctxt: ctxt,
            young: young,
            old: old,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            free: Address::null(),
            promotion_failed: false,
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let to_space = self.young.to_space();

        let mut scan = to_space.start;
        self.free = scan;

        self.unprotect_to_space();

        self.visit_roots();
        self.copy_dirty_cards();
        self.visit_copied_objects();
        self.young.swap_spaces(self.free);

        self.protect_to_space();

        timer.stop_with(|dur| {
            println!("GC: Minor GC ({} ms)", in_ms(dur));
        });
    }

    fn unprotect_to_space(&mut self) {
        // make memory writable again, so that we
        // can copy objects to the to-space.
        // Since this has some overhead, do it only in debug builds.

        if cfg!(debug_assertions) {
            let to_space = self.young.to_space();

            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::Writable,
            );
        }
    }

    fn protect_to_space(&mut self) {
        // Make from-space unaccessible both from read/write.
        // Since this has some overhead, do it only in debug builds.
        if cfg!(debug_assertions) {
            let to_space = self.young.to_space();
            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::None,
            );
        }
    }

    fn visit_roots(&mut self) {
        // detect all references from roots into young generation
        for &root in self.rootset {
            let root_ptr = root.get();

            if self.young.contains(Address::from_ptr(root_ptr)) {
                root.set(self.copy(root_ptr));
            }
        }
    }

    fn visit_copied_objects(&mut self) {
        // visit all fields in copied objects
        while scan < self.free {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if self.young.contains(Address::from_ptr(field_ptr)) {
                    field.set(self.copy(field_ptr));
                }
            });

            scan = scan.offset(object.size());
        }
    }

    // copy all references from old- into young-generation.
    fn copy_dirty_cards(&mut self) {
        self.card_table.visit_dirty(|card| {
            let crossing_entry = self.crossing_map.get(card);
            let card_start = self.old.address_from_card(card);
            let card_end = card_start.offset(CARD_SIZE);

            match crossing_entry {
                CrossingEntry::NoRefs => panic!("card dirty without any refs"),
                CrossingEntry::LeadingRefs(refs) => {
                    let mut ptr = card_start;

                    for _ in 0..refs {
                        let ind_ptr = IndirectObj::from_address(ptr);
                        let dir_ptr = ind_ptr.get();

                        if self.young.contains(Address::from_ptr(dir_ptr)) {
                            ind_ptr.set(self.copy(dir_ptr));
                        }

                        ptr = ptr.offset(mem::ptr_width() as usize);
                    }

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }

                CrossingEntry::FirstObjectOffset(offset) => {
                    let ptr = card_start.offset(offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }
            }
        });
    }

    fn copy_card(&mut self, card: Card, mut ptr: Address, card_end: Address) {
        let old_end: Address = self.old.free();
        let mut end = cmp::min(card_end, old_end);
        let mut ref_to_young_gen = false;

        loop {
            while ptr < end {
                let object = unsafe { &mut *ptr.to_mut_ptr::<Obj>() };

                object.visit_reference_fields(|field| {
                    let field_ptr = field.get();

                    if self.young.contains(Address::from_ptr(field_ptr)) {
                        let copied_obj = self.copy(field_ptr);
                        field.set(copied_obj);

                        // determine if copied object is still in young generation
                        if self.young.contains(Address::from_ptr(copied_obj)) {
                            ref_to_young_gen = true;
                        }
                    }
                });

                ptr = ptr.offset(object.size());
            }

            // if we are in the last card of the old generation, promoted objects
            // will increase `end` towards `card_end`. Those newly promoted objects
            // need also be handled.
            if end == card_end {
                break;
            }

            let old_end = self.old.free();
            let next_end = cmp::min(card_end, old_end);

            if end != next_end {
                end = next_end;
            } else {
                break;
            }
        }

        // if there are no references to the young generation in this card,
        // set the card to clean.
        if !ref_to_young_gen {
            self.card_table.set(card, CardEntry::Clean);
        }
    }

    fn copy(&mut self, obj: *mut Obj) -> *mut Obj {
        let obj_addr = Address::from_ptr(obj);
        let obj = unsafe { &mut *obj };

        if let Some(fwd) = obj.header().forwarded() {
            return fwd.to_mut_ptr();
        }

        let obj_size = obj.size();

        // if object is old enough we copy it into the old generation
        if self.young.should_be_promoted(obj_addr) {
            let copy_addr = Address::from_ptr(self.old.alloc(obj_size));

            // if there isn't enough space in old gen keep it in the
            // young generation for now
            if copy_addr.is_null() {
                self.promotion_failed = true;

            } else {
                self.promote_object(obj, copy_addr, obj_size);
                return copy_addr.to_mut_ptr();
            }
        }

        let copy_addr = self.free;
        self.free = copy_addr.offset(obj_size);

        obj.copy_to(copy_addr, obj_size);
        obj.header_mut().forward_to(copy_addr);

        copy_addr.to_mut_ptr()
    }

    fn promote_object(&mut self, obj: &mut Obj, copy_addr: Address, obj_size: usize) {
        obj.copy_to(copy_addr, obj_size);

        // Promoted object can have references to the young generation.
        // Set the card table entry to dirty if this is the case.
        self.handle_promoted_object(copy_addr);

        obj.header_mut().forward_to(copy_addr);
    }

    fn handle_promoted_object(&mut self, addr: Address) {
        let card = self.old.card_from_address(addr);

        // card is already dirty, nothing left to do.
        if self.card_table.get(card).is_dirty() {
            return;
        }

        let object: &mut Obj = unsafe { &mut *addr.to_mut_ptr() };
        let mut old_to_young_ref = false;

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                old_to_young_ref = true;
            }
        });

        if old_to_young_ref {
            self.card_table.set(card, CardEntry::Dirty);
        }
    }

    pub fn promotion_failed(&self) -> bool {
        self.promotion_failed
    }
}
<commit_msg>swiper: fix compile error<commit_after>use std::cmp;

use ctxt::SemContext;

use gc::Address;
use gc::root::IndirectObj;
use gc::swiper::CARD_SIZE;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{Card, CrossingEntry, CrossingMap};
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;

use mem;
use object::Obj;
use os::{self, ProtType};
use timer::{in_ms, Timer};

pub struct MinorCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    young: &'a YoungGen,
    old: &'a OldGen,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,

    free: Address,
    promotion_failed: bool,
}

impl<'a, 'ast> MinorCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [IndirectObj],
    ) -> MinorCollector<'a, 'ast> {
        MinorCollector {
            ctxt: ctxt,
            young: young,
            old: old,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            free: Address::null(),
            promotion_failed: false,
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        self.free = self.young.to_space().start;

        self.unprotect_to_space();

        self.visit_roots();
        self.copy_dirty_cards();
        self.visit_copied_objects();
        self.young.swap_spaces(self.free);

        self.protect_to_space();

        timer.stop_with(|dur| {
            println!("GC: Minor GC ({} ms)", in_ms(dur));
        });
    }

    fn unprotect_to_space(&mut self) {
        // make memory writable again, so that we
        // can copy objects to the to-space.
        // Since this has some overhead, do it only in debug builds.

        if cfg!(debug_assertions) {
            let to_space = self.young.to_space();

            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::Writable,
            );
        }
    }

    fn protect_to_space(&mut self) {
        // Make from-space unaccessible both from read/write.
        // Since this has some overhead, do it only in debug builds.
        if cfg!(debug_assertions) {
            let to_space = self.young.to_space();
            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::None,
            );
        }
    }

    fn visit_roots(&mut self) {
        // detect all references from roots into young generation
        for &root in self.rootset {
            let root_ptr = root.get();

            if self.young.contains(Address::from_ptr(root_ptr)) {
                root.set(self.copy(root_ptr));
            }
        }
    }

    fn visit_copied_objects(&mut self) {
        let mut scan = self.young.to_space().start;

        // visit all fields in copied objects
        while scan < self.free {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if self.young.contains(Address::from_ptr(field_ptr)) {
                    field.set(self.copy(field_ptr));
                }
            });

            scan = scan.offset(object.size());
        }
    }

    // copy all references from old- into young-generation.
    fn copy_dirty_cards(&mut self) {
        self.card_table.visit_dirty(|card| {
            let crossing_entry = self.crossing_map.get(card);
            let card_start = self.old.address_from_card(card);
            let card_end = card_start.offset(CARD_SIZE);

            match crossing_entry {
                CrossingEntry::NoRefs => panic!("card dirty without any refs"),
                CrossingEntry::LeadingRefs(refs) => {
                    let mut ptr = card_start;

                    for _ in 0..refs {
                        let ind_ptr = IndirectObj::from_address(ptr);
                        let dir_ptr = ind_ptr.get();

                        if self.young.contains(Address::from_ptr(dir_ptr)) {
                            ind_ptr.set(self.copy(dir_ptr));
                        }

                        ptr = ptr.offset(mem::ptr_width() as usize);
                    }

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }

                CrossingEntry::FirstObjectOffset(offset) => {
                    let ptr = card_start.offset(offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }
            }
        });
    }

    fn copy_card(&mut self, card: Card, mut ptr: Address, card_end: Address) {
        let old_end: Address = self.old.free();
        let mut end = cmp::min(card_end, old_end);
        let mut ref_to_young_gen = false;

        loop {
            while ptr < end {
                let object = unsafe { &mut *ptr.to_mut_ptr::<Obj>() };

                object.visit_reference_fields(|field| {
                    let field_ptr = field.get();

                    if self.young.contains(Address::from_ptr(field_ptr)) {
                        let copied_obj = self.copy(field_ptr);
                        field.set(copied_obj);

                        // determine if copied object is still in young generation
                        if self.young.contains(Address::from_ptr(copied_obj)) {
                            ref_to_young_gen = true;
                        }
                    }
                });

                ptr = ptr.offset(object.size());
            }

            // if we are in the last card of the old generation, promoted objects
            // will increase `end` towards `card_end`. Those newly promoted objects
            // need also be handled.
            if end == card_end {
                break;
            }

            let old_end = self.old.free();
            let next_end = cmp::min(card_end, old_end);

            if end != next_end {
                end = next_end;
            } else {
                break;
            }
        }

        // if there are no references to the young generation in this card,
        // set the card to clean.
        if !ref_to_young_gen {
            self.card_table.set(card, CardEntry::Clean);
        }
    }

    fn copy(&mut self, obj: *mut Obj) -> *mut Obj {
        let obj_addr = Address::from_ptr(obj);
        let obj = unsafe { &mut *obj };

        if let Some(fwd) = obj.header().forwarded() {
            return fwd.to_mut_ptr();
        }

        let obj_size = obj.size();

        // if object is old enough we copy it into the old generation
        if self.young.should_be_promoted(obj_addr) {
            let copy_addr = Address::from_ptr(self.old.alloc(obj_size));

            // if there isn't enough space in old gen keep it in the
            // young generation for now
            if copy_addr.is_null() {
                self.promotion_failed = true;

            } else {
                self.promote_object(obj, copy_addr, obj_size);
                return copy_addr.to_mut_ptr();
            }
        }

        let copy_addr = self.free;
        self.free = copy_addr.offset(obj_size);

        obj.copy_to(copy_addr, obj_size);
        obj.header_mut().forward_to(copy_addr);

        copy_addr.to_mut_ptr()
    }

    fn promote_object(&mut self, obj: &mut Obj, copy_addr: Address, obj_size: usize) {
        obj.copy_to(copy_addr, obj_size);

        // Promoted object can have references to the young generation.
        // Set the card table entry to dirty if this is the case.
        self.handle_promoted_object(copy_addr);

        obj.header_mut().forward_to(copy_addr);
    }

    fn handle_promoted_object(&mut self, addr: Address) {
        let card = self.old.card_from_address(addr);

        // card is already dirty, nothing left to do.
        if self.card_table.get(card).is_dirty() {
            return;
        }

        let object: &mut Obj = unsafe { &mut *addr.to_mut_ptr() };
        let mut old_to_young_ref = false;

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                old_to_young_ref = true;
            }
        });

        if old_to_young_ref {
            self.card_table.set(card, CardEntry::Dirty);
        }
    }

    pub fn promotion_failed(&self) -> bool {
        self.promotion_failed
    }
}
<|endoftext|>