<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;
use std::path::Path;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TryRecvError};
use std::thread;
use std::time::Duration;

use butterfly;
use common::ui::{Status, UI};
use depot_client;
use env;
use hcore::package::{PackageIdent, PackageInstall};
use hcore::service::ServiceGroup;
use hcore::crypto::{artifact, default_cache_key_path, SigKeyPair};
use hcore::crypto::keys::parse_name_with_rev;
use hcore::fs::{CACHE_ARTIFACT_PATH, FS_ROOT_PATH};
use launcher_client::LauncherCli;
use time::{SteadyTime, Duration as TimeDuration};

use {PRODUCT, VERSION};
use error::Result;
use census::CensusRing;
use manager::service::{Service, Topology, UpdateStrategy};

static LOGKEY: &'static str = "SU";
const FREQUENCY_ENVVAR: &'static str = "HAB_UPDATE_STRATEGY_FREQUENCY_MS";
const DEFAULT_FREQUENCY: i64 = 60_000;

type UpdaterStateList = HashMap<ServiceGroup, UpdaterState>;

enum UpdaterState {
    AtOnce(Receiver<PackageInstall>),
    Rolling(RollingState),
}

enum RollingState {
    AwaitingElection,
    InElection,
    Leader(LeaderState),
    Follower(FollowerState),
}

enum LeaderState {
    Polling(Receiver<PackageInstall>),
    Waiting,
}

enum FollowerState {
    Waiting,
    Updating(Receiver<PackageInstall>),
}

pub struct ServiceUpdater {
    states: UpdaterStateList,
    butterfly: butterfly::Server,
}

impl ServiceUpdater {
    pub fn new(butterfly: butterfly::Server) -> Self {
        ServiceUpdater {
            states: UpdaterStateList::default(),
            butterfly: butterfly,
        }
    }

    pub fn add(&mut self, service: &Service) -> bool {
        match service.update_strategy {
            UpdateStrategy::None => false,
            UpdateStrategy::AtOnce => {
                self.states
                    .entry(service.service_group.clone())
                    .or_insert_with(|| {
                        let rx = Worker::new(service).start(&service.service_group, None);
                        UpdaterState::AtOnce(rx)
                    });
                true
            }
            UpdateStrategy::Rolling => {
                self.states.entry(service.service_group.clone()).or_insert(
                    UpdaterState::Rolling(RollingState::AwaitingElection),
                );
                true
            }
        }
    }

    pub fn check_for_updated_package(
        &mut self,
        service: &mut Service,
        census_ring: &CensusRing,
        launcher: &LauncherCli,
    ) -> bool {
        let mut updated = false;
        match self.states.get_mut(&service.service_group) {
            Some(&mut UpdaterState::AtOnce(ref mut rx)) => {
                match rx.try_recv() {
                    Ok(package) => {
                        service.update_package(package, launcher);
                        return true;
                    }
                    Err(TryRecvError::Empty) => return false,
                    Err(TryRecvError::Disconnected) => {}
                }
                debug!("Service Updater worker has died; restarting...");
                *rx = Worker::new(service).start(&service.service_group, None);
            }
            Some(&mut UpdaterState::Rolling(ref mut st @ RollingState::AwaitingElection)) => {
                if let Some(census_group) = census_ring.census_group_for(&service.service_group) {
                    if service.topology == Topology::Leader {
                        debug!(
                            "Rolling Update, determining proper suitability because we're in \
                                a leader topology"
                        );
                        match (census_group.me(), census_group.leader()) {
                            (Some(me), Some(leader)) => {
                                let suitability = if me.member_id == leader.member_id {
                                    u64::min_value()
                                } else {
                                    u64::max_value()
                                };
                                self.butterfly.start_update_election(
                                    service.service_group.clone(),
                                    suitability,
                                    0,
                                );
                                *st = RollingState::InElection
                            }
                            _ => return false,
                        }
                    } else {
                        debug!("Rolling update, using default suitability");
                        self.butterfly.start_update_election(
                            service.service_group.clone(),
                            0,
                            0,
                        );
                        *st = RollingState::InElection;
                    }
                }
            }
            Some(&mut UpdaterState::Rolling(ref mut st @ RollingState::InElection)) => {
                if let Some(census_group) = census_ring.census_group_for(&service.service_group) {
                    match (census_group.me(), census_group.update_leader()) {
                        (Some(me), Some(leader)) => {
                            if me.member_id == leader.member_id {
                                debug!("We're the leader");
                                // Start in waiting state to ensure all members agree with our
                                // version before attempting a new rolling upgrade.
                                *st = RollingState::Leader(LeaderState::Waiting);
                            } else {
                                debug!("We're a follower");
                                *st = RollingState::Follower(FollowerState::Waiting);
                            }
                        }
                        (Some(_), None) => return false,
                        _ => return false,
                    }
                }
            }
            Some(&mut UpdaterState::Rolling(RollingState::Leader(ref mut state))) => {
                match *state {
                    LeaderState::Polling(ref mut rx) => {
                        match rx.try_recv() {
                            Ok(package) => {
                                debug!("Rolling Update, polling found a new package");
                                service.update_package(package, launcher);
                                updated = true;
                            }
                            Err(TryRecvError::Empty) => return false,
                            Err(TryRecvError::Disconnected) => {
                                debug!("Service Updater worker has died; restarting...");
                                *rx = Worker::new(service).start(&service.service_group, None);
                            }
                        }
                    }
                    LeaderState::Waiting => {
                        match census_ring.census_group_for(&service.service_group) {
                            Some(census_group) => {
                                if census_group.members().iter().any(|cm| {
                                    cm.pkg.as_ref().unwrap() !=
                                        census_group.me().unwrap().pkg.as_ref().unwrap()
                                })
                                {
                                    debug!("Update leader still waiting for followers...");
                                    return false;
                                }
                                let rx = Worker::new(service).start(&service.service_group, None);
                                *state = LeaderState::Polling(rx);
                            }
                            None => {
                                panic!(
                                    "Expected census list to have service group '{}'!",
                                    &*service.service_group
                                )
                            }
                        }
                    }
                }
                if updated {
                    *state = LeaderState::Waiting;
                }
            }
            Some(&mut UpdaterState::Rolling(RollingState::Follower(ref mut state))) => {
                match *state {
                    FollowerState::Waiting => {
                        match census_ring.census_group_for(&service.service_group) {
                            Some(census_group) => {
                                match (
                                    census_group.update_leader(),
                                    census_group.previous_peer(),
                                    census_group.me(),
                                ) {
                                    (Some(leader), Some(peer), Some(me)) => {
                                        if leader.pkg == me.pkg {
                                            debug!("We're not in an update");
                                            return false;
                                        }
                                        if leader.pkg != peer.pkg {
                                            debug!("We're in an update but it's not our turn");
                                            return false;
                                        }
                                        debug!("We're in an update and it's our turn");
                                        let rx = Worker::new(service).start(
                                            &service.service_group,
                                            leader.pkg.clone(),
                                        );
                                        *state = FollowerState::Updating(rx);
                                    }
                                    _ => return false,
                                }
                            }
                            None => {
                                panic!(
                                    "Expected census list to have service group '{}'!",
                                    &*service.service_group
                                )
                            }
                        }
                    }
                    FollowerState::Updating(ref mut rx) => {
                        match census_ring.census_group_for(&service.service_group) {
                            Some(census_group) => {
                                match rx.try_recv() {
                                    Ok(package) => {
                                        service.update_package(package, launcher);
                                        updated = true
                                    }
                                    Err(TryRecvError::Empty) => return false,
                                    Err(TryRecvError::Disconnected) => {
                                        debug!("Service Updater worker has died; restarting...");
                                        let package =
                                            census_group.update_leader().unwrap().pkg.clone();
                                        *rx = Worker::new(service).start(
                                            &service.service_group,
                                            package,
                                        );
                                    }
                                }
                            }
                            None => {
                                panic!(
                                    "Expected census list to have service group '{}'!",
                                    &*service.service_group
                                )
                            }
                        }
                    }
                }
                if updated {
                    *state = FollowerState::Waiting;
                }
            }
            None => {}
        }
        updated
    }
}

struct Worker {
    current: PackageIdent,
    spec_ident: PackageIdent,
    depot: depot_client::Client,
    channel: String,
    update_strategy: UpdateStrategy,
    ui: UI,
}

impl Worker {
    fn new(service: &Service) -> Self {
        Worker {
            current: service.pkg.ident.clone(),
            spec_ident: service.spec_ident.clone(),
            depot: depot_client::Client::new(&service.depot_url, PRODUCT, VERSION, None).unwrap(),
            channel: service.channel.clone(),
            update_strategy: service.update_strategy.clone(),
            ui: UI::default(),
        }
    }

