<commit_before>use std::cell::{RefCell, Ref};

use opencl;
use opencl::mem::{Buffer, CLBuffer};

use context::Context;
use matrix::Matrix;
use num::Num;

pub enum ClMatrixMode {
    In,
    Out,
    Mut,
}

pub struct ClMatrix<T: Num> {
    rows: usize,
    columns: usize,
    buffer: CLBuffer<T>,
    event: RefCell<Option<Event>>,
}

impl<T: Num> ClMatrix<T> {
    pub fn new(ctx: &Context, rows: usize, columns: usize, mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        ClMatrix {
            rows: rows,
            columns: columns,
            buffer: ctx.ctx.create_buffer(rows*columns, cl_mem_mode),
            event: RefCell::new(None),
        }
    }

    pub fn from_matrix(ctx: &Context,
                       matrix: &Matrix<T>,
                       mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        let cl_matrix =
            ClMatrix {
                rows: matrix.rows(),
                columns: matrix.columns(),
                buffer: ctx.ctx.create_buffer(matrix.rows()*matrix.columns(), cl_mem_mode),
                event: RefCell::new(None),
            };

        ctx.queue.write(&cl_matrix.buffer, &&matrix.buffer()[..], ());
        cl_matrix
    }

    pub fn get(&self, ctx: &Context) -> Matrix<T> {
        let vec = ctx.queue.get(&self.buffer, &*self.get_event().unwrap());
        Matrix::from_vec(self.rows, self.columns, vec)
    }

    pub fn set(&self, ctx: &Context, matrix: &Matrix<T>) {
        ctx.queue.write(&self.buffer, &&matrix.buffer()[..], ());
    }

    pub fn rows(&self) -> usize {
        self.rows
    }

    pub fn columns(&self) -> usize {
        self.columns
    }

    fn set_event(&self, e: Event) {
        *self.event.borrow_mut() = Some(e);
    }

    fn get_event(&self) -> Option<Ref<Event>> {
        if self.event.borrow().is_some() {
            //Some(Ref::map(&self.event.borrow(), |x| x.unwrap().as_ref().unwrap()))
            Ref::filter_map(self.event.borrow(), |o| o.as_ref())
        } else {
            None
        }
    }

    pub fn copy_to(&self, ctx: &Context, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_copy_to_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn add(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_add_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, event_list));
    }

    pub fn sub(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_sub_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, event_list));
    }

    pub fn multiply(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_multiply_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, event_list));
    }

    pub fn transpose(&self, ctx: &Context, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_transpose_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &self.rows);
        kernel.set_arg(3, &self.columns);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, (self.rows, self.columns),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn dot(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dot_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.columns);
        kernel.set_arg(4, &other.columns);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel,
                                                        (self.rows, other.columns),
                                                        None, event_list));
    }

    pub fn max(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_max_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn min(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_min_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn dmax(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dmax_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn dmin(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dmin_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn mse(&self, ctx: &Context, train: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_mse_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &train.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.rows);
        kernel.set_arg(4, &self.columns);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), train.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel,
                                                        self.columns,
                                                        None, event_list));
    }

    pub fn dmse(&self, ctx: &Context, train: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dmse_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &train.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.rows);
        kernel.set_arg(4, &self.columns);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), train.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel,
                                                        self.columns,
                                                        None, event_list));
    }
}

pub type Event = opencl::hl::Event;

#[test]
fn cl_matrix_add() {
    let ref ctx = Context::new();

    let a = Matrix::from_vec(1, 10000, (0..10000).collect());
    let b = Matrix::from_vec(1, 10000, (0..10000).map(|x| x*2).collect());

    let a_cl = ClMatrix::from_matrix(ctx, &a, ClMatrixMode::In);
    let b_cl = ClMatrix::from_matrix(ctx, &b, ClMatrixMode::In);
    let mut c_cl: ClMatrix<i32> = ClMatrix::new(ctx, 1, 10000, ClMatrixMode::Out);

    a_cl.add(ctx, &b_cl, &mut c_cl);

    let c = c_cl.get(ctx);

    for i in 0..10000 {
        assert!(c[(0, i)] == a[(0, i)] + b[(0, i)]);
    }
}
<commit_msg>Fixed borrowing stuff<commit_after>use std::cell::{RefCell, Ref};

use opencl;
use opencl::mem::{Buffer, CLBuffer};

use context::Context;
use matrix::Matrix;
use num::Num;

pub enum ClMatrixMode {
    In,
    Out,
    Mut,
}

pub struct ClMatrix<T: Num> {
    rows: usize,
    columns: usize,
    buffer: CLBuffer<T>,
    event: RefCell<Option<Event>>,
}

impl<T: Num> ClMatrix<T> {
    pub fn new(ctx: &Context, rows: usize, columns: usize, mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        ClMatrix {
            rows: rows,
            columns: columns,
            buffer: ctx.ctx.create_buffer(rows*columns, cl_mem_mode),
            event: RefCell::new(None),
        }
    }

    pub fn from_matrix(ctx: &Context,
                       matrix: &Matrix<T>,
                       mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        let cl_matrix =
            ClMatrix {
                rows: matrix.rows(),
                columns: matrix.columns(),
                buffer: ctx.ctx.create_buffer(matrix.rows()*matrix.columns(), cl_mem_mode),
                event: RefCell::new(None),
            };

        ctx.queue.write(&cl_matrix.buffer, &&matrix.buffer()[..], ());
        cl_matrix
    }

    pub fn get(&self, ctx: &Context) -> Matrix<T> {
        let vec = ctx.queue.get(&self.buffer, &*self.get_event().unwrap());
        Matrix::from_vec(self.rows, self.columns, vec)
    }

    pub fn set(&self, ctx: &Context, matrix: &Matrix<T>) {
        ctx.queue.write(&self.buffer, &&matrix.buffer()[..], ());
    }

    pub fn rows(&self) -> usize {
        self.rows
    }

    pub fn columns(&self) -> usize {
        self.columns
    }

    fn set_event(&self, e: Event) {
        *self.event.borrow_mut() = Some(e);
    }

    fn get_event(&self) -> Option<Ref<Event>> {
        if self.event.borrow().is_some() {
            //Some(Ref::map(&self.event.borrow(), |x| x.unwrap().as_ref().unwrap()))
            Ref::filter_map(self.event.borrow(), |o| o.as_ref())
        } else {
            None
        }
    }