    /// Start a new update worker.
    ///
    /// Passing an optional package identifier will make the worker perform a run-once update to
    /// retrieve a specific version from a remote Depot. If no package identifier is specified,
    /// then the updater will poll until a newer more suitable package is found.
    fn start(mut self, sg: &ServiceGroup, ident: Option<PackageIdent>) -> Receiver<PackageInstall> {
        let (tx, rx) = sync_channel(0);
        thread::Builder::new()
            .name(format!("service-updater-{}", sg))
            .spawn(move || match ident {
                Some(latest) => self.run_once(tx, latest),
                None => self.run_poll(tx),
            })
            .expect("unable to start service-updater thread");
        rx
    }

    fn run_once(&mut self, sender: SyncSender<PackageInstall>, ident: PackageIdent) {
        outputln!("Updating from {} to {}", self.current, ident);
        loop {
            let next_check = SteadyTime::now() +
                TimeDuration::milliseconds(self.update_frequency());
            match self.install(&ident, true) {
                Ok(package) => {
                    self.current = package.ident().clone();
                    sender.send(package).expect("Main thread has gone away!");
                    break;
                }
                Err(e) => warn!("Failed to install updated package: {:?}", e),
            }
            let time_to_wait = (next_check - SteadyTime::now()).num_milliseconds();
            if time_to_wait > 0 {
                thread::sleep(Duration::from_millis(time_to_wait as u64));
            }
        }
    }

    fn run_poll(&mut self, sender: SyncSender<PackageInstall>) {
        loop {
            let next_check = SteadyTime::now() +
                TimeDuration::milliseconds(self.update_frequency());
            let mut package: Option<PackageInstall> = None;
            match self.depot.show_package(
                &self.spec_ident,
                Some(&self.channel),
            ) {
                Ok(remote) => {
                    let latest: PackageIdent = remote.get_ident().clone().into();
                    if latest > self.current {
                        outputln!("Updating from {} to {}", self.current, latest);
                        match self.install(&latest, true) {
                            Ok(pkg) => package = Some(pkg),
                            Err(e) => warn!("Failed to install updated package: {:?}", e),
                        }
                    } else {
                        info!("Package found is not newer than ours");
                    }
                }
                Err(e) => warn!("Updater failed to get latest package: {:?}", e),
            }
            if self.update_strategy == UpdateStrategy::AtOnce {
                if let Ok(cached) = PackageInstall::load(
                    &self.spec_ident,
                    Some(&Path::new(&*FS_ROOT_PATH)),
                )
                {
                    let compare = match package {
                        Some(ref pkg) => pkg.ident.clone(),
                        None => self.current.clone(),
                    };

                    if cached.ident > compare {
                        package = Some(cached);
                    }
                }
            }

            if let Some(pkg) = package {
                self.current = pkg.ident.clone();
                sender.send(pkg).expect("Main thread has gone away!");
                break;
            }
            let time_to_wait = (next_check - SteadyTime::now()).num_milliseconds();
            if time_to_wait > 0 {
                thread::sleep(Duration::from_millis(time_to_wait as u64));
            }
        }
    }

    fn install(&mut self, package: &PackageIdent, recurse: bool) -> Result<PackageInstall> {
        let package = match PackageInstall::load(package, Some(&*FS_ROOT_PATH)) {
            Ok(pkg) => pkg,
            Err(_) => self.download(package)?,
        };
        if recurse {
            for ident in package.tdeps()?.iter() {
                self.install(&ident, false)?;
            }
        }
        Ok(package)
    }

    fn download(&mut self, package: &PackageIdent) -> Result<PackageInstall> {
        outputln!("Downloading {}", package);
        let mut archive = self.depot.fetch_package(
            package,
            &Path::new(&*FS_ROOT_PATH).join(
                CACHE_ARTIFACT_PATH,
            ),
            self.ui.progress(),
        )?;

        // TODO (CM): Copied (with modifications) from
        // common::command::package::install; will be properly
        // factored in an upcoming broad refactoring and consolidation
        // of our installation logic.
        let cache_key_path = &default_cache_key_path(None);
        let nwr = artifact::artifact_signer(&archive.path)?;
        if let Err(_) = SigKeyPair::get_public_key_path(&nwr, cache_key_path) {
            self.fetch_origin_key(&nwr)?;
        }

        archive.verify(cache_key_path)?;
        outputln!("Installing {}", package);
        archive.unpack(None)?;
        let pkg = PackageInstall::load(archive.ident().as_ref().unwrap(), Some(&*FS_ROOT_PATH))?;
        Ok(pkg)
    }

    fn update_frequency(&self) -> i64 {
        match env::var(FREQUENCY_ENVVAR) {
            Ok(val) => {
                match val.parse::<i64>() {
                    Ok(num) => num,
                    Err(_) => {
                        outputln!(
                            "Unable to parse '{}' from {} as a valid integer. Falling back \
                                  to default {} MS frequency.",
                            val,
                            FREQUENCY_ENVVAR,
                            DEFAULT_FREQUENCY
                        );
                        DEFAULT_FREQUENCY
                    }
                }
            }
            Err(_) => DEFAULT_FREQUENCY,
        }
    }

    // TODO (CM): Copied (with modifications) from
    // common::command::package::install; will be properly factored in
    // an upcoming broad refactoring and consolidation of our
    // installation logic.
    fn fetch_origin_key(&mut self, name_with_rev: &str) -> Result<()> {
        self.ui.status(
            Status::Downloading,
            format!("{} public origin key", &name_with_rev),
        )?;
        let (name, rev) = parse_name_with_rev(&name_with_rev)?;
        self.depot.fetch_origin_key(
            &name,
            &rev,
            &default_cache_key_path(None),
            self.ui.progress(),
        )?;
        self.ui.status(
            Status::Cached,
            format!("{} public origin key", &name_with_rev),
        )?;
        Ok(())
    }
}
<commit_msg>Don't use coloring for UI messages<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;
use std::path::Path;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TryRecvError};
use std::thread;
use std::time::Duration;

use butterfly;
use common::ui::{Coloring, Status, UI};
use depot_client;
use env;
use hcore::package::{PackageIdent, PackageInstall};
use hcore::service::ServiceGroup;
use hcore::crypto::{artifact, default_cache_key_path, SigKeyPair};
use hcore::crypto::keys::parse_name_with_rev;
use hcore::fs::{CACHE_ARTIFACT_PATH, FS_ROOT_PATH};
use launcher_client::LauncherCli;
use time::{SteadyTime, Duration as TimeDuration};

use {PRODUCT, VERSION};
use error::Result;
use census::CensusRing;
use manager::service::{Service, Topology, UpdateStrategy};

static LOGKEY: &'static str = "SU";
const FREQUENCY_ENVVAR: &'static str = "HAB_UPDATE_STRATEGY_FREQUENCY_MS";
const DEFAULT_FREQUENCY: i64 = 60_000;

type UpdaterStateList = HashMap<ServiceGroup, UpdaterState>;

enum UpdaterState {
    AtOnce(Receiver<PackageInstall>),
    Rolling(RollingState),
}

enum RollingState {
    AwaitingElection,
    InElection,
    Leader(LeaderState),
    Follower(FollowerState),
}

enum LeaderState {
    Polling(Receiver<PackageInstall>),
    Waiting,
}

enum FollowerState {
    Waiting,
    Updating(Receiver<PackageInstall>),
}

pub struct ServiceUpdater {
    states: UpdaterStateList,
    butterfly: butterfly::Server,
}

impl ServiceUpdater {
    pub fn new(butterfly: butterfly::Server) -> Self {
        ServiceUpdater {
            states: UpdaterStateList::default(),
            butterfly: butterfly,
        }
    }

    pub fn add(&mut self, service: &Service) -> bool {
        match service.update_strategy {
            UpdateStrategy::None => false,
            UpdateStrategy::AtOnce => {
                self.states
                    .entry(service.service_group.clone())
                    .or_insert_with(|| {
                        let rx = Worker::new(service).start(&service.service_group, None);
                        UpdaterState::AtOnce(rx)
                    });
                true
            }
            UpdateStrategy::Rolling => {
                self.states.entry(service.service_group.clone()).or_insert(
                    UpdaterState::Rolling(RollingState::AwaitingElection),
                );
                true
            }
        }
    }