    pub fn copy_to(&self, ctx: &Context, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_copy_to_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn add(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_add_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, event_list));
    }

    pub fn sub(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_sub_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, event_list));
    }

    pub fn multiply(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_multiply_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, event_list));
    }

    pub fn transpose(&self, ctx: &Context, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_transpose_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &self.rows);
        kernel.set_arg(3, &self.columns);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, (self.rows, self.columns),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn dot(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dot_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.columns);
        kernel.set_arg(4, &other.columns);

        let new_event = {
            let event_list: &[Option<Ref<Event>>] = &[self.get_event(), other.get_event()];
            ctx.queue.enqueue_async_kernel(&kernel,
                                           (self.rows, other.columns),
                                           None, event_list)
        };
        output.set_event(new_event);
    }

    pub fn max(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_max_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn min(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_min_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn dmax(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dmax_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn dmin(&self, ctx: &Context, threshold: T, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dmin_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &threshold);

        output.set_event(ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(),
                                                        None, &*self.get_event().unwrap()));
    }

    pub fn mse(&self, ctx: &Context, train: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_mse_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &train.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.rows);
        kernel.set_arg(4, &self.columns);

        let new_event = {
            let event_list: &[Option<Ref<Event>>] = &[self.get_event(), train.get_event()];
            ctx.queue.enqueue_async_kernel(&kernel,
                                           self.columns,
                                           None, event_list)
        };
        output.set_event(new_event);
    }

    pub fn dmse(&self, ctx: &Context, train: &ClMatrix<T>, output: &ClMatrix<T>) {
        let kernel = ctx.program.create_kernel(format!("vector_dmse_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &train.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.rows);
        kernel.set_arg(4, &self.columns);

        let new_event = {
            let event_list: &[Option<Ref<Event>>] = &[self.get_event(), train.get_event()];
            ctx.queue.enqueue_async_kernel(&kernel,
                                           self.columns,
                                           None, event_list)
        };
        output.set_event(new_event);
    }
}

pub type Event = opencl::hl::Event;

#[test]
fn cl_matrix_add() {
    let ref ctx = Context::new();

    let a = Matrix::from_vec(1, 10000, (0..10000).collect());
    let b = Matrix::from_vec(1, 10000, (0..10000).map(|x| x*2).collect());

    let a_cl = ClMatrix::from_matrix(ctx, &a, ClMatrixMode::In);
    let b_cl = ClMatrix::from_matrix(ctx, &b, ClMatrixMode::In);
    let mut c_cl: ClMatrix<i32> = ClMatrix::new(ctx, 1, 10000, ClMatrixMode::Out);

    a_cl.add(ctx, &b_cl, &mut c_cl);

    let c = c_cl.get(ctx);

    for i in 0..10000 {
        assert!(c[(0, i)] == a[(0, i)] + b[(0, i)]);
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::sync::mpsc::channel;
use std::sync::{Mutex, Arc};
use std::time::{Instant, Duration};
use std::thread;
use std::net::{self, ToSocketAddrs};
use std::hash::Hash;
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::{Path, PathBuf};

use notify::{
    RecommendedWatcher,
    Watcher,
    RecursiveMode,
    DebouncedEvent
};

use clap;
use handlebars::Handlebars;
use percent_encoding::percent_decode;

use hyper::StatusCode;

use cargo_shim::{
    Profile,
    TargetKind,
    CargoTarget
};

use build::{BuildArgs, Project, PathKind, ShouldTriggerRebuild};
use http_utils::{
    SimpleServer,
    response_from_data,
    response_from_status,
    response_from_file
};

use deployment::{Deployment, ArtifactKind};
use error::Error;

fn auto_reload_code( hash: u32 ) -> String {
    // TODO: We probably should do this with with Websockets.
    const TEMPLATE: &'static str = r##"
        window.addEventListener( "load", function() {
            var current_build_hash = {{{current_build_hash}}};
            function try_reload() {
                var req = new XMLHttpRequest();
                req.addEventListener( "load" , function() {
                    if( req.responseText != current_build_hash ) {
                        window.location.reload( true );
                    }
                });
                req.addEventListener( "loadend", function() {
                    setTimeout( try_reload, 500 );
                });
                req.open( "GET", "/__cargo-web__/build_hash" );
                req.send();
            }
            try_reload();
        });
    "##;

    let handlebars = Handlebars::new();
    let mut template_data = BTreeMap::new();
    template_data.insert( "current_build_hash", hash );
    handlebars.render_template( TEMPLATE, &template_data ).unwrap()
}

fn hash< T: Hash >( value: T ) -> u64 {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hasher;

    let mut hasher = DefaultHasher::new();
    value.hash( &mut hasher );
    hasher.finish()
}

#[derive(Clone)]
struct Counter {
    seed: u64,
    value: u64
}

impl Counter {
    fn new() -> Self {
        let timestamp = SystemTime::now().duration_since( UNIX_EPOCH ).unwrap();
        let seed = hash( timestamp.as_secs() ) ^ hash( timestamp.subsec_nanos() );

        Counter {
            seed,
            value: 0
        }
    }

    fn next( &self ) -> Self {
        Counter {
            seed: self.seed,
            value: self.value + 1
        }
    }

    fn get_hash( &self ) -> u32 {
        hash( self.seed + self.value ) as u32
    }
}

impl From< PathKind > for RecursiveMode {
    fn from( mode: PathKind ) -> Self {
        match mode {
            PathKind::File => RecursiveMode::NonRecursive,
            PathKind::Directory => RecursiveMode::Recursive
        }
    }
}

struct LastBuild {
    counter: Counter,
    deployment: Deployment,
    project: Project,
    target: CargoTarget
}

fn select_target( project: &Project ) -> Result< CargoTarget, Error > {
    let target = {
        let targets = project.target_or_select( |target| {
            target.kind == TargetKind::Bin ||
            (target.kind == TargetKind::CDyLib && project.backend().is_native_wasm())
        })?;

        if targets.is_empty() {
            return Err(
                Error::ConfigurationError( format!( "cannot start a webserver for a crate which is a library!" ) )
            );
        }

        targets[ 0 ].clone()
    };

    Ok( target )
}

impl LastBuild {
    fn new( project: Project, target: CargoTarget, counter: Counter ) -> Result< Self, Error > {
        let config = project.aggregate_configuration( Profile::Main )?;
        let result = project.build( &config, &target )?;
        let deployment = Deployment::new( project.package(), &target, &result )?;

        Ok( LastBuild {
            counter,
            deployment,
            project,
            target
        })
    }

    fn get_build_hash( &self ) -> u32 {
        self.counter.get_hash()
    }
}

fn should_rebuild( paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)], path: &Path ) -> bool {
    paths_to_watch.iter()
        .filter( |&&(_, _, should_rebuild)| should_rebuild == ShouldTriggerRebuild::Yes )
        .any( |&(ref root, _, _)| path.starts_with( root ) )
}

fn watch_paths( watcher: &mut RecommendedWatcher, paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)] ) {
    for &(ref path, ref mode, _) in paths_to_watch {
        // TODO: Handle paths that currently don't exist which *will* be created.
        match watcher.watch( &path, (*mode).into() ) {
            Ok( _ ) => trace!( "Watching {:?} ({:?})", path, mode ),
            Err( error ) => trace!( "Failed to watch {:?} ({:?}): {:?}", path, mode, error )
        }
    }
}