    pub fn check_for_updated_package(
        &mut self,
        service: &mut Service,
        census_ring: &CensusRing,
        launcher: &LauncherCli,
    ) -> bool {
        let mut updated = false;
        match self.states.get_mut(&service.service_group) {
            Some(&mut UpdaterState::AtOnce(ref mut rx)) => {
                match rx.try_recv() {
                    Ok(package) => {
                        service.update_package(package, launcher);
                        return true;
                    }
                    Err(TryRecvError::Empty) => return false,
                    Err(TryRecvError::Disconnected) => {}
                }
                debug!("Service Updater worker has died; restarting...");
                *rx = Worker::new(service).start(&service.service_group, None);
            }
            Some(&mut UpdaterState::Rolling(ref mut st @ RollingState::AwaitingElection)) => {
                if let Some(census_group) = census_ring.census_group_for(&service.service_group) {
                    if service.topology == Topology::Leader {
                        debug!(
                            "Rolling Update, determining proper suitability because we're in \
                                a leader topology"
                        );
                        match (census_group.me(), census_group.leader()) {
                            (Some(me), Some(leader)) => {
                                let suitability = if me.member_id == leader.member_id {
                                    u64::min_value()
                                } else {
                                    u64::max_value()
                                };
                                self.butterfly.start_update_election(
                                    service.service_group.clone(),
                                    suitability,
                                    0,
                                );
                                *st = RollingState::InElection
                            }
                            _ => return false,
                        }
                    } else {
                        debug!("Rolling update, using default suitability");
                        self.butterfly.start_update_election(
                            service.service_group.clone(),
                            0,
                            0,
                        );
                        *st = RollingState::InElection;
                    }
                }
            }
            Some(&mut UpdaterState::Rolling(ref mut st @ RollingState::InElection)) => {
                if let Some(census_group) = census_ring.census_group_for(&service.service_group) {
                    match (census_group.me(), census_group.update_leader()) {
                        (Some(me), Some(leader)) => {
                            if me.member_id == leader.member_id {
                                debug!("We're the leader");
                                // Start in waiting state to ensure all members agree with our
                                // version before attempting a new rolling upgrade.
                                *st = RollingState::Leader(LeaderState::Waiting);
                            } else {
                                debug!("We're a follower");
                                *st = RollingState::Follower(FollowerState::Waiting);
                            }
                        }
                        (Some(_), None) => return false,
                        _ => return false,
                    }
                }
            }
            Some(&mut UpdaterState::Rolling(RollingState::Leader(ref mut state))) => {
                match *state {
                    LeaderState::Polling(ref mut rx) => {
                        match rx.try_recv() {
                            Ok(package) => {
                                debug!("Rolling Update, polling found a new package");
                                service.update_package(package, launcher);
                                updated = true;
                            }
                            Err(TryRecvError::Empty) => return false,
                            Err(TryRecvError::Disconnected) => {
                                debug!("Service Updater worker has died; restarting...");
                                *rx = Worker::new(service).start(&service.service_group, None);
                            }
                        }
                    }
                    LeaderState::Waiting => {
                        match census_ring.census_group_for(&service.service_group) {
                            Some(census_group) => {
                                if census_group.members().iter().any(|cm| {
                                    cm.pkg.as_ref().unwrap() !=
                                        census_group.me().unwrap().pkg.as_ref().unwrap()
                                })
                                {
                                    debug!("Update leader still waiting for followers...");
                                    return false;
                                }
                                let rx = Worker::new(service).start(&service.service_group, None);
                                *state = LeaderState::Polling(rx);
                            }
                            None => {
                                panic!(
                                    "Expected census list to have service group '{}'!",
                                    &*service.service_group
                                )
                            }
                        }
                    }
                }
                if updated {
                    *state = LeaderState::Waiting;
                }
            }
            Some(&mut UpdaterState::Rolling(RollingState::Follower(ref mut state))) => {
                match *state {
                    FollowerState::Waiting => {
                        match census_ring.census_group_for(&service.service_group) {
                            Some(census_group) => {
                                match (
                                    census_group.update_leader(),
                                    census_group.previous_peer(),
                                    census_group.me(),
                                ) {
                                    (Some(leader), Some(peer), Some(me)) => {
                                        if leader.pkg == me.pkg {
                                            debug!("We're not in an update");
                                            return false;
                                        }
                                        if leader.pkg != peer.pkg {
                                            debug!("We're in an update but it's not our turn");
                                            return false;
                                        }
                                        debug!("We're in an update and it's our turn");
                                        let rx = Worker::new(service).start(
                                            &service.service_group,
                                            leader.pkg.clone(),
                                        );
                                        *state = FollowerState::Updating(rx);
                                    }
                                    _ => return false,
                                }
                            }
                            None => {
                                panic!(
                                    "Expected census list to have service group '{}'!",
                                    &*service.service_group
                                )
                            }
                        }
                    }
                    FollowerState::Updating(ref mut rx) => {
                        match census_ring.census_group_for(&service.service_group) {
                            Some(census_group) => {
                                match rx.try_recv() {
                                    Ok(package) => {
                                        service.update_package(package, launcher);
                                        updated = true
                                    }
                                    Err(TryRecvError::Empty) => return false,
                                    Err(TryRecvError::Disconnected) => {
                                        debug!("Service Updater worker has died; restarting...");
                                        let package =
                                            census_group.update_leader().unwrap().pkg.clone();
                                        *rx = Worker::new(service).start(
                                            &service.service_group,
                                            package,
                                        );
                                    }
                                }
                            }
                            None => {
                                panic!(
                                    "Expected census list to have service group '{}'!",
                                    &*service.service_group
                                )
                            }
                        }
                    }
                }
                if updated {
                    *state = FollowerState::Waiting;
                }
            }
            None => {}
        }
        updated
    }
}

struct Worker {
    current: PackageIdent,
    spec_ident: PackageIdent,
    depot: depot_client::Client,
    channel: String,
    update_strategy: UpdateStrategy,
    ui: UI,
}

impl Worker {
    fn new(service: &Service) -> Self {
        Worker {
            current: service.pkg.ident.clone(),
            spec_ident: service.spec_ident.clone(),
            depot: depot_client::Client::new(&service.depot_url, PRODUCT, VERSION, None).unwrap(),
            channel: service.channel.clone(),
            update_strategy: service.update_strategy.clone(),
            ui: UI::default_with(Coloring::Never, None),
        }
    }

    /// Start a new update worker.
    ///
    /// Passing an optional package identifier will make the worker perform a run-once update to
    /// retrieve a specific version from a remote Depot. If no package identifier is specified,
    /// then the updater will poll until a newer more suitable package is found.
    fn start(mut self, sg: &ServiceGroup, ident: Option<PackageIdent>) -> Receiver<PackageInstall> {
        let (tx, rx) = sync_channel(0);
        thread::Builder::new()
            .name(format!("service-updater-{}", sg))
            .spawn(move || match ident {
                Some(latest) => self.run_once(tx, latest),
                None => self.run_poll(tx),
            })
            .expect("unable to start service-updater thread");
        rx
    }

    fn run_once(&mut self, sender: SyncSender<PackageInstall>, ident: PackageIdent) {
        outputln!("Updating from {} to {}", self.current, ident);
        loop {
            let next_check = SteadyTime::now() +
                TimeDuration::milliseconds(self.update_frequency());
            match self.install(&ident, true) {
                Ok(package) => {
                    self.current = package.ident().clone();
                    sender.send(package).expect("Main thread has gone away!");
                    break;
                }
                Err(e) => warn!("Failed to install updated package: {:?}", e),
            }
            let time_to_wait = (next_check - SteadyTime::now()).num_milliseconds();
            if time_to_wait > 0 {
                thread::sleep(Duration::from_millis(time_to_wait as u64));
            }
        }
    }

    fn run_poll(&mut self, sender: SyncSender<PackageInstall>) {
        loop {
            let next_check = SteadyTime::now() +
                TimeDuration::milliseconds(self.update_frequency());
            let mut package: Option<PackageInstall> = None;
            match self.depot.show_package(
                &self.spec_ident,
                Some(&self.channel),
            ) {
                Ok(remote) => {
                    let latest: PackageIdent = remote.get_ident().clone().into();
                    if latest > self.current {
                        outputln!("Updating from {} to {}", self.current, latest);
                        match self.install(&latest, true) {
                            Ok(pkg) => package = Some(pkg),
                            Err(e) => warn!("Failed to install updated package: {:?}", e),
                        }
                    } else {
                        info!("Package found is not newer than ours");
                    }
                }
                Err(e) => warn!("Updater failed to get latest package: {:?}", e),
            }
            if self.update_strategy == UpdateStrategy::AtOnce {
                if let Ok(cached) = PackageInstall::load(
                    &self.spec_ident,
                    Some(&Path::new(&*FS_ROOT_PATH)),
                )
                {
                    let compare = match package {
                        Some(ref pkg) => pkg.ident.clone(),
                        None => self.current.clone(),
                    };

                    if cached.ident > compare {
                        package = Some(cached);
                    }
                }
            }

            if let Some(pkg) = package {
                self.current = pkg.ident.clone();
                sender.send(pkg).expect("Main thread has gone away!");
                break;
            }
            let time_to_wait = (next_check - SteadyTime::now()).num_milliseconds();
            if time_to_wait > 0 {
                thread::sleep(Duration::from_millis(time_to_wait as u64));
            }
        }
    }

    fn install(&mut self, package: &PackageIdent, recurse: bool) -> Result<PackageInstall> {
        let package = match PackageInstall::load(package, Some(&*FS_ROOT_PATH)) {
            Ok(pkg) => pkg,
            Err(_) => self.download(package)?,
        };
        if recurse {
            for ident in package.tdeps()?.iter() {
                self.install(&ident, false)?;
            }
        }
        Ok(package)
    }

    fn download(&mut self, package: &PackageIdent) -> Result<PackageInstall> {
        outputln!("Downloading {}", package);
        let mut archive = self.depot.fetch_package(
            package,
            &Path::new(&*FS_ROOT_PATH).join(
                CACHE_ARTIFACT_PATH,
            ),
            self.ui.progress(),
        )?;

        // TODO (CM): Copied (with modifications) from
        // common::command::package::install; will be properly
        // factored in an upcoming broad refactoring and consolidation
        // of our installation logic.
        let cache_key_path = &default_cache_key_path(None);
        let nwr = artifact::artifact_signer(&archive.path)?;
        if let Err(_) = SigKeyPair::get_public_key_path(&nwr, cache_key_path) {
            self.fetch_origin_key(&nwr)?;
        }

        archive.verify(cache_key_path)?;
        outputln!("Installing {}", package);
        archive.unpack(None)?;
        let pkg = PackageInstall::load(archive.ident().as_ref().unwrap(), Some(&*FS_ROOT_PATH))?;
        Ok(pkg)
    }

    fn update_frequency(&self) -> i64 {
        match env::var(FREQUENCY_ENVVAR) {
            Ok(val) => {
                match val.parse::<i64>() {
                    Ok(num) => num,
                    Err(_) => {
                        outputln!(
                            "Unable to parse '{}' from {} as a valid integer. Falling back \
                                  to default {} MS frequency.",
                            val,
                            FREQUENCY_ENVVAR,
                            DEFAULT_FREQUENCY
                        );
                        DEFAULT_FREQUENCY
                    }
                }
            }
            Err(_) => DEFAULT_FREQUENCY,
        }
    }

    // TODO (CM): Copied (with modifications) from
    // common::command::package::install; will be properly factored in
    // an upcoming broad refactoring and consolidation of our
    // installation logic.
    fn fetch_origin_key(&mut self, name_with_rev: &str) -> Result<()> {
        self.ui.status(
            Status::Downloading,
            format!("{} public origin key", &name_with_rev),
        )?;
        let (name, rev) = parse_name_with_rev(&name_with_rev)?;
        self.depot.fetch_origin_key(
            &name,
            &rev,
            &default_cache_key_path(None),
            self.ui.progress(),
        )?;
        self.ui.status(
            Status::Cached,
            format!("{} public origin key", &name_with_rev),
        )?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::result::Result;

pub struct Table {
    column_names: HashMap<String, usize>, // assume last column reserved
    rows: Vec<Vec<f64>>,
}

impl Table {
    pub fn new(c_n: HashMap<String, usize>, r: Vec<Vec<f64>>) -> Table {
        Table {
            rows: r,
            column_names: c_n,
        }
    }

    pub fn get_column_names(&self) -> &HashMap<String, usize> {
        &self.column_names
    }

    pub fn get_rows(&self) -> &Vec<Vec<f64>> {
        &self.rows
    }

    pub fn get_basic_solution(&self) -> Result<Vec<(String, f64)>, (usize, usize)> {
        let mut basic_solution = Vec::with_capacity(self.column_names.len());
        // Note: ignore RHS column.
        'columns: for i in 0..self.column_names.len() - 1 {
            let mut one_entry_index = 0;
            let mut matched_one = false;
            // Find columns that have exactly one 1.0 and rest 0.0 values...
            'column_entries: for j in 0..self.rows.len() {
                match (matched_one, self.rows[j][i]) {
                    (false, 1.0) | (false, -1.0) => {
                        one_entry_index = j;
                        matched_one = true;
                    }
                    (true, 0.0) | (false, 0.0) => continue 'column_entries,
                    _ => {
                        // ... if this is not the case then the value of this
                        // variables in the basic solution is 0.0.
                        basic_solution.push((get_name_of_index(&self.column_names, i).unwrap(),
                                            0.0));
                        continue 'columns;
                    }
                }
            }
            // ... and when we find a basic variable calculate its value but
            // watch out as it might be all zeros so check if we matched a 1.0.
            if matched_one {
                let basic_variable_value = self.rows[one_entry_index][i] *
                                           self.rows[one_entry_index][self.column_names.len() - 1];
                // If the basic variable turns out negative that this solution
                // is not feasable...
                if basic_variable_value.is_sign_negative() {
                    // ... report the row where it happened.
                    return Err((one_entry_index, i));
                } else {
                    // ... if not continue generating the solution.
                    basic_solution.push((get_name_of_index(&self.column_names, i).unwrap(),
                                         basic_variable_value));
                }
            } else {
                basic_solution.push((get_name_of_index(&self.column_names, i).unwrap(), 0.0));
            }
        }
        // If we got here then solution is feasable so return it.
        Ok(basic_solution)
    }

    pub fn is_solution_optimal(&self) -> bool {
        for i in 0..self.column_names.len() - 1 {
            if self.rows[self.rows.len() - 1][i].is_sign_negative() {
                return false;
            }
        }
        true
    }

    pub fn sub_cell(&mut self, row_index: usize, colunm_index: usize, by: f64) {
        self.rows[row_index][colunm_index] = self.rows[row_index][colunm_index] - by;
    }

    pub fn div_cell(&mut self, row_index: usize, colunm_index: usize, by: f64) {
        self.rows[row_index][colunm_index] = self.rows[row_index][colunm_index] / by;
    }
}

fn get_name_of_index(c_n: &HashMap<String, usize>, index: usize) -> Result<String, &str> {
    for (key, val) in c_n.iter() {
        if *val == index {
            return Ok(key.clone());
        }
    }
    Err("Name not found for index given.")
}
<commit_msg>Do not report indexes if the fault is at the last row.<commit_after>use std::collections::HashMap;
use std::result::Result;

pub struct Table {
    column_names: HashMap<String, usize>, // assume last column reserved
    rows: Vec<Vec<f64>>,
}

impl Table {
    pub fn new(c_n: HashMap<String, usize>, r: Vec<Vec<f64>>) -> Table {
        Table {
            rows: r,
            column_names: c_n,
        }
    }

    pub fn get_column_names(&self) -> &HashMap<String, usize> {
        &self.column_names
    }

    pub fn get_rows(&self) -> &Vec<Vec<f64>> {
        &self.rows
    }

    pub fn get_basic_solution(&self) -> Result<Vec<(String, f64)>, (usize, usize)> {
        let mut basic_solution = Vec::with_capacity(self.column_names.len());
        // Note: ignore RHS column.
        'columns: for i in 0..self.column_names.len() - 1 {
            let mut one_entry_index = 0;
            let mut matched_one = false;
            // Find columns that have exactly one 1.0 and rest 0.0 values...
            'column_entries: for j in 0..self.rows.len() {
                match (matched_one, self.rows[j][i]) {
                    (false, 1.0) | (false, -1.0) => {
                        one_entry_index = j;
                        matched_one = true;
                    }
                    (true, 0.0) | (false, 0.0) => continue 'column_entries,
                    _ => {
                        // ... if this is not the case then the value of this
                        // variables in the basic solution is 0.0.
                        basic_solution.push((get_name_of_index(&self.column_names, i).unwrap(),
                                            0.0));
                        continue 'columns;
                    }
                }
            }
            // ... and when we find a basic variable calculate its value but
            // watch out as it might be all zeros so check if we matched a 1.0.
            if matched_one {
                let basic_variable_value = self.rows[one_entry_index][i] *
                                           self.rows[one_entry_index][self.column_names.len() - 1];
                // If the basic variable turns out negative that this solution
                // is not feasable...
                if basic_variable_value.is_sign_negative() &&
                   one_entry_index != self.rows.len() - 1 {
                    // ... report the row where it happened.
                    return Err((one_entry_index, i));
                } else {
                    // ... if not continue generating the solution.
                    basic_solution.push((get_name_of_index(&self.column_names, i).unwrap(),
                                         basic_variable_value));
                }
            } else {
                basic_solution.push((get_name_of_index(&self.column_names, i).unwrap(), 0.0));
            }
        }
        // If we got here then solution is feasable so return it.
        Ok(basic_solution)
    }

    pub fn is_solution_optimal(&self) -> bool {
        for i in 0..self.column_names.len() - 1 {
            if self.rows[self.rows.len() - 1][i].is_sign_negative() {
                return false;
            }
        }
        true
    }

    pub fn sub_cell(&mut self, row_index: usize, colunm_index: usize, by: f64) {
        self.rows[row_index][colunm_index] = self.rows[row_index][colunm_index] - by;
    }

    pub fn div_cell(&mut self, row_index: usize, colunm_index: usize, by: f64) {
        self.rows[row_index][colunm_index] = self.rows[row_index][colunm_index] / by;
    }
}

fn get_name_of_index(c_n: &HashMap<String, usize>, index: usize) -> Result<String, &str> {
    for (key, val) in c_n.iter() {
        if *val == index {
            return Ok(key.clone());
        }
    }
    Err("Name not found for index given.")
}
<|endoftext|>


<commit_before>use libc;
use std::io;

pub mod public {
    use libc;
    use std::time::Duration;