fn monitor_for_changes_and_rebuild(
    last_build: Arc< Mutex< LastBuild > >
) -> Arc< Mutex< RecommendedWatcher > > {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new( tx, Duration::from_millis( 500 ) ).unwrap();

    let last_paths_to_watch = {
        let last_build = last_build.lock().unwrap();
        let paths_to_watch = last_build.project.paths_to_watch( &last_build.target );
        debug!( "Found paths to watch: {:#?}", paths_to_watch );

        watch_paths( &mut watcher, &paths_to_watch );
        paths_to_watch.clone()
    };

    let watcher = Arc::new( Mutex::new( watcher ) );
    let weak_watcher = Arc::downgrade( &watcher );

    thread::spawn( move || {
        let rx = rx;
        let mut last_paths_to_watch = last_paths_to_watch;

        while let Ok( event ) = rx.recv() {
            trace!( "Watch event: {:?}", event );
            let should_rebuild = match event {
                DebouncedEvent::Create( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                DebouncedEvent::Remove( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                DebouncedEvent::Rename( ref old_path, ref new_path ) => {
                    should_rebuild( &last_paths_to_watch, old_path ) ||
                    should_rebuild( &last_paths_to_watch, new_path )
                },
                DebouncedEvent::Write( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                _ => continue
            };

            if !should_rebuild {
                trace!( "Nothing of consequence changed; bumping build counter without rebuilding" );
                let mut last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                last_build.counter = counter;
                continue;
            }

            eprintln!( "==== Triggering `cargo build` ====" );
            let (counter, build_args) = {
                let last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                let build_args = last_build.project.build_args().clone();
                (counter, build_args)
            };

            if let Ok( project ) = build_args.load_project() {
                if let Ok( target ) = select_target( &project ) {
                    let mut new_paths_to_watch = project.paths_to_watch( &target );

                    if new_paths_to_watch != last_paths_to_watch {
                        debug!( "Paths to watch have changed; new paths to watch: {:#?}", new_paths_to_watch );
                        if let Some( watcher ) = weak_watcher.upgrade() {
                            let mut watcher = watcher.lock().expect( "watcher was poisoned" );
                            for (path, _, _) in last_paths_to_watch {
                                let _ = watcher.unwatch( path );
                            }

                            watch_paths( &mut watcher, &new_paths_to_watch );
                        }
                        last_paths_to_watch = new_paths_to_watch;
                    }

                    if let Ok( new_build ) = LastBuild::new( project, target, counter ) {
                        *last_build.lock().unwrap() = new_build;
                    }
                }
            }
        }
    });

    watcher
}

fn address_or_default< 'a >( matches: &clap::ArgMatches< 'a > ) -> net::SocketAddr {
    let host = matches.value_of( "host" ).unwrap_or( "localhost" );
    let port = matches.value_of( "port" ).unwrap_or( "8000" );
    format!( "{}:{}", host, port ).to_socket_addrs().unwrap().next().unwrap()
}

pub fn command_start< 'a >( matches: &clap::ArgMatches< 'a > ) -> Result< (), Error > {
    let auto_reload = matches.is_present( "auto-reload" );
    let build_args = BuildArgs::new( matches )?;
    let project = build_args.load_project()?;

    let last_build = {
        let target = select_target( &project )?;
        LastBuild::new( project, target, Counter::new() )?
    };

    let last_build = Arc::new( Mutex::new( last_build ) );
    let (target, js_url) = {
        let last_build = last_build.lock().unwrap();
        let target = last_build.target.clone();
        let js_url = last_build.deployment.js_url().to_owned();
        (target, js_url)
    };

    let _watcher = monitor_for_changes_and_rebuild( last_build.clone() );

    let address = address_or_default( matches );
    let server = SimpleServer::new(&address, move |request| {
        let path = request.uri().path();
        let path = percent_decode( path.as_bytes() ).decode_utf8().unwrap();
        let last_build = last_build.lock().unwrap();

        if path == "/__cargo-web__/build_hash" {
            let data = format!( "{}", last_build.get_build_hash() );
            return response_from_data("application/text", data.into_bytes());
        }

        debug!( "Received a request for {:?}", path );
        if let Some( mut artifact ) = last_build.deployment.get_by_url(&path) {
            if auto_reload && (path == "/" || path == "/index.html") {
                let result = artifact.map_text( |text| {
                    let injected_code = auto_reload_code( last_build.get_build_hash() );
                    text.replace( "<head>", &format!( "<head><script>{}</script>", injected_code ) )
                });
                artifact = match result {
                    Ok( artifact ) => artifact,
                    Err( error ) => {
                        warn!( "Cannot read {:?}: {:?}", path, error );
                        return response_from_status(StatusCode::INTERNAL_SERVER_ERROR);
                    }
                }
            }

            match artifact.kind {
                ArtifactKind::Data( data ) => {
                    return response_from_data(artifact.mime_type, data);
                },

                ArtifactKind::File( fp ) => {
                    return response_from_file(artifact.mime_type, fp);
                }
            }
        } else {
            response_from_status(StatusCode::NOT_FOUND)
        }
    });

    eprintln!( "" );
    eprintln!( "If you need to serve any extra files put them in the 'static' directory" );
    eprintln!( "in the root of your crate; they will be served alongside your application." );
    match target.kind {
        TargetKind::Example => eprintln!( "You can also put a '{}-static' directory in your 'examples' directory.", target.name ),
        TargetKind::Bin | TargetKind::CDyLib => eprintln!( "You can also put a 'static' directory in your 'src' directory." ),
        _ => unreachable!()
    };
    eprintln!( "" );
    eprintln!( "Your application is being served at '/{}'. It will be automatically", js_url );
    eprintln!( "rebuilt if you make any changes in your code." );
    eprintln!( "" );
    eprintln!( "You can access the web server at `http://{}`.", &address );

    if matches.is_present( "open" ) {
        thread::spawn( move || {
            // Wait for server to start
            let start = Instant::now();
            let check_url = format!( "http://{}/__cargo-web__/build_hash", &address );
            let mut response = None;
            while start.elapsed() < Duration::from_secs( 10 ) && response.is_none() {
                thread::sleep( Duration::from_millis( 100 ) );
                response = ::reqwest::get( &check_url ).ok();
            }

            ::open::that( &format!( "http://{}", &address ) ).expect( "Failed to open browser" );
        });
    }

    server.run();

    Ok(())
}
<commit_msg>Warn about `/js/app.js` being deprecated<commit_after>use std::collections::BTreeMap;
use std::sync::mpsc::channel;
use std::sync::{Mutex, Arc};
use std::time::{Instant, Duration};
use std::thread;
use std::net::{self, ToSocketAddrs};
use std::hash::Hash;
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::{Path, PathBuf};

use notify::{
    RecommendedWatcher,
    Watcher,
    RecursiveMode,
    DebouncedEvent
};

use clap;
use handlebars::Handlebars;
use percent_encoding::percent_decode;

use hyper::StatusCode;

use cargo_shim::{
    Profile,
    TargetKind,
    CargoTarget
};

use build::{BuildArgs, Project, PathKind, ShouldTriggerRebuild};
use http_utils::{
    SimpleServer,
    response_from_data,
    response_from_status,
    response_from_file
};

use deployment::{Deployment, ArtifactKind};
use error::Error;

fn auto_reload_code( hash: u32 ) -> String {
    // TODO: We probably should do this with with Websockets.
    const TEMPLATE: &'static str = r##"
        window.addEventListener( "load", function() {
            var current_build_hash = {{{current_build_hash}}};
            function try_reload() {
                var req = new XMLHttpRequest();
                req.addEventListener( "load" , function() {
                    if( req.responseText != current_build_hash ) {
                        window.location.reload( true );
                    }
                });
                req.addEventListener( "loadend", function() {
                    setTimeout( try_reload, 500 );
                });
                req.open( "GET", "/__cargo-web__/build_hash" );
                req.send();
            }
            try_reload();
        });
    "##;

    let handlebars = Handlebars::new();
    let mut template_data = BTreeMap::new();
    template_data.insert( "current_build_hash", hash );
    handlebars.render_template( TEMPLATE, &template_data ).unwrap()
}

fn hash< T: Hash >( value: T ) -> u64 {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hasher;

    let mut hasher = DefaultHasher::new();
    value.hash( &mut hasher );
    hasher.finish()
}

#[derive(Clone)]
struct Counter {
    seed: u64,
    value: u64
}

impl Counter {
    fn new() -> Self {
        let timestamp = SystemTime::now().duration_since( UNIX_EPOCH ).unwrap();
        let seed = hash( timestamp.as_secs() ) ^ hash( timestamp.subsec_nanos() );

        Counter {
            seed,
            value: 0
        }
    }

    fn next( &self ) -> Self {
        Counter {
            seed: self.seed,
            value: self.value + 1
        }
    }

    fn get_hash( &self ) -> u32 {
        hash( self.seed + self.value ) as u32
    }
}

impl From< PathKind > for RecursiveMode {
    fn from( mode: PathKind ) -> Self {
        match mode {
            PathKind::File => RecursiveMode::NonRecursive,
            PathKind::Directory => RecursiveMode::Recursive
        }
    }
}

struct LastBuild {
    counter: Counter,
    deployment: Deployment,
    project: Project,
    target: CargoTarget
}

fn select_target( project: &Project ) -> Result< CargoTarget, Error > {
    let target = {
        let targets = project.target_or_select( |target| {
            target.kind == TargetKind::Bin ||
            (target.kind == TargetKind::CDyLib && project.backend().is_native_wasm())
        })?;

        if targets.is_empty() {
            return Err(
                Error::ConfigurationError( format!( "cannot start a webserver for a crate which is a library!" ) )
            );
        }

        targets[ 0 ].clone()
    };

    Ok( target )
}

impl LastBuild {
    fn new( project: Project, target: CargoTarget, counter: Counter ) -> Result< Self, Error > {
        let config = project.aggregate_configuration( Profile::Main )?;
        let result = project.build( &config, &target )?;
        let deployment = Deployment::new( project.package(), &target, &result )?;

        Ok( LastBuild {
            counter,
            deployment,
            project,
            target
        })
    }

    fn get_build_hash( &self ) -> u32 {
        self.counter.get_hash()
    }
}

fn should_rebuild( paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)], path: &Path ) -> bool {
    paths_to_watch.iter()
        .filter( |&&(_, _, should_rebuild)| should_rebuild == ShouldTriggerRebuild::Yes )
        .any( |&(ref root, _, _)| path.starts_with( root ) )
}

fn watch_paths( watcher: &mut RecommendedWatcher, paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)] ) {
    for &(ref path, ref mode, _) in paths_to_watch {
        // TODO: Handle paths that currently don't exist which *will* be created.
        match watcher.watch( &path, (*mode).into() ) {
            Ok( _ ) => trace!( "Watching {:?} ({:?})", path, mode ),
            Err( error ) => trace!( "Failed to watch {:?} ({:?}): {:?}", path, mode, error )
        }
    }
}