    pub type CSocket = libc::c_int;
    pub type Buf = *const libc::c_void;
    pub type MutBuf = *mut libc::c_void;
    pub type BufLen = libc::size_t;
    pub type CouldFail = libc::ssize_t;
    pub type SockLen = libc::socklen_t;
    pub type MutSockLen = *mut libc::socklen_t;
    pub type SockAddr = libc::sockaddr;
    pub type SockAddrIn = libc::sockaddr_in;
    pub type SockAddrIn6 = libc::sockaddr_in6;
    pub type SockAddrStorage = libc::sockaddr_storage;
    pub type SockAddrFamily = libc::sa_family_t;
    pub type SockAddrFamily6 = libc::sa_family_t;
    pub type InAddr = libc::in_addr;
    pub type In6Addr = libc::in6_addr;

    #[cfg(not(any(target_os = "macos")))]
    pub type TvUsecType = libc::c_long;
    #[cfg(any(target_os = "macos"))]
    pub type TvUsecType = libc::__darwin_suseconds_t;

    pub const AF_INET: libc::c_int = libc::AF_INET;
    pub const AF_INET6: libc::c_int = libc::AF_INET6;
    pub const SOCK_RAW: libc::c_int = libc::SOCK_RAW;

    pub const SOL_SOCKET: libc::c_int = libc::SOL_SOCKET;
    pub const SO_RCVTIMEO: libc::c_int = libc::SO_RCVTIMEO;
    pub const SO_SNDTIMEO: libc::c_int = libc::SO_SNDTIMEO;

    pub const IPPROTO_IP: libc::c_int = libc::IPPROTO_IP;
    pub const IP_HDRINCL: libc::c_int = libc::IP_HDRINCL;
    pub const IP_TTL: libc::c_int = libc::IP_TTL;

    pub use libc::{IFF_UP, IFF_BROADCAST, IFF_LOOPBACK, IFF_POINTOPOINT, IFF_MULTICAST};

    pub const INVALID_SOCKET: CSocket = -1;

    pub unsafe fn close(sock: CSocket) {
        let _ = libc::close(sock);
    }

    pub unsafe fn socket(af: libc::c_int, sock: libc::c_int, proto: libc::c_int) -> CSocket {
        libc::socket(af, sock, proto)
    }

    pub unsafe fn getsockopt(socket: CSocket,
                            level: libc::c_int,
                            name: libc::c_int,
                            value: MutBuf,
                            option_len: MutSockLen)
        -> libc::c_int {
        libc::getsockopt(socket, level, name, value, option_len)
    }

    pub unsafe fn setsockopt(socket: CSocket,
                            level: libc::c_int,
                            name: libc::c_int,
                            value: Buf,
                            option_len: SockLen)
        -> libc::c_int {
        libc::setsockopt(socket, level, name, value, option_len)
    }

    /// Convert a platform specific `timeval` into a Duration
    pub fn timeval_to_duration(tv: libc::timeval) -> Duration {
        Duration::new(tv.tv_sec as u64, (tv.tv_usec as u32) * 1000)
    }

    /// Convert a Duration into a platform specific `timeval`
    pub fn duration_to_timeval(dur: Duration) -> libc::timeval {
        libc::timeval {
            tv_sec: dur.as_secs() as libc::time_t,
            tv_usec: dur.subsec_micros() as TvUsecType
        }
    }

    /// Convert a platform specific `timespec` into a Duration
    pub fn timespec_to_duration(ts: libc::timespec) -> Duration {
        Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)
    }

    /// Convert a Duration into a platform specific `timespec`
    pub fn duration_to_timespec(dur: Duration) -> libc::timespec {
        libc::timespec {
            tv_sec: dur.as_secs() as libc::time_t,
            tv_nsec: dur.subsec_nanos() as TvUsecType
        }
    }

}

use self::public::*;

#[inline(always)]
pub fn ipv4_addr(addr: InAddr) -> u32 {
    (addr.s_addr as u32).to_be()
}

#[inline(always)]
pub fn mk_inaddr(addr: u32) -> InAddr {
    InAddr { s_addr: addr }
}

pub unsafe fn sendto(socket: CSocket,
                     buf: Buf,
                     len: BufLen,
                     flags: libc::c_int,
                     addr: *const SockAddr,
                     addrlen: SockLen)
    -> CouldFail {
    libc::sendto(socket, buf, len, flags, addr, addrlen)
}

pub unsafe fn recvfrom(socket: CSocket,
                       buf: MutBuf,
                       len: BufLen,
                       flags: libc::c_int,
                       addr: *mut SockAddr,
                       addrlen: *mut SockLen)
    -> CouldFail {
    libc::recvfrom(socket, buf, len, flags, addr, addrlen)
}

#[inline]
pub fn retry<F>(f: &mut F) -> libc::ssize_t
    where F: FnMut() -> libc::ssize_t
{
    loop {
        let ret = f();
        if ret != -1 || errno() as isize != libc::EINTR as isize {
            return ret;
        }
    }
}

fn errno() -> i32 {
    io::Error::last_os_error().raw_os_error().unwrap()
}

#[cfg(test)]
mod tests {
    use std::time::Duration;
    use duration_to_timespec;
    use timespec_to_duration;

    #[test]
    fn test_duration_to_timespec(){
        let d1 = Duration::new(1, 0);
        let d2 = Duration::from_millis(500);

        let t1 = duration_to_timespec(d1);
        let t2 = duration_to_timespec(d2);

        let r1 = timespec_to_duration(t1);
        let r2 = timespec_to_duration(t2);

        assert_eq!(d1, r1);
        assert_eq!(d2, r2);
    }
}
<commit_msg>Use i32 directly for tv_usec<commit_after>use libc;
use std::io;

pub mod public {
    use libc;
    use std::time::Duration;

    pub type CSocket = libc::c_int;
    pub type Buf = *const libc::c_void;
    pub type MutBuf = *mut libc::c_void;
    pub type BufLen = libc::size_t;
    pub type CouldFail = libc::ssize_t;
    pub type SockLen = libc::socklen_t;
    pub type MutSockLen = *mut libc::socklen_t;
    pub type SockAddr = libc::sockaddr;
    pub type SockAddrIn = libc::sockaddr_in;
    pub type SockAddrIn6 = libc::sockaddr_in6;
    pub type SockAddrStorage = libc::sockaddr_storage;
    pub type SockAddrFamily = libc::sa_family_t;
    pub type SockAddrFamily6 = libc::sa_family_t;
    pub type InAddr = libc::in_addr;
    pub type In6Addr = libc::in6_addr;

    #[cfg(not(any(target_os = "macos", target_os = "ios")))]
    pub type TvUsecType = libc::c_long;
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    pub type TvUsecType = libc::c_int;

    pub const AF_INET: libc::c_int = libc::AF_INET;
    pub const AF_INET6: libc::c_int = libc::AF_INET6;
    pub const SOCK_RAW: libc::c_int = libc::SOCK_RAW;

    pub const SOL_SOCKET: libc::c_int = libc::SOL_SOCKET;
    pub const SO_RCVTIMEO: libc::c_int = libc::SO_RCVTIMEO;
    pub const SO_SNDTIMEO: libc::c_int = libc::SO_SNDTIMEO;

    pub const IPPROTO_IP: libc::c_int = libc::IPPROTO_IP;
    pub const IP_HDRINCL: libc::c_int = libc::IP_HDRINCL;
    pub const IP_TTL: libc::c_int = libc::IP_TTL;

    pub use libc::{IFF_UP, IFF_BROADCAST, IFF_LOOPBACK, IFF_POINTOPOINT, IFF_MULTICAST};

    pub const INVALID_SOCKET: CSocket = -1;

    pub unsafe fn close(sock: CSocket) {
        let _ = libc::close(sock);
    }

    pub unsafe fn socket(af: libc::c_int, sock: libc::c_int, proto: libc::c_int) -> CSocket {
        libc::socket(af, sock, proto)
    }

    pub unsafe fn getsockopt(socket: CSocket,
                            level: libc::c_int,
                            name: libc::c_int,
                            value: MutBuf,
                            option_len: MutSockLen)
        -> libc::c_int {
        libc::getsockopt(socket, level, name, value, option_len)
    }

    pub unsafe fn setsockopt(socket: CSocket,
                            level: libc::c_int,
                            name: libc::c_int,
                            value: Buf,
                            option_len: SockLen)
        -> libc::c_int {
        libc::setsockopt(socket, level, name, value, option_len)
    }

    /// Convert a platform specific `timeval` into a Duration
    pub fn timeval_to_duration(tv: libc::timeval) -> Duration {
        Duration::new(tv.tv_sec as u64, (tv.tv_usec as u32) * 1000)
    }

    /// Convert a Duration into a platform specific `timeval`
    pub fn duration_to_timeval(dur: Duration) -> libc::timeval {
        libc::timeval {
            tv_sec: dur.as_secs() as libc::time_t,
            tv_usec: dur.subsec_micros() as TvUsecType
        }
    }

    /// Convert a platform specific `timespec` into a Duration
    pub fn timespec_to_duration(ts: libc::timespec) -> Duration {
        Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)
    }

    /// Convert a Duration into a platform specific `timespec`
    pub fn duration_to_timespec(dur: Duration) -> libc::timespec {
        libc::timespec {
            tv_sec: dur.as_secs() as libc::time_t,
            tv_nsec: dur.subsec_nanos() as TvUsecType
        }
    }

}

use self::public::*;

#[inline(always)]
pub fn ipv4_addr(addr: InAddr) -> u32 {
    (addr.s_addr as u32).to_be()
}

#[inline(always)]
pub fn mk_inaddr(addr: u32) -> InAddr {
    InAddr { s_addr: addr }
}

pub unsafe fn sendto(socket: CSocket,
                     buf: Buf,
                     len: BufLen,
                     flags: libc::c_int,
                     addr: *const SockAddr,
                     addrlen: SockLen)
    -> CouldFail {
    libc::sendto(socket, buf, len, flags, addr, addrlen)
}

pub unsafe fn recvfrom(socket: CSocket,
                       buf: MutBuf,
                       len: BufLen,
                       flags: libc::c_int,
                       addr: *mut SockAddr,
                       addrlen: *mut SockLen)
    -> CouldFail {
    libc::recvfrom(socket, buf, len, flags, addr, addrlen)
}

#[inline]
pub fn retry<F>(f: &mut F) -> libc::ssize_t
    where F: FnMut() -> libc::ssize_t
{
    loop {
        let ret = f();
        if ret != -1 || errno() as isize != libc::EINTR as isize {
            return ret;
        }
    }
}

fn errno() -> i32 {
    io::Error::last_os_error().raw_os_error().unwrap()
}

#[cfg(test)]
mod tests {
    use std::time::Duration;
    use duration_to_timespec;
    use timespec_to_duration;

    #[test]
    fn test_duration_to_timespec(){
        let d1 = Duration::new(1, 0);
        let d2 = Duration::from_millis(500);

        let t1 = duration_to_timespec(d1);
        let t2 = duration_to_timespec(d2);

        let r1 = timespec_to_duration(t1);
        let r2 = timespec_to_duration(t2);

        assert_eq!(d1, r1);
        assert_eq!(d2, r2);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use devtools;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
use dom::bindings::error::{ErrorResult, report_pending_exception};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{RootCollection, Root};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::Reflectable;
use dom::errorevent::ErrorEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler, WorkerEventHandler, WorkerErrorHandler};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::workerglobalscope::{WorkerGlobalScopeTypeId, WorkerGlobalScopeInit};
use script_task::{ScriptTask, ScriptChan, ScriptMsg, TimerSource, ScriptPort};
use script_task::StackRootTLS;

use devtools_traits::DevtoolScriptControlMsg;
use msg::constellation_msg::PipelineId;
use net_traits::load_whole_resource;
use profile_traits::mem::{self, Reporter, ReporterRequest};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};

use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
use js::jsapi::{JSContext, RootedValue, HandleValue};
use js::jsapi::{JSAutoRequest, JSAutoCompartment};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::mem::replace;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel, Select, RecvError};

/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[derive(JSTraceable, Clone)]
pub struct SendableWorkerScriptChan {
    sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: TrustedWorkerAddress,
}

impl ScriptChan for SendableWorkerScriptChan {
    fn send(&self, msg: ScriptMsg) -> Result<(), ()> {
        return self.sender.send((self.worker.clone(), msg)).map_err(|_| ());
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        box SendableWorkerScriptChan {
            sender: self.sender.clone(),
            worker: self.worker.clone(),
        }
    }
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
    workerscope: &'a DedicatedWorkerGlobalScope,
    old_worker: Option<TrustedWorkerAddress>,
}

impl<'a> AutoWorkerReset<'a> {
    fn new(workerscope: &'a DedicatedWorkerGlobalScope, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
        AutoWorkerReset {
            workerscope: workerscope,
            old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)),
        }
    }
}

impl<'a> Drop for AutoWorkerReset<'a> {
    fn drop(&mut self) {
        *self.workerscope.worker.borrow_mut() = self.old_worker.clone();
    }
}

enum MixedMessage {
    FromWorker((TrustedWorkerAddress, ScriptMsg)),
    FromDevtools(DevtoolScriptControlMsg),
}

// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
    workerglobalscope: WorkerGlobalScope,
    id: PipelineId,
    receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
    own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: DOMRefCell<Option<TrustedWorkerAddress>>,
    /// Sender to the parent thread.
    parent_sender: Box<ScriptChan+Send>,
}

impl DedicatedWorkerGlobalScope {
    fn new_inherited(init: WorkerGlobalScopeInit,
                     worker_url: Url,
                     id: PipelineId,
                     devtools_port: Receiver<DevtoolScriptControlMsg>,
                     runtime: Rc<Runtime>,
                     parent_sender: Box<ScriptChan+Send>,
                     own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                     receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
                     -> DedicatedWorkerGlobalScope {
        DedicatedWorkerGlobalScope {
            workerglobalscope: WorkerGlobalScope::new_inherited(
                WorkerGlobalScopeTypeId::DedicatedGlobalScope, init, worker_url,
                runtime, devtools_port),
            id: id,
            receiver: receiver,
            own_sender: own_sender,
            parent_sender: parent_sender,
            worker: DOMRefCell::new(None),
        }
    }

    pub fn new(init: WorkerGlobalScopeInit,
               worker_url: Url,
               id: PipelineId,
               devtools_port: Receiver<DevtoolScriptControlMsg>,
               runtime: Rc<Runtime>,
               parent_sender: Box<ScriptChan+Send>,
               own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
               receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
               -> Root<DedicatedWorkerGlobalScope> {
        let scope = box DedicatedWorkerGlobalScope::new_inherited(
            init, worker_url, id, devtools_port, runtime.clone(), parent_sender,
            own_sender, receiver);
        DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
    }
}

impl DedicatedWorkerGlobalScope {
    pub fn run_worker_scope(init: WorkerGlobalScopeInit,
                            worker_url: Url,
                            id: PipelineId,
                            devtools_ipc_port: IpcReceiver<DevtoolScriptControlMsg>,
                            worker: TrustedWorkerAddress,
                            parent_sender: Box<ScriptChan+Send>,
                            own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                            receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
        let serialized_worker_url = worker_url.serialize();
        spawn_named(format!("WebWorker for {}", serialized_worker_url), move || {
            task_state::initialize(SCRIPT | IN_WORKER);

            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);

            let (url, source) = match load_whole_resource(&init.resource_task, worker_url) {
                Err(_) => {
                    println!("error loading script {}", serialized_worker_url);
                    parent_sender.send(ScriptMsg::RunnableMsg(
                        box WorkerEventHandler::new(worker))).unwrap();
                    return;
                }
                Ok((metadata, bytes)) => {
                    (metadata.final_url, String::from_utf8(bytes).unwrap())
                }
            };

            let runtime = Rc::new(ScriptTask::new_rt_and_cx());
            let serialized_url = url.serialize();
            let parent_sender_for_reporter = parent_sender.clone();

            let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
            ROUTER.route_ipc_receiver_to_mpsc_sender(devtools_ipc_port, devtools_mpsc_chan);

            let global = DedicatedWorkerGlobalScope::new(
                init, url, id, devtools_mpsc_port, runtime.clone(),
                parent_sender, own_sender, receiver);
            // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
            // registration (#6631), so we instead use a random number and cross our fingers.
            let scope = WorkerGlobalScopeCast::from_ref(global.r());

            {
                let _ar = AutoWorkerReset::new(global.r(), worker);

                match runtime.evaluate_script(
                    global.r().reflector().get_jsobject(), source, serialized_url, 1) {
                    Ok(_) => (),
                    Err(_) => {
                        // TODO: An error needs to be dispatched to the parent.
                        // https://github.com/servo/servo/issues/6422
                        println!("evaluate_script failed");
                        let _ar = JSAutoRequest::new(runtime.cx());
                        report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
                    }
                }
            }

            // Register this task as a memory reporter.
            let reporter_name = format!("worker-reporter-{}", random::<u64>());
            let (reporter_sender, reporter_receiver) = ipc::channel().unwrap();
            ROUTER.add_route(reporter_receiver.to_opaque(), box move |reporter_request| {
                // Just injects an appropriate event into the worker task's queue.
                let reporter_request: ReporterRequest = reporter_request.to().unwrap();
                parent_sender_for_reporter.send(ScriptMsg::CollectReports(
                        reporter_request.reports_channel)).unwrap()
            });
            scope.mem_profiler_chan().send(mem::ProfilerMsg::RegisterReporter(
                    reporter_name.clone(),
                    Reporter(reporter_sender)));

            while let Ok(event) = global.receive_event() {
                global.handle_event(event);
            }

            // Unregister this task as a memory reporter.
            let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
            scope.mem_profiler_chan().send(msg);
        });
    }
}

pub trait DedicatedWorkerGlobalScopeHelpers {
    fn script_chan(self) -> Box<ScriptChan+Send>;
    fn pipeline(self) -> PipelineId;
    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
    fn process_event(self, msg: ScriptMsg);
}

impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn script_chan(self) -> Box<ScriptChan+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn pipeline(self) -> PipelineId {
        self.id
    }

    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>) {
        let (tx, rx) = channel();
        let chan = box SendableWorkerScriptChan {
            sender: tx,
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        };
        (chan, box rx)
    }

    fn process_event(self, msg: ScriptMsg) {
        self.handle_script_event(msg);
    }
}