fn monitor_for_changes_and_rebuild(
    last_build: Arc< Mutex< LastBuild > >
) -> Arc< Mutex< RecommendedWatcher > > {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new( tx, Duration::from_millis( 500 ) ).unwrap();

    let last_paths_to_watch = {
        let last_build = last_build.lock().unwrap();
        let paths_to_watch = last_build.project.paths_to_watch( &last_build.target );
        debug!( "Found paths to watch: {:#?}", paths_to_watch );

        watch_paths( &mut watcher, &paths_to_watch );
        paths_to_watch.clone()
    };

    let watcher = Arc::new( Mutex::new( watcher ) );
    let weak_watcher = Arc::downgrade( &watcher );

    thread::spawn( move || {
        let rx = rx;
        let mut last_paths_to_watch = last_paths_to_watch;

        while let Ok( event ) = rx.recv() {
            trace!( "Watch event: {:?}", event );
            let should_rebuild = match event {
                DebouncedEvent::Create( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                DebouncedEvent::Remove( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                DebouncedEvent::Rename( ref old_path, ref new_path ) => {
                    should_rebuild( &last_paths_to_watch, old_path ) ||
                    should_rebuild( &last_paths_to_watch, new_path )
                },
                DebouncedEvent::Write( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                _ => continue
            };

            if !should_rebuild {
                trace!( "Nothing of consequence changed; bumping build counter without rebuilding" );
                let mut last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                last_build.counter = counter;
                continue;
            }

            eprintln!( "==== Triggering `cargo build` ====" );
            let (counter, build_args) = {
                let last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                let build_args = last_build.project.build_args().clone();
                (counter, build_args)
            };

            if let Ok( project ) = build_args.load_project() {
                if let Ok( target ) = select_target( &project ) {
                    let mut new_paths_to_watch = project.paths_to_watch( &target );

                    if new_paths_to_watch != last_paths_to_watch {
                        debug!( "Paths to watch have changed; new paths to watch: {:#?}", new_paths_to_watch );
                        if let Some( watcher ) = weak_watcher.upgrade() {
                            let mut watcher = watcher.lock().expect( "watcher was poisoned" );
                            for (path, _, _) in last_paths_to_watch {
                                let _ = watcher.unwatch( path );
                            }

                            watch_paths( &mut watcher, &new_paths_to_watch );
                        }
                        last_paths_to_watch = new_paths_to_watch;
                    }

                    if let Ok( new_build ) = LastBuild::new( project, target, counter ) {
                        *last_build.lock().unwrap() = new_build;
                    }
                }
            }
        }
    });

    watcher
}

fn address_or_default< 'a >( matches: &clap::ArgMatches< 'a > ) -> net::SocketAddr {
    let host = matches.value_of( "host" ).unwrap_or( "localhost" );
    let port = matches.value_of( "port" ).unwrap_or( "8000" );
    format!( "{}:{}", host, port ).to_socket_addrs().unwrap().next().unwrap()
}

pub fn command_start< 'a >( matches: &clap::ArgMatches< 'a > ) -> Result< (), Error > {
    let auto_reload = matches.is_present( "auto-reload" );
    let build_args = BuildArgs::new( matches )?;
    let project = build_args.load_project()?;

    let last_build = {
        let target = select_target( &project )?;
        LastBuild::new( project, target, Counter::new() )?
    };

    let last_build = Arc::new( Mutex::new( last_build ) );
    let (target, js_url) = {
        let last_build = last_build.lock().unwrap();
        let target = last_build.target.clone();
        let js_url = last_build.deployment.js_url().to_owned();
        (target, js_url)
    };

    let _watcher = monitor_for_changes_and_rebuild( last_build.clone() );

    let target_name = target.name.clone();
    let address = address_or_default( matches );
    let server = SimpleServer::new(&address, move |request| {
        let path = request.uri().path();
        let path = percent_decode( path.as_bytes() ).decode_utf8().unwrap();
        let last_build = last_build.lock().unwrap();

        if path == "/__cargo-web__/build_hash" {
            let data = format!( "{}", last_build.get_build_hash() );
            return response_from_data("application/text", data.into_bytes());
        }

        if path == "/js/app.js" {
            eprintln!( "!!!!!!!!!!!!!!!!!!!!!" );
            eprintln!( "WARNING: `/js/app.js` is deprecated; you should update your HTML file to use `/{}.js` instead!", target_name );
            eprintln!( "!!!!!!!!!!!!!!!!!!!!!" );
        }

        debug!( "Received a request for {:?}", path );
        if let Some( mut artifact ) = last_build.deployment.get_by_url(&path) {
            if auto_reload && (path == "/" || path == "/index.html") {
                let result = artifact.map_text( |text| {
                    let injected_code = auto_reload_code( last_build.get_build_hash() );
                    text.replace( "<head>", &format!( "<head><script>{}</script>", injected_code ) )
                });
                artifact = match result {
                    Ok( artifact ) => artifact,
                    Err( error ) => {
                        warn!( "Cannot read {:?}: {:?}", path, error );
                        return response_from_status(StatusCode::INTERNAL_SERVER_ERROR);
                    }
                }
            }

            match artifact.kind {
                ArtifactKind::Data( data ) => {
                    return response_from_data(artifact.mime_type, data);
                },

                ArtifactKind::File( fp ) => {
                    return response_from_file(artifact.mime_type, fp);
                }
            }
        } else {
            response_from_status(StatusCode::NOT_FOUND)
        }
    });

    eprintln!( "" );
    eprintln!( "If you need to serve any extra files put them in the 'static' directory" );
    eprintln!( "in the root of your crate; they will be served alongside your application." );
    match target.kind {
        TargetKind::Example => eprintln!( "You can also put a '{}-static' directory in your 'examples' directory.", target.name ),
        TargetKind::Bin | TargetKind::CDyLib => eprintln!( "You can also put a 'static' directory in your 'src' directory." ),
        _ => unreachable!()
    };
    eprintln!( "" );
    eprintln!( "Your application is being served at '/{}'. It will be automatically", js_url );
    eprintln!( "rebuilt if you make any changes in your code." );
    eprintln!( "" );
    eprintln!( "You can access the web server at `http://{}`.", &address );

    if matches.is_present( "open" ) {
        thread::spawn( move || {
            // Wait for server to start
            let start = Instant::now();
            let check_url = format!( "http://{}/__cargo-web__/build_hash", &address );
            let mut response = None;
            while start.elapsed() < Duration::from_secs( 10 ) && response.is_none() {
                thread::sleep( Duration::from_millis( 100 ) );
                response = ::reqwest::get( &check_url ).ok();
            }

            ::open::that( &format!( "http://{}", &address ) ).expect( "Failed to open browser" );
        });
    }

    server.run();

    Ok(())
}
<|endoftext|>


<commit_before>use alloc::boxed::Box;

use collections::slice;
use collections::vec::Vec;
use collections::vec_deque::VecDeque;

use core::ptr;

use common::{debug, memory};

use drivers::pciconfig::PciConfig;

use network::common::*;
use network::scheme::*;

use schemes::{Result, KScheme, Resource, Url};

use sync::Intex;

const CTRL: u32 = 0x00;
const CTRL_LRST: u32 = 1 << 3;
const CTRL_ASDE: u32 = 1 << 5;
const CTRL_SLU: u32 = 1 << 6;
const CTRL_ILOS: u32 = 1 << 7;
const CTRL_VME: u32 = 1 << 30;
const CTRL_PHY_RST: u32 = 1 << 31;

const STATUS: u32 = 0x08;

const FCAL: u32 = 0x28;
const FCAH: u32 = 0x2C;
const FCT: u32 = 0x30;
const FCTTV: u32 = 0x170;

const ICR: u32 = 0xC0;

const IMS: u32 = 0xD0;
const IMS_TXDW: u32 = 1;
const IMS_TXQE: u32 = 1 << 1;
const IMS_LSC: u32 = 1 << 2;
const IMS_RXSEQ: u32 = 1 << 3;
const IMS_RXDMT: u32 = 1 << 4;
const IMS_RX: u32 = 1 << 6;
const IMS_RXT: u32 = 1 << 7;

const RCTL: u32 = 0x100;
const RCTL_EN: u32 = 1 << 1;
const RCTL_UPE: u32 = 1 << 3;
const RCTL_MPE: u32 = 1 << 4;
const RCTL_LPE: u32 = 1 << 5;
const RCTL_LBM: u32 = 1 << 6 | 1 << 7;
const RCTL_BAM: u32 = 1 << 15;
const RCTL_BSIZE1: u32 = 1 << 16;
const RCTL_BSIZE2: u32 = 1 << 17;
const RCTL_BSEX: u32 = 1 << 25;
const RCTL_SECRC: u32 = 1 << 26;

const RDBAL: u32 = 0x2800;
const RDBAH: u32 = 0x2804;
const RDLEN: u32 = 0x2808;
const RDH: u32 = 0x2810;
const RDT: u32 = 0x2818;

const RAL0: u32 = 0x5400;
const RAH0: u32 = 0x5404;

#[repr(packed)]
struct Rd {
    buffer: u64,
    length: u16,
    checksum: u16,
    status: u8,
    error: u8,
    special: u16,
}
const RD_DD: u8 = 1;
const RD_EOP: u8 = 1 << 1;

const TCTL: u32 = 0x400;
const TCTL_EN: u32 = 1 << 1;
const TCTL_PSP: u32 = 1 << 3;

const TDBAL: u32 = 0x3800;
const TDBAH: u32 = 0x3804;
const TDLEN: u32 = 0x3808;
const TDH: u32 = 0x3810;
const TDT: u32 = 0x3818;

#[repr(packed)]
struct Td {
    buffer: u64,
    length: u16,
    cso: u8,
    command: u8,
    status: u8,
    css: u8,
    special: u16,
}
const TD_CMD_EOP: u8 = 1;
const TD_CMD_IFCS: u8 = 1 << 1;
const TD_CMD_RS: u8 = 1 << 3;
const TD_DD: u8 = 1;

pub struct Intel8254x {
    pub pci: PciConfig,
    pub base: usize,
    pub memory_mapped: bool,
    pub irq: u8,
    pub resources: Intex<Vec<*mut NetworkResource>>,
    pub inbound: VecDeque<Vec<u8>>,
    pub outbound: VecDeque<Vec<u8>>,
}

impl KScheme for Intel8254x {
    fn scheme(&self) -> &str {
        "network"
    }

    fn open(&mut self, _: &Url, _: usize) -> Result<Box<Resource>> {
        Ok(NetworkResource::new(self))
    }

    fn on_irq(&mut self, irq: u8) {
        if irq == self.irq {
            unsafe {
                debug::dh(self.read(ICR) as usize);
                debug::dl();
            }

            self.sync();
        }
    }

    fn on_poll(&mut self) {
        self.sync();
    }
}

impl NetworkScheme for Intel8254x {
    fn add(&mut self, resource: *mut NetworkResource) {
        self.resources.lock().push(resource);
    }

    fn remove(&mut self, resource: *mut NetworkResource) {
        let mut resources = self.resources.lock();

        let mut i = 0;
        while i < resources.len() {
            let mut remove = false;

            match resources.get(i) {
                Some(ptr) => if *ptr == resource {
                    remove = true;
                } else {
                    i += 1;
                },
                None => break,
            }

            if remove {
                resources.remove(i);
            }
        }
    }

    fn sync(&mut self) {
        unsafe {
            {
                let resources = self.resources.lock();

                for resource in resources.iter() {
                    while let Some(bytes) = (**resource).outbound.lock().pop_front() {
                        self.outbound.push_back(bytes);
                    }
                }
            }

            self.send_outbound();

            self.receive_inbound();

            {
                let resources = self.resources.lock();

                while let Some(bytes) = self.inbound.pop_front() {
                    for resource in resources.iter() {
                        (**resource).inbound.lock().push_back(bytes.clone());
                    }
                }
            }
        }
    }
}

impl Intel8254x {
    pub unsafe fn new(mut pci: PciConfig) -> Box<Self> {
        let base = pci.read(0x10) as usize;

        let mut module = box Intel8254x {
            pci: pci,
            base: base & 0xFFFFFFF0,
            memory_mapped: base & 1 == 0,
            irq: pci.read(0x3C) as u8 & 0xF,
            resources: Intex::new(Vec::new()),
            inbound: VecDeque::new(),
            outbound: VecDeque::new(),
        };

        module.init();

        module
    }

    pub unsafe fn receive_inbound(&mut self) {
        let receive_ring = self.read(RDBAL) as *mut Rd;
        let length = self.read(RDLEN);

        for tail in 0..length / 16 {
            let rd = &mut *receive_ring.offset(tail as isize);
            if rd.status & RD_DD == RD_DD {
                debug::d("Recv ");
                debug::dh(rd as *mut Rd as usize);
                debug::d(" ");
                debug::dh(rd.status as usize);
                debug::d(" ");
                debug::dh(rd.buffer as usize);
                debug::d(" ");
                debug::dh(rd.length as usize);
                debug::dl();

                self.inbound.push_back(Vec::from(slice::from_raw_parts(rd.buffer as *const u8,
                                                                       rd.length as usize)));

                rd.status = 0;
            }
        }
    }

    pub unsafe fn send_outbound(&mut self) {
        while let Some(bytes) = self.outbound.pop_front() {
            let transmit_ring = self.read(TDBAL) as *mut Td;
            let length = self.read(TDLEN);

            loop {
                let head = self.read(TDH);
                let mut tail = self.read(TDT);
                let old_tail = tail;

                tail += 1;
                if tail >= length / 16 {
                    tail = 0;
                }

                if tail != head {
                    if bytes.len() < 16384 {
                        let td = &mut *transmit_ring.offset(old_tail as isize);

                        debug::d("Send ");
                        debug::dh(old_tail as usize);
                        debug::d(" ");
                        debug::dh(td.status as usize);
                        debug::d(" ");
                        debug::dh(td.buffer as usize);
                        debug::d(" ");
                        debug::dh(bytes.len() & 0x3FFF);
                        debug::dl();

                        ::memcpy(td.buffer as *mut u8, bytes.as_ptr(), bytes.len());
                        td.length = (bytes.len() & 0x3FFF) as u16;
                        td.cso = 0;
                        td.command = TD_CMD_EOP | TD_CMD_IFCS | TD_CMD_RS;
                        td.status = 0;
                        td.css = 0;
                        td.special = 0;

                        self.write(TDT, tail);
                    } else {
                        // TODO: More than one TD
                        debug::dl();
                        debug::d("Intel 8254x: Frame too long for transmit: ");
                        debug::dd(bytes.len());
                        debug::dl();
                    }

                    break;
                }
            }
        }
    }

    pub unsafe fn read(&self, register: u32) -> u32 {
        if self.memory_mapped {
            ptr::read((self.base + register as usize) as *mut u32)
        } else {
            0
        }
    }

    pub unsafe fn write(&self, register: u32, data: u32) -> u32 {
        if self.memory_mapped {
            ptr::write((self.base + register as usize) as *mut u32, data);
            ptr::read((self.base + register as usize) as *mut u32)
        } else {
            0
        }
    }

    pub unsafe fn flag(&self, register: u32, flag: u32, value: bool) {
        if value {
            self.write(register, self.read(register) | flag);
        } else {
            self.write(register, self.read(register) & (0xFFFFFFFF - flag));
        }
    }

    pub unsafe fn init(&mut self) {
        debug::d("Intel 8254x on: ");
        debug::dh(self.base);
        if self.memory_mapped {
            debug::d(" memory mapped");
        } else {
            debug::d(" port mapped");
        }
        debug::d(", IRQ: ");
        debug::dbh(self.irq);

        self.pci.flag(4, 4, true); // Bus mastering

        // Enable auto negotiate, link, clear reset, do not Invert Loss-Of Signal
        self.flag(CTRL, CTRL_ASDE | CTRL_SLU, true);
        self.flag(CTRL, CTRL_LRST, false);
        self.flag(CTRL, CTRL_PHY_RST, false);
        self.flag(CTRL, CTRL_ILOS, false);

        // No flow control
        self.write(FCAH, 0);
        self.write(FCAL, 0);
        self.write(FCT, 0);
        self.write(FCTTV, 0);

        // Do not use VLANs
        self.flag(CTRL, CTRL_VME, false);

        debug::d(" CTRL ");
        debug::dh(self.read(CTRL) as usize);

        // TODO: Clear statistical counters

        debug::d(" MAC: ");
        let mac_low = self.read(RAL0);
        let mac_high = self.read(RAH0);
        MAC_ADDR = MacAddr {
            bytes: [mac_low as u8,
                    (mac_low >> 8) as u8,
                    (mac_low >> 16) as u8,
                    (mac_low >> 24) as u8,
                    mac_high as u8,
                    (mac_high >> 8) as u8],
        };
        debug::d(&MAC_ADDR.to_string());

        //
        // MTA => 0;
        //

        // Receive Buffer
        let receive_ring_length = 1024;
        let receive_ring = memory::alloc(receive_ring_length * 16) as *mut Rd;
        for i in 0..receive_ring_length {
            let receive_buffer = memory::alloc(16384);
            ptr::write(receive_ring.offset(i as isize),
                       Rd {
                           buffer: receive_buffer as u64,
                           length: 0,
                           checksum: 0,
                           status: 0,
                           error: 0,
                           special: 0,
                       });
        }

        self.write(RDBAH, 0);
        self.write(RDBAL, receive_ring as u32);
        self.write(RDLEN, (receive_ring_length * 16) as u32);
        self.write(RDH, 0);
        self.write(RDT, receive_ring_length as u32 - 1);

        // Transmit Buffer
        let transmit_ring_length = 64;
        let transmit_ring = memory::alloc(transmit_ring_length * 16) as *mut Td;
        for i in 0..transmit_ring_length {
            let transmit_buffer = memory::alloc(16384);
            ptr::write(transmit_ring.offset(i as isize),
                       Td {
                           buffer: transmit_buffer as u64,
                           length: 0,
                           cso: 0,
                           command: 0,
                           status: 0,
                           css: 0,
                           special: 0,
                       });
        }

        self.write(TDBAH, 0);
        self.write(TDBAL, transmit_ring as u32);
        self.write(TDLEN, (transmit_ring_length * 16) as u32);
        self.write(TDH, 0);
        self.write(TDT, 0);

        self.write(IMS,
                   IMS_RXT | IMS_RX | IMS_RXDMT | IMS_RXSEQ | IMS_LSC | IMS_TXQE | IMS_TXDW);

        debug::d(" IMS ");
        debug::dh(self.read(IMS) as usize);

        self.flag(RCTL, RCTL_EN, true);
        self.flag(RCTL, RCTL_UPE, true);
        // self.flag(RCTL, RCTL_MPE, true);
        self.flag(RCTL, RCTL_LPE, true);
        self.flag(RCTL, RCTL_LBM, false);
        // RCTL.RDMTS = Minimum threshold size ???
        // RCTL.MO = Multicast offset
        self.flag(RCTL, RCTL_BAM, true);
        self.flag(RCTL, RCTL_BSIZE1, true);
        self.flag(RCTL, RCTL_BSIZE2, false);
        self.flag(RCTL, RCTL_BSEX, true);
        self.flag(RCTL, RCTL_SECRC, true);

        debug::d(" RCTL ");
        debug::dh(self.read(RCTL) as usize);

        self.flag(TCTL, TCTL_EN, true);
        self.flag(TCTL, TCTL_PSP, true);
        // TCTL.CT = Collition threshold
        // TCTL.COLD = Collision distance
        // TIPG Packet Gap
        // TODO ...

        debug::d(" TCTL ");
        debug::dh(self.read(TCTL) as usize);

        debug::dl();
    }
}
<commit_msg>Remove debugging output from intel network driver<commit_after>use alloc::boxed::Box;

use collections::slice;
use collections::vec::Vec;
use collections::vec_deque::VecDeque;

use core::ptr;

use common::{debug, memory};

use drivers::pciconfig::PciConfig;

use network::common::*;
use network::scheme::*;

use schemes::{Result, KScheme, Resource, Url};

use sync::Intex;

const CTRL: u32 = 0x00;
const CTRL_LRST: u32 = 1 << 3;
const CTRL_ASDE: u32 = 1 << 5;
const CTRL_SLU: u32 = 1 << 6;
const CTRL_ILOS: u32 = 1 << 7;
const CTRL_VME: u32 = 1 << 30;
const CTRL_PHY_RST: u32 = 1 << 31;

const STATUS: u32 = 0x08;

const FCAL: u32 = 0x28;
const FCAH: u32 = 0x2C;
const FCT: u32 = 0x30;
const FCTTV: u32 = 0x170;

const ICR: u32 = 0xC0;

const IMS: u32 = 0xD0;
const IMS_TXDW: u32 = 1;
const IMS_TXQE: u32 = 1 << 1;
const IMS_LSC: u32 = 1 << 2;
const IMS_RXSEQ: u32 = 1 << 3;
const IMS_RXDMT: u32 = 1 << 4;
const IMS_RX: u32 = 1 << 6;
const IMS_RXT: u32 = 1 << 7;

const RCTL: u32 = 0x100;
const RCTL_EN: u32 = 1 << 1;
const RCTL_UPE: u32 = 1 << 3;
const RCTL_MPE: u32 = 1 << 4;
const RCTL_LPE: u32 = 1 << 5;
const RCTL_LBM: u32 = 1 << 6 | 1 << 7;
const RCTL_BAM: u32 = 1 << 15;
const RCTL_BSIZE1: u32 = 1 << 16;
const RCTL_BSIZE2: u32 = 1 << 17;
const RCTL_BSEX: u32 = 1 << 25;
const RCTL_SECRC: u32 = 1 << 26;

const RDBAL: u32 = 0x2800;
const RDBAH: u32 = 0x2804;
const RDLEN: u32 = 0x2808;
const RDH: u32 = 0x2810;
const RDT: u32 = 0x2818;

const RAL0: u32 = 0x5400;
const RAH0: u32 = 0x5404;

#[repr(packed)]
struct Rd {
    buffer: u64,
    length: u16,
    checksum: u16,
    status: u8,
    error: u8,
    special: u16,
}
const RD_DD: u8 = 1;
const RD_EOP: u8 = 1 << 1;

const TCTL: u32 = 0x400;
const TCTL_EN: u32 = 1 << 1;
const TCTL_PSP: u32 = 1 << 3;

const TDBAL: u32 = 0x3800;
const TDBAH: u32 = 0x3804;
const TDLEN: u32 = 0x3808;
const TDH: u32 = 0x3810;
const TDT: u32 = 0x3818;

#[repr(packed)]
struct Td {
    buffer: u64,
    length: u16,
    cso: u8,
    command: u8,
    status: u8,
    css: u8,
    special: u16,
}
const TD_CMD_EOP: u8 = 1;
const TD_CMD_IFCS: u8 = 1 << 1;
const TD_CMD_RS: u8 = 1 << 3;
const TD_DD: u8 = 1;

pub struct Intel8254x {
    pub pci: PciConfig,
    pub base: usize,
    pub memory_mapped: bool,
    pub irq: u8,
    pub resources: Intex<Vec<*mut NetworkResource>>,
    pub inbound: VecDeque<Vec<u8>>,
    pub outbound: VecDeque<Vec<u8>>,
}

impl KScheme for Intel8254x {
    fn scheme(&self) -> &str {
        "network"
    }

    fn open(&mut self, _: &Url, _: usize) -> Result<Box<Resource>> {
        Ok(NetworkResource::new(self))
    }

    fn on_irq(&mut self, irq: u8) {
        if irq == self.irq {
            unsafe { self.read(ICR) };

            self.sync();
        }
    }

    fn on_poll(&mut self) {
        self.sync();
    }
}

impl NetworkScheme for Intel8254x {
    fn add(&mut self, resource: *mut NetworkResource) {
        self.resources.lock().push(resource);
    }

    fn remove(&mut self, resource: *mut NetworkResource) {
        let mut resources = self.resources.lock();

        let mut i = 0;
        while i < resources.len() {
            let mut remove = false;

            match resources.get(i) {
                Some(ptr) => if *ptr == resource {
                    remove = true;
                } else {
                    i += 1;
                },
                None => break,
            }

            if remove {
                resources.remove(i);
            }
        }
    }

    fn sync(&mut self) {
        unsafe {
            {
                let resources = self.resources.lock();

                for resource in resources.iter() {
                    while let Some(bytes) = (**resource).outbound.lock().pop_front() {
                        self.outbound.push_back(bytes);
                    }
                }
            }

            self.send_outbound();

            self.receive_inbound();

            {
                let resources = self.resources.lock();

                while let Some(bytes) = self.inbound.pop_front() {
                    for resource in resources.iter() {
                        (**resource).inbound.lock().push_back(bytes.clone());
                    }
                }
            }
        }
    }
}

impl Intel8254x {
    pub unsafe fn new(mut pci: PciConfig) -> Box<Self> {
        let base = pci.read(0x10) as usize;

        let mut module = box Intel8254x {
            pci: pci,
            base: base & 0xFFFFFFF0,
            memory_mapped: base & 1 == 0,
            irq: pci.read(0x3C) as u8 & 0xF,
            resources: Intex::new(Vec::new()),
            inbound: VecDeque::new(),
            outbound: VecDeque::new(),
        };

        module.init();

        module
    }

    pub unsafe fn receive_inbound(&mut self) {
        let receive_ring = self.read(RDBAL) as *mut Rd;
        let length = self.read(RDLEN);

        for tail in 0..length / 16 {
            let rd = &mut *receive_ring.offset(tail as isize);
            if rd.status & RD_DD == RD_DD {
                debug::d("Recv ");
                debug::dh(rd as *mut Rd as usize);
                debug::d(" ");
                debug::dh(rd.status as usize);
                debug::d(" ");
                debug::dh(rd.buffer as usize);
                debug::d(" ");
                debug::dh(rd.length as usize);
                debug::dl();

                self.inbound.push_back(Vec::from(slice::from_raw_parts(rd.buffer as *const u8,
                                                                       rd.length as usize)));

                rd.status = 0;
            }
        }
    }

    pub unsafe fn send_outbound(&mut self) {
        while let Some(bytes) = self.outbound.pop_front() {
            let transmit_ring = self.read(TDBAL) as *mut Td;
            let length = self.read(TDLEN);

            loop {
                let head = self.read(TDH);
                let mut tail = self.read(TDT);
                let old_tail = tail;

                tail += 1;
                if tail >= length / 16 {
                    tail = 0;
                }

                if tail != head {
                    if bytes.len() < 16384 {
                        let td = &mut *transmit_ring.offset(old_tail as isize);

                        debug::d("Send ");
                        debug::dh(old_tail as usize);
                        debug::d(" ");
                        debug::dh(td.status as usize);
                        debug::d(" ");
                        debug::dh(td.buffer as usize);
                        debug::d(" ");
                        debug::dh(bytes.len() & 0x3FFF);
                        debug::dl();

                        ::memcpy(td.buffer as *mut u8, bytes.as_ptr(), bytes.len());
                        td.length = (bytes.len() & 0x3FFF) as u16;
                        td.cso = 0;
                        td.command = TD_CMD_EOP | TD_CMD_IFCS | TD_CMD_RS;
                        td.status = 0;
                        td.css = 0;
                        td.special = 0;

                        self.write(TDT, tail);
                    } else {
                        // TODO: More than one TD
                        debug::dl();
                        debug::d("Intel 8254x: Frame too long for transmit: ");
                        debug::dd(bytes.len());
                        debug::dl();
                    }

                    break;
                }
            }
        }
    }

    pub unsafe fn read(&self, register: u32) -> u32 {
        if self.memory_mapped {
            ptr::read((self.base + register as usize) as *mut u32)
        } else {
            0
        }
    }

    pub unsafe fn write(&self, register: u32, data: u32) -> u32 {
        if self.memory_mapped {
            ptr::write((self.base + register as usize) as *mut u32, data);
            ptr::read((self.base + register as usize) as *mut u32)
        } else {
            0
        }
    }

    pub unsafe fn flag(&self, register: u32, flag: u32, value: bool) {
        if value {
            self.write(register, self.read(register) | flag);
        } else {
            self.write(register, self.read(register) & (0xFFFFFFFF - flag));
        }
    }

    pub unsafe fn init(&mut self) {
        debug::d("Intel 8254x on: ");
        debug::dh(self.base);
        if self.memory_mapped {
            debug::d(" memory mapped");
        } else {
            debug::d(" port mapped");
        }
        debug::d(", IRQ: ");
        debug::dbh(self.irq);

        self.pci.flag(4, 4, true); // Bus mastering

        // Enable auto negotiate, link, clear reset, do not Invert Loss-Of Signal
        self.flag(CTRL, CTRL_ASDE | CTRL_SLU, true);
        self.flag(CTRL, CTRL_LRST, false);
        self.flag(CTRL, CTRL_PHY_RST, false);
        self.flag(CTRL, CTRL_ILOS, false);

        // No flow control
        self.write(FCAH, 0);
        self.write(FCAL, 0);
        self.write(FCT, 0);
        self.write(FCTTV, 0);

        // Do not use VLANs
        self.flag(CTRL, CTRL_VME, false);

        debug::d(" CTRL ");
        debug::dh(self.read(CTRL) as usize);

        // TODO: Clear statistical counters

        debug::d(" MAC: ");
        let mac_low = self.read(RAL0);
        let mac_high = self.read(RAH0);
        MAC_ADDR = MacAddr {
            bytes: [mac_low as u8,
                    (mac_low >> 8) as u8,
                    (mac_low >> 16) as u8,
                    (mac_low >> 24) as u8,
                    mac_high as u8,
                    (mac_high >> 8) as u8],
        };
        debug::d(&MAC_ADDR.to_string());

        //
        // MTA => 0;
        //

        // Receive Buffer
        let receive_ring_length = 1024;
        let receive_ring = memory::alloc(receive_ring_length * 16) as *mut Rd;
        for i in 0..receive_ring_length {
            let receive_buffer = memory::alloc(16384);
            ptr::write(receive_ring.offset(i as isize),
                       Rd {
                           buffer: receive_buffer as u64,
                           length: 0,
                           checksum: 0,
                           status: 0,
                           error: 0,
                           special: 0,
                       });
        }

        self.write(RDBAH, 0);
        self.write(RDBAL, receive_ring as u32);
        self.write(RDLEN, (receive_ring_length * 16) as u32);
        self.write(RDH, 0);
        self.write(RDT, receive_ring_length as u32 - 1);

        // Transmit Buffer
        let transmit_ring_length = 64;
        let transmit_ring = memory::alloc(transmit_ring_length * 16) as *mut Td;
        for i in 0..transmit_ring_length {
            let transmit_buffer = memory::alloc(16384);
            ptr::write(transmit_ring.offset(i as isize),
                       Td {
                           buffer: transmit_buffer as u64,
                           length: 0,
                           cso: 0,
                           command: 0,
                           status: 0,
                           css: 0,
                           special: 0,
                       });
        }

        self.write(TDBAH, 0);
        self.write(TDBAL, transmit_ring as u32);
        self.write(TDLEN, (transmit_ring_length * 16) as u32);
        self.write(TDH, 0);
        self.write(TDT, 0);

        self.write(IMS,
                   IMS_RXT | IMS_RX | IMS_RXDMT | IMS_RXSEQ | IMS_LSC | IMS_TXQE | IMS_TXDW);

        debug::d(" IMS ");
        debug::dh(self.read(IMS) as usize);

        self.flag(RCTL, RCTL_EN, true);
        self.flag(RCTL, RCTL_UPE, true);
        // self.flag(RCTL, RCTL_MPE, true);
        self.flag(RCTL, RCTL_LPE, true);
        self.flag(RCTL, RCTL_LBM, false);
        // RCTL.RDMTS = Minimum threshold size ???
        // RCTL.MO = Multicast offset
        self.flag(RCTL, RCTL_BAM, true);
        self.flag(RCTL, RCTL_BSIZE1, true);
        self.flag(RCTL, RCTL_BSIZE2, false);
        self.flag(RCTL, RCTL_BSEX, true);
        self.flag(RCTL, RCTL_SECRC, true);

        debug::d(" RCTL ");
        debug::dh(self.read(RCTL) as usize);

        self.flag(TCTL, TCTL_EN, true);
        self.flag(TCTL, TCTL_PSP, true);
        // TCTL.CT = Collition threshold
        // TCTL.COLD = Collision distance
        // TIPG Packet Gap
        // TODO ...

        debug::d(" TCTL ");
        debug::dh(self.read(TCTL) as usize);

        debug::dl();
    }
}
<|endoftext|>


<commit_before>//! Exposes the JavaScript event loop for scheduling asynchronous events.
//!
//! The [_event loop_][event-loop] is how Node.js provides JavaScript programs
//! access to concurrent events such as the completion of [file][fs] or
//! [network][net] operations, scheduled [timers][timer], or receiving messages
//! from other [processes][process].
//!
//! When an asynchronous operation is started from JavaScript, it registers
//! a JavaScript callback function to wait for the operation to complete. When
//! the operation completes, the callback and the result data are added to an
//! internal _event queue_ in the Node.js runtime so that the event can be
//! processed in order.
//!
//! The event loop processes completed events one at a time in the JavaScript
//! execution thread by calling the registered callback function with its result
//! value as an argument.
//!
//! ## Creating Custom Events
//!
//! This module allows Neon programs to create new types of concurrent events
//! in Rust and expose them to JavaScript as asynchronous functions.
//!
//! A common use for custom events is to run expensive or long-lived
//! computations in a background thread without blocking the JavaScript
//! thread. For example, using the [`psd` crate][psd-crate], a Neon program could
//! asynchronously parse (potentially large) [PSD files][psd-file] in a
//! background thread:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! #
//! # fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) { }
//! #
//! fn parse_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//!     // The types `String`, `Root<JsFunction>`, and `EventQueue` can all be
//!     // sent across threads.
//!     let filename = cx.argument::<JsString>(0)?.value(&mut cx);
//!     let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
//!     let queue = cx.queue();
//!
//!     // Spawn a thread to complete the execution. This will _not_ block the
//!     // JavaScript event loop.
//!     std::thread::spawn(move || {
//!         // Do the heavy lifting inside the background thread.
//!         parse(filename, callback, queue);
//!     });
//!
//!     Ok(cx.undefined())
//! }
//! # }
//! ```
//!
//! (Note that this usage of [`spawn`](std::thread::spawn) makes use of Rust's
//! [`move`][move] syntax to transfer ownership of data to the background
//! thread.)
//!
//! Upon completion of its task, the background thread can use the JavaScript
//! callback and the event queue to notify the main thread of the result:
//!
//! ```
//! # extern crate psd;
//! # extern crate failure;
//! #
//! # #[cfg(feature = "napi-1")] fn main() {
//! # use neon::prelude::*;
//! # use psd::Psd;
//! # use failure::Error;
//! #
//! fn psd_from_filename(filename: String) -> Result<Psd, Error> {
//!     Psd::from_bytes(&std::fs::read(&filename)?)
//! }
//!
//! fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) {
//!     let result = psd_from_filename(filename);
//!
//!     // Send a closure as a task to be executed by the JavaScript event
//!     // queue. This _will_ block the event queue while executing.
//!     queue.send(move |mut cx| {
//!         let callback = callback.into_inner(&mut cx);
//!         let this = cx.undefined();
//!         let null = cx.null();
//!         let args = match result {
//!             Ok(psd) => {
//!                 // Extract data from the parsed file.
//!                 let width = cx.number(psd.width());
//!                 let height = cx.number(psd.height());
//!
//!                 // Save the data in a result object.
//!                 let obj = cx.empty_object();
//!                 obj.set(&mut cx, "width", width)?;
//!                 obj.set(&mut cx, "height", height)?;
//!                 vec![
//!                     cx.null().upcast::<JsValue>(),
//!                     obj.upcast(),
//!                 ]
//!             }
//!             Err(_) => {
//!                 // A full implementation would provide
//!                 // more detailed error diagnostics.
//!                 let err = cx.string("PSD parse error");
//!                 vec![
//!                     err.upcast::<JsValue>(),
//!                 ]
//!             }
//!         };
//!
//!         callback.call(&mut cx, this, args)?;
//!
//!         Ok(())
//!     });
//! }
//! # }
//! ```
//!
//! ## See also
//!
//! 1. Panu Pitkamaki. [Event loop from 10,000ft][event-loop].
//!
//! [event-loop]: https://bytearcher.com/articles/event-loop-10-000ft/
//! [fs]: https://nodejs.org/dist/latest/docs/api/fs.html
//! [net]: https://nodejs.org/dist/latest/docs/api/net.html
//! [process]: https://nodejs.org/dist/latest/docs/api/process.html
//! [timer]: https://nodejs.org/dist/latest/docs/api/timers.html
//! [move]: https://doc.rust-lang.org/std/keyword.move.html
//! [psd-crate]: https://crates.io/crates/psd
//! [psd-file]: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/

#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
mod event_queue;

#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub use self::event_queue::{EventQueue, EventQueueError};

#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
mod event_handler;

#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
pub use self::event_handler::EventHandler;

#[cfg(all(feature = "napi-1", feature = "event-handler-api"))]
compile_error!(
    "The `EventHandler` API is not supported with the N-API \
    backend. Use `EventQueue` instead."
);
<commit_msg>Add initial section title, and some rewording of opening sentence<commit_after>//! Exposes the JavaScript event loop for scheduling asynchronous events.
//!
//! ## The Event Loop
//!
//! The [_event loop_][event-loop] is how Node.js provides JavaScript programs
//! access to concurrent events such as completion of [file][fs] or
//! [network][net] operations, notification of scheduled [timers][timer], or
//! receiving of messages from other [processes][process].
//!
//! When an asynchronous operation is started from JavaScript, it registers
//! a JavaScript callback function to wait for the operation to complete. When
//! the operation completes, the callback and the result data are added to an
//! internal _event queue_ in the Node.js runtime so that the event can be
//! processed in order.
//!
//! The event loop processes completed events one at a time in the JavaScript
//! execution thread by calling the registered callback function with its result
//! value as an argument.
//!
//! ## Creating Custom Events
//!
//! This module allows Neon programs to create new types of concurrent events
//! in Rust and expose them to JavaScript as asynchronous functions.
//!
//! A common use for custom events is to run expensive or long-lived
//! computations in a background thread without blocking the JavaScript
//! thread. For example, using the [`psd` crate][psd-crate], a Neon program could
//! asynchronously parse (potentially large) [PSD files][psd-file] in a
//! background thread:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! #
//! # fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) { }
//! #
//! fn parse_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//!     // The types `String`, `Root<JsFunction>`, and `EventQueue` can all be
//!     // sent across threads.
//!     let filename = cx.argument::<JsString>(0)?.value(&mut cx);
//!     let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
//!     let queue = cx.queue();
//!
//!     // Spawn a thread to complete the execution. This will _not_ block the
//!     // JavaScript event loop.
//!     std::thread::spawn(move || {
//!         // Do the heavy lifting inside the background thread.
//!         parse(filename, callback, queue);
//!     });
//!
//!     Ok(cx.undefined())
//! }
//! # }
//! ```
//!
//! (Note that this usage of [`spawn`](std::thread::spawn) makes use of Rust's
//! [`move`][move] syntax to transfer ownership of data to the background
//! thread.)
//!
//! Upon completion of its task, the background thread can use the JavaScript
//! callback and the event queue to notify the main thread of the result:
//!
//! ```
//! # extern crate psd;
//! # extern crate failure;
//! #
//! # #[cfg(feature = "napi-1")] fn main() {
//! # use neon::prelude::*;
//! # use psd::Psd;
//! # use failure::Error;
//! #
//! fn psd_from_filename(filename: String) -> Result<Psd, Error> {
//!     Psd::from_bytes(&std::fs::read(&filename)?)
//! }
//!
//! fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) {
//!     let result = psd_from_filename(filename);
//!
//!     // Send a closure as a task to be executed by the JavaScript event
//!     // queue. This _will_ block the event queue while executing.
//!     queue.send(move |mut cx| {
//!         let callback = callback.into_inner(&mut cx);
//!         let this = cx.undefined();
//!         let null = cx.null();
//!         let args = match result {
//!             Ok(psd) => {
//!                 // Extract data from the parsed file.
//!                 let width = cx.number(psd.width());
//!                 let height = cx.number(psd.height());
//!
//!                 // Save the data in a result object.
//!                 let obj = cx.empty_object();
//!                 obj.set(&mut cx, "width", width)?;
//!                 obj.set(&mut cx, "height", height)?;
//!                 vec![
//!                     cx.null().upcast::<JsValue>(),
//!                     obj.upcast(),
//!                 ]
//!             }
//!             Err(_) => {
//!                 // A full implementation would provide
//!                 // more detailed error diagnostics.
//!                 let err = cx.string("PSD parse error");
//!                 vec![
//!                     err.upcast::<JsValue>(),
//!                 ]
//!             }
//!         };
//!
//!         callback.call(&mut cx, this, args)?;
//!
//!         Ok(())
//!     });
//! }
//! # }
//! ```
//!
//! ## See also
//!
//! 1. Panu Pitkamaki. [Event loop from 10,000ft][event-loop].
//!
//! [event-loop]: https://bytearcher.com/articles/event-loop-10-000ft/
//! [fs]: https://nodejs.org/dist/latest/docs/api/fs.html
//! [net]: https://nodejs.org/dist/latest/docs/api/net.html
//! [process]: https://nodejs.org/dist/latest/docs/api/process.html
//! [timer]: https://nodejs.org/dist/latest/docs/api/timers.html
//! [move]: https://doc.rust-lang.org/std/keyword.move.html
//! [psd-crate]: https://crates.io/crates/psd
//! [psd-file]: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/

#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
mod event_queue;

#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub use self::event_queue::{EventQueue, EventQueueError};

#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
mod event_handler;

#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
pub use self::event_handler::EventHandler;

#[cfg(all(feature = "napi-1", feature = "event-handler-api"))]
compile_error!(
    "The `EventHandler` API is not supported with the N-API \
    backend. Use `EventQueue` instead."
);
<|endoftext|>