trait PrivateDedicatedWorkerGlobalScopeHelpers {
    fn handle_script_event(self, msg: ScriptMsg);
    fn dispatch_error_to_worker(self, &ErrorEvent);
    fn receive_event(self) -> Result<MixedMessage, RecvError>;
    fn handle_event(self, event: MixedMessage);
}

impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    #[allow(unsafe_code)]
    fn receive_event(self) -> Result<MixedMessage, RecvError> {
        let scope = WorkerGlobalScopeCast::from_ref(self);
        let worker_port = &self.receiver;
        let devtools_port = scope.devtools_port();

        let sel = Select::new();
        let mut worker_handle = sel.handle(worker_port);
        let mut devtools_handle = sel.handle(devtools_port);
        unsafe {
            worker_handle.add();
            if scope.devtools_sender().is_some() {
                devtools_handle.add();
            }
        }
        let ret = sel.wait();
        if ret == worker_handle.id() {
            Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
        } else if ret == devtools_handle.id() {
            Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
        } else {
            panic!("unexpected select result!")
        }
    }

    fn handle_script_event(self, msg: ScriptMsg) {
        match msg {
            ScriptMsg::DOMMessage(data) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let target = EventTargetCast::from_ref(self);
                let _ar = JSAutoRequest::new(scope.get_cx());
                let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get());
                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
                data.read(GlobalRef::Worker(scope), message.handle_mut());
                MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
            },
            ScriptMsg::RunnableMsg(runnable) => {
                runnable.handler()
            },
            ScriptMsg::RefcountCleanup(addr) => {
                LiveDOMReferences::cleanup(addr);
            }
            ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                scope.handle_fire_timer(timer_id);
            }
            ScriptMsg::CollectReports(reports_chan) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let cx = scope.get_cx();
                let path_seg = format!("url({})", scope.get_url());
                let reports = ScriptTask::get_reports(cx, path_seg);
                reports_chan.send(reports);
            }
            _ => panic!("Unexpected message"),
        }
    }

    fn handle_event(self, event: MixedMessage) {
        match event {
            MixedMessage::FromDevtools(msg) => {
                let global_ref = GlobalRef::Worker(WorkerGlobalScopeCast::from_ref(self));
                match msg {
                    DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) =>
                        devtools::handle_evaluate_js(&global_ref, string, sender),
                    DevtoolScriptControlMsg::GetCachedMessages(pipe_id, message_types, sender) =>
                        devtools::handle_get_cached_messages(pipe_id, message_types, sender),
                    DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) =>
                        devtools::handle_wants_live_notifications(&global_ref, bool_val),
                    _ => debug!("got an unusable devtools control message inside the worker!"),
                }
            },
            MixedMessage::FromWorker((linked_worker, msg)) => {
                let _ar = AutoWorkerReset::new(self, linked_worker);
                self.handle_script_event(msg);
            },
        }
    }

    fn dispatch_error_to_worker(self, errorevent: &ErrorEvent) {
        let msg = errorevent.Message();
        let file_name = errorevent.Filename();
        let line_num = errorevent.Lineno();
        let col_num = errorevent.Colno();
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerErrorHandler::new(worker, msg, file_name, line_num, col_num))).unwrap();
 }
}

impl<'a> DedicatedWorkerGlobalScopeMethods for &'a DedicatedWorkerGlobalScope {
    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
    fn PostMessage(self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
        let data = try!(StructuredCloneData::write(cx, message));
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerMessageHandler::new(worker, data))).unwrap();
        Ok(())
    }

    event_handler!(message, GetOnmessage, SetOnmessage);
}

impl DedicatedWorkerGlobalScopeDerived for EventTarget {
    fn is_dedicatedworkerglobalscope(&self) -> bool {
        match *self.type_id() {
            EventTargetTypeId::WorkerGlobalScope(WorkerGlobalScopeTypeId::DedicatedGlobalScope) => true,
            _ => false
        }
    }
}
<commit_msg>Remove the parent_sender_for_reporter local from DedicatedWorkerGlobalScope::run_worker_scope.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use devtools;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
use dom::bindings::error::{ErrorResult, report_pending_exception};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{RootCollection, Root};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::Reflectable;
use dom::errorevent::ErrorEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler, WorkerEventHandler, WorkerErrorHandler};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::workerglobalscope::{WorkerGlobalScopeTypeId, WorkerGlobalScopeInit};
use script_task::{ScriptTask, ScriptChan, ScriptMsg, TimerSource, ScriptPort};
use script_task::StackRootTLS;

use devtools_traits::DevtoolScriptControlMsg;
use msg::constellation_msg::PipelineId;
use net_traits::load_whole_resource;
use profile_traits::mem::{self, Reporter, ReporterRequest};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};

use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
use js::jsapi::{JSContext, RootedValue, HandleValue};
use js::jsapi::{JSAutoRequest, JSAutoCompartment};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::mem::replace;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel, Select, RecvError};

/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[derive(JSTraceable, Clone)]
pub struct SendableWorkerScriptChan {
    sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: TrustedWorkerAddress,
}

impl ScriptChan for SendableWorkerScriptChan {
    fn send(&self, msg: ScriptMsg) -> Result<(), ()> {
        return self.sender.send((self.worker.clone(), msg)).map_err(|_| ());
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        box SendableWorkerScriptChan {
            sender: self.sender.clone(),
            worker: self.worker.clone(),
        }
    }
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
    workerscope: &'a DedicatedWorkerGlobalScope,
    old_worker: Option<TrustedWorkerAddress>,
}

impl<'a> AutoWorkerReset<'a> {
    fn new(workerscope: &'a DedicatedWorkerGlobalScope, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
        AutoWorkerReset {
            workerscope: workerscope,
            old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)),
        }
    }
}

impl<'a> Drop for AutoWorkerReset<'a> {
    fn drop(&mut self) {
        *self.workerscope.worker.borrow_mut() = self.old_worker.clone();
    }
}

enum MixedMessage {
    FromWorker((TrustedWorkerAddress, ScriptMsg)),
    FromDevtools(DevtoolScriptControlMsg),
}

// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
    workerglobalscope: WorkerGlobalScope,
    id: PipelineId,
    receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
    own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: DOMRefCell<Option<TrustedWorkerAddress>>,
    /// Sender to the parent thread.
    parent_sender: Box<ScriptChan+Send>,
}

impl DedicatedWorkerGlobalScope {
    fn new_inherited(init: WorkerGlobalScopeInit,
                     worker_url: Url,
                     id: PipelineId,
                     devtools_port: Receiver<DevtoolScriptControlMsg>,
                     runtime: Rc<Runtime>,
                     parent_sender: Box<ScriptChan+Send>,
                     own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                     receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
                     -> DedicatedWorkerGlobalScope {
        DedicatedWorkerGlobalScope {
            workerglobalscope: WorkerGlobalScope::new_inherited(
                WorkerGlobalScopeTypeId::DedicatedGlobalScope, init, worker_url,
                runtime, devtools_port),
            id: id,
            receiver: receiver,
            own_sender: own_sender,
            parent_sender: parent_sender,
            worker: DOMRefCell::new(None),
        }
    }

    pub fn new(init: WorkerGlobalScopeInit,
               worker_url: Url,
               id: PipelineId,
               devtools_port: Receiver<DevtoolScriptControlMsg>,
               runtime: Rc<Runtime>,
               parent_sender: Box<ScriptChan+Send>,
               own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
               receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
               -> Root<DedicatedWorkerGlobalScope> {
        let scope = box DedicatedWorkerGlobalScope::new_inherited(
            init, worker_url, id, devtools_port, runtime.clone(), parent_sender,
            own_sender, receiver);
        DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
    }
}

impl DedicatedWorkerGlobalScope {
    pub fn run_worker_scope(init: WorkerGlobalScopeInit,
                            worker_url: Url,
                            id: PipelineId,
                            devtools_ipc_port: IpcReceiver<DevtoolScriptControlMsg>,
                            worker: TrustedWorkerAddress,
                            parent_sender: Box<ScriptChan+Send>,
                            own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                            receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
        let serialized_worker_url = worker_url.serialize();
        spawn_named(format!("WebWorker for {}", serialized_worker_url), move || {
            task_state::initialize(SCRIPT | IN_WORKER);

            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);

            let (url, source) = match load_whole_resource(&init.resource_task, worker_url) {
                Err(_) => {
                    println!("error loading script {}", serialized_worker_url);
                    parent_sender.send(ScriptMsg::RunnableMsg(
                        box WorkerEventHandler::new(worker))).unwrap();
                    return;
                }
                Ok((metadata, bytes)) => {
                    (metadata.final_url, String::from_utf8(bytes).unwrap())
                }
            };

            let runtime = Rc::new(ScriptTask::new_rt_and_cx());
            let serialized_url = url.serialize();

            let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
            ROUTER.route_ipc_receiver_to_mpsc_sender(devtools_ipc_port, devtools_mpsc_chan);

            let global = DedicatedWorkerGlobalScope::new(
                init, url, id, devtools_mpsc_port, runtime.clone(),
                parent_sender.clone(), own_sender, receiver);
            // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
            // registration (#6631), so we instead use a random number and cross our fingers.
            let scope = WorkerGlobalScopeCast::from_ref(global.r());

            {
                let _ar = AutoWorkerReset::new(global.r(), worker);

                match runtime.evaluate_script(
                    global.r().reflector().get_jsobject(), source, serialized_url, 1) {
                    Ok(_) => (),
                    Err(_) => {
                        // TODO: An error needs to be dispatched to the parent.
                        // https://github.com/servo/servo/issues/6422
                        println!("evaluate_script failed");
                        let _ar = JSAutoRequest::new(runtime.cx());
                        report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
                    }
                }
            }

            // Register this task as a memory reporter.
            let reporter_name = format!("worker-reporter-{}", random::<u64>());
            let (reporter_sender, reporter_receiver) = ipc::channel().unwrap();
            ROUTER.add_route(reporter_receiver.to_opaque(), box move |reporter_request| {
                // Just injects an appropriate event into the worker task's queue.
                let reporter_request: ReporterRequest = reporter_request.to().unwrap();
                parent_sender.send(ScriptMsg::CollectReports(
                        reporter_request.reports_channel)).unwrap()
            });
            scope.mem_profiler_chan().send(mem::ProfilerMsg::RegisterReporter(
                    reporter_name.clone(),
                    Reporter(reporter_sender)));

            while let Ok(event) = global.receive_event() {
                global.handle_event(event);
            }

            // Unregister this task as a memory reporter.
            let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
            scope.mem_profiler_chan().send(msg);
        });
    }
}

pub trait DedicatedWorkerGlobalScopeHelpers {
    fn script_chan(self) -> Box<ScriptChan+Send>;
    fn pipeline(self) -> PipelineId;
    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
    fn process_event(self, msg: ScriptMsg);
}

impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn script_chan(self) -> Box<ScriptChan+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn pipeline(self) -> PipelineId {
        self.id
    }

    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>) {
        let (tx, rx) = channel();
        let chan = box SendableWorkerScriptChan {
            sender: tx,
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        };
        (chan, box rx)
    }

    fn process_event(self, msg: ScriptMsg) {
        self.handle_script_event(msg);
    }
}

trait PrivateDedicatedWorkerGlobalScopeHelpers {
    fn handle_script_event(self, msg: ScriptMsg);
    fn dispatch_error_to_worker(self, &ErrorEvent);
    fn receive_event(self) -> Result<MixedMessage, RecvError>;
    fn handle_event(self, event: MixedMessage);
}

impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    #[allow(unsafe_code)]
    fn receive_event(self) -> Result<MixedMessage, RecvError> {
        let scope = WorkerGlobalScopeCast::from_ref(self);
        let worker_port = &self.receiver;
        let devtools_port = scope.devtools_port();

        let sel = Select::new();
        let mut worker_handle = sel.handle(worker_port);
        let mut devtools_handle = sel.handle(devtools_port);
        unsafe {
            worker_handle.add();
            if scope.devtools_sender().is_some() {
                devtools_handle.add();
            }
        }
        let ret = sel.wait();
        if ret == worker_handle.id() {
            Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
        } else if ret == devtools_handle.id() {
            Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
        } else {
            panic!("unexpected select result!")
        }
    }

    fn handle_script_event(self, msg: ScriptMsg) {
        match msg {
            ScriptMsg::DOMMessage(data) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let target = EventTargetCast::from_ref(self);
                let _ar = JSAutoRequest::new(scope.get_cx());
                let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get());
                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
                data.read(GlobalRef::Worker(scope), message.handle_mut());
                MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
            },
            ScriptMsg::RunnableMsg(runnable) => {
                runnable.handler()
            },
            ScriptMsg::RefcountCleanup(addr) => {
                LiveDOMReferences::cleanup(addr);
            }
            ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                scope.handle_fire_timer(timer_id);
            }
            ScriptMsg::CollectReports(reports_chan) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let cx = scope.get_cx();
                let path_seg = format!("url({})", scope.get_url());
                let reports = ScriptTask::get_reports(cx, path_seg);
                reports_chan.send(reports);
            }
            _ => panic!("Unexpected message"),
        }
    }

    fn handle_event(self, event: MixedMessage) {
        match event {
            MixedMessage::FromDevtools(msg) => {
                let global_ref = GlobalRef::Worker(WorkerGlobalScopeCast::from_ref(self));
                match msg {
                    DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) =>
                        devtools::handle_evaluate_js(&global_ref, string, sender),
                    DevtoolScriptControlMsg::GetCachedMessages(pipe_id, message_types, sender) =>
                        devtools::handle_get_cached_messages(pipe_id, message_types, sender),
                    DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) =>
                        devtools::handle_wants_live_notifications(&global_ref, bool_val),
                    _ => debug!("got an unusable devtools control message inside the worker!"),
                }
            },
            MixedMessage::FromWorker((linked_worker, msg)) => {
                let _ar = AutoWorkerReset::new(self, linked_worker);
                self.handle_script_event(msg);
            },
        }
    }

    fn dispatch_error_to_worker(self, errorevent: &ErrorEvent) {
        let msg = errorevent.Message();
        let file_name = errorevent.Filename();
        let line_num = errorevent.Lineno();
        let col_num = errorevent.Colno();
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerErrorHandler::new(worker, msg, file_name, line_num, col_num))).unwrap();
 }
}

impl<'a> DedicatedWorkerGlobalScopeMethods for &'a DedicatedWorkerGlobalScope {
    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
    fn PostMessage(self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
        let data = try!(StructuredCloneData::write(cx, message));
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerMessageHandler::new(worker, data))).unwrap();
        Ok(())
    }

    event_handler!(message, GetOnmessage, SetOnmessage);
}

impl DedicatedWorkerGlobalScopeDerived for EventTarget {
    fn is_dedicatedworkerglobalscope(&self) -> bool {
        match *self.type_id() {
            EventTargetTypeId::WorkerGlobalScope(WorkerGlobalScopeTypeId::DedicatedGlobalScope) => true,
            _ => false
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// `raw_now` returns the monotonic time since some unspecified starting point.
pub use self::inner::raw_now;

#[cfg(not(target_os = "linux"))]
mod inner {
    use time::{self, Timespec};

    const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;

    pub fn raw_now() -> Timespec {
        // TODO Add monotonic raw clock time impl for macos and windows
        // Currently use `time::get_precise_ns()` instead.
        let ns = time::precise_time_ns();
        let s = ns / NANOSECONDS_PER_SECOND;
        let ns = ns % NANOSECONDS_PER_SECOND;
        Timespec::new(s as i64, ns as i32)
    }
}

#[cfg(target_os = "linux")]
mod inner {
    use std::io;
    use time::Timespec;
    use libc;

    pub fn raw_now() -> Timespec {
        let mut t = libc::timespec {
            tv_sec: 0,
            tv_nsec: 0,
        };
        let res = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut t) };
        if res != 0 {
            panic!("failed to get monotonic raw locktime, err {}",
                   io::Error::last_os_error());
        }
        Timespec::new(t.tv_sec, t.tv_nsec as i32)
    }
}

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

    #[test]
    fn test_now_monotonic_raw() {
        let early_time = raw_now();
        let late_time = raw_now();
        // The monotonic raw clocktime must be strictly monotonic increasing.
        assert!(late_time > early_time);
    }
}
<commit_msg>util/clocktime: add assert message for test case (#1955)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// `raw_now` returns the monotonic time since some unspecified starting point.
pub use self::inner::raw_now;

#[cfg(not(target_os = "linux"))]
mod inner {
    use time::{self, Timespec};

    const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;

    pub fn raw_now() -> Timespec {
        // TODO Add monotonic raw clock time impl for macos and windows
        // Currently use `time::get_precise_ns()` instead.
        let ns = time::precise_time_ns();
        let s = ns / NANOSECONDS_PER_SECOND;
        let ns = ns % NANOSECONDS_PER_SECOND;
        Timespec::new(s as i64, ns as i32)
    }
}

#[cfg(target_os = "linux")]
mod inner {
    use std::io;
    use time::Timespec;
    use libc;

    pub fn raw_now() -> Timespec {
        let mut t = libc::timespec {
            tv_sec: 0,
            tv_nsec: 0,
        };
        let res = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut t) };
        if res != 0 {
            panic!("failed to get monotonic raw locktime, err {}",
                   io::Error::last_os_error());
        }
        Timespec::new(t.tv_sec, t.tv_nsec as i32)
    }
}

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

    #[test]
    fn test_now_monotonic_raw() {
        let early_time = raw_now();
        let late_time = raw_now();
        // The monotonic raw clocktime must be strictly monotonic increasing.
        assert!(late_time >= early_time,
                "expect late time {:?} >= early time {:?}",
                late_time,
                early_time);
    }
}
<|endoftext|>