<commit_before>use futures::{future, Future};
use futures_cpupool::Builder;
use rdkafka::error::KafkaResult;
use rdkafka::consumer::Consumer;
use regex::Regex;
use rocket::State;
use rocket::http::RawStr;

use cache::Cache;
use error::*;
use live_consumer::LiveConsumerStore;
use metadata::{CONSUMERS, ClusterId, TopicName};
use offsets::OffsetStore;
use web_server::pages::omnisearch::OmnisearchFormParams;
use utils::CompressedJSON;

use std::collections::HashMap;

//
// ********** TOPICS LIST **********
//

#[derive(Serialize)]
struct TopicDetails {
    topic_name: String,
    partition_count: usize,
    errors: String,
    b_rate_15: f64,
    m_rate_15: f64
}

#[get("/api/clusters/<cluster_id>/topics?<timestamp>")]
pub fn cluster_topics(cluster_id: ClusterId, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let result_data = cache.topics
        .filter_clone(|&(ref c, _)| c == &cluster_id)
        .into_iter()
        .map(|((_, topic_name), partitions)| {
            let metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.to_owned()))
                .unwrap_or_default()
                .aggregate_broker_metrics();
            TopicDetails {
                topic_name: topic_name,
                partition_count: partitions.len(),
                errors: partitions.into_iter().filter_map(|p| p.error)
                    .collect::<Vec<_>>().join(","),
                b_rate_15: metrics.b_rate_15.round(),
                m_rate_15: metrics.m_rate_15.round(),
            }
        })
        .collect::<Vec<_>>();

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** BROKERS LIST **********
//

#[get("/api/clusters/<cluster_id>/brokers?<timestamp>")]
pub fn brokers(cluster_id: ClusterId, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let brokers = brokers.unwrap();
    let broker_metrics = cache.metrics.get(&(cluster_id.to_owned(), "__TOTAL__".to_owned()))
        .unwrap_or_default();
    let mut result_data = Vec::with_capacity(brokers.len());
    for broker in brokers {
        let metric = broker_metrics.brokers
            .get(&broker.id)
            .cloned()
            .unwrap_or_default();
        result_data.push(json!((broker.id, broker.hostname, metric.b_rate_15.round(), metric.m_rate_15.round())));
    }

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** GROUP **********
//

struct GroupInfo {
    state: String,
    members: usize,
    stored_offsets: usize,
}

impl GroupInfo {
    fn new(state: String, members: usize) -> GroupInfo {
        GroupInfo { state: state, members: members, stored_offsets: 0 }
    }

    fn new_empty() -> GroupInfo {
        GroupInfo { state: "Offsets only".to_owned(), members: 0, stored_offsets: 0 }
    }

    fn add_offset(&mut self) {
        self.stored_offsets += 1;
    }
}

// TODO: add doc
// TODO: add limit
fn build_group_list<F>(cache: &Cache, filter_fn: F) -> HashMap<(ClusterId, String), GroupInfo>
        where F: Fn(&ClusterId, &TopicName, &String) -> bool {

    let mut groups: HashMap<(ClusterId, String), GroupInfo> = cache.groups
        .lock_iter(|iter| {
            iter.filter(|&(&(ref c, ref t), g)| filter_fn(c, t, &g.name))
                .map(|(&(ref c, _), g)| ((c.clone(), g.name.clone()), GroupInfo::new(g.state.clone(), g.members.len())))
                .collect()
        });

    let offsets = cache.offsets.filter_clone_k(|&(ref c, ref g, ref t)| filter_fn(c, t, g));
    for (cluster_id, group, _) in offsets {
        groups.entry((cluster_id, group)).or_insert_with(GroupInfo::new_empty).add_offset();
    }

    groups
}

#[get("/api/clusters/<cluster_id>/groups?<timestamp>")]
pub fn cluster_groups(cluster_id: ClusterId, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let groups = build_group_list(cache.inner(), |c, _, _| &cluster_id == c);

    let mut result_data = Vec::with_capacity(groups.len());
    for ((_cluster_id, group_name), info) in groups {
        result_data.push(json!((group_name, info.state, info.members, info.stored_offsets)));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/clusters/<cluster_id>/topics/<topic_name>/groups?<timestamp>")]
pub fn topic_groups(cluster_id: ClusterId, topic_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let groups = build_group_list(cache.inner(), |c, t, _| &cluster_id == c && topic_name == t);

    let mut result_data = Vec::with_capacity(groups.len());
    for ((_cluster_id, group_name), info) in groups {
        result_data.push(json!((group_name, info.state, info.members, info.stored_offsets)));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/clusters/<cluster_id>/groups/<group_name>/members?<timestamp>")]
pub fn group_members(cluster_id: ClusterId, group_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let group = cache.groups.get(&(cluster_id.clone(), group_name.to_string()));
    if group.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let group = group.unwrap();

    let mut result_data = Vec::with_capacity(group.members.len());
    for member in group.members {
        result_data.push(json!((member.id, member.client_id, member.client_host)));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/clusters/<cluster_id>/groups/<group_name>/offsets?<timestamp>")]
pub fn group_offsets(cluster_id: ClusterId, group_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let offsets = cache.offsets_by_cluster_group(&cluster_id, group_name.as_str());

    let wms = time!("fetch wms", fetch_watermarks(&cluster_id, &offsets));
    let wms = match wms {
        Ok(wms) => wms,
        Err(e) => {
            error!("Error while fetching watermarks: {}", e);
            return CompressedJSON(json!({"data": []}));
        }
    };

    let mut result_data = Vec::with_capacity(offsets.len());
    for ((_cluster_id, _group, topic), partitions) in offsets {
        for (partition_id, &curr_offset) in partitions.iter().enumerate() {
            let (low, high) = match wms.get(&(topic.clone(), partition_id as i32)) {
                Some(&Ok((low_mark, high_mark))) => (low_mark, high_mark),
                _ => (-1, -1),
            };
            let (lag_shown, percentage_shown) = match (high - low, high - curr_offset) {
                (0, _) => ("Empty topic".to_owned(), "0.0%".to_owned()),
                (size, lag) if lag > size => ("Out of retention".to_owned(), "".to_owned()),
                (size, lag) => (lag.to_string(), format!("{:.1}%", (lag as f64) / (size as f64) * 100.0))
            };
            result_data.push(json!((topic.clone(), partition_id, high-low, low, high, curr_offset, lag_shown, percentage_shown)));
        }
    }

    CompressedJSON(json!({"data": result_data}))
}

fn fetch_watermarks(cluster_id: &ClusterId, offsets: &[((ClusterId, String, TopicName), Vec<i64>)])
        -> Result<HashMap<(TopicName, i32), KafkaResult<(i64, i64)>>> {
    let consumer = CONSUMERS.get_err(cluster_id)?;

    let cpu_pool = Builder::new().pool_size(32).create();

    let mut futures = Vec::new();

    for &((_, _, ref topic), ref partitions) in offsets {
        for partition_id in 0..partitions.len() {
            let consumer_clone = consumer.clone();
            let topic_clone = topic.clone();
            let wm_future = cpu_pool.spawn_fn(move || {
                let wms = consumer_clone.fetch_watermarks(&topic_clone, partition_id as i32, 10000);
                Ok::<_, ()>(((topic_clone, partition_id as i32), wms))  // never fail
            });
            futures.push(wm_future);
        }
    }

    let watermarks = future::join_all(futures).wait().unwrap()
        .into_iter()
        .collect::<HashMap<_, _>>();

    Ok(watermarks)
}

//
// ********** TOPIC TOPOLOGY **********
//

#[get("/api/clusters/<cluster_id>/topics/<topic_name>/topology?<timestamp>")]
pub fn topic_topology(cluster_id: ClusterId, topic_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let partitions = cache.topics.get(&(cluster_id.to_owned(), topic_name.to_string()));
    if partitions.is_none() {
        return CompressedJSON(json!({"data": []}));
    }

    let topic_metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.to_string()))
        .unwrap_or_default();
    let partitions = partitions.unwrap();

    let mut result_data = Vec::with_capacity(partitions.len());
    for p in partitions {
        let partition_metrics = topic_metrics.brokers.get(&p.leader)
            .and_then(|broker_metrics| broker_metrics.partitions.get(p.id as usize))
            .cloned()
            .unwrap_or_default();
        result_data.push(json!((p.id, partition_metrics.size_bytes, p.leader, p.replicas, p.isr, p.error)));
    }

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** SEARCH **********
//

#[get("/api/search/consumer?<search>")]
pub fn consumer_search(search: OmnisearchFormParams, cache: State<Cache>) -> CompressedJSON {
    let groups = if search.regex {
        Regex::new(&search.string)
            .map(|r| build_group_list(&cache, |_, _, g| r.is_match(g)))
            .unwrap_or_default()
    } else {
        build_group_list(&cache, |_, _, g| g.contains(&search.string))
    };

    let mut result_data = Vec::with_capacity(groups.len());
    for ((cluster_id, group_name), info) in groups {
        result_data.push(json!((cluster_id, group_name, info.state, info.members, info.stored_offsets)));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/search/topic?<search>")]
pub fn topic_search(search: OmnisearchFormParams, cache: State<Cache>) -> CompressedJSON {
    let topics = if search.regex {
        Regex::new(&search.string)
            .map(|r| cache.topics.filter_clone(|&(_, ref name)| r.is_match(name)))
            .unwrap_or_default()
    } else {
        cache.topics.filter_clone(|&(_, ref name)| name.contains(&search.string))
    };

    let mut result_data = Vec::new();
    for ((cluster_id, topic_name), partitions) in topics {
        let metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.clone()))
            .unwrap_or_default()
            .aggregate_broker_metrics();
        let errors = partitions.iter().find(|p| p.error.is_some());
        result_data.push(json!((cluster_id, topic_name, partitions.len(), errors, metrics.b_rate_15, metrics.m_rate_15)));
    }

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** INTERNALS **********
//

#[get("/api/internals/cache/brokers?<timestamp>")]
pub fn cache_brokers(cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let result_data = cache.brokers.lock_iter(|brokers_cache_entry| {
        brokers_cache_entry.map(|(cluster_id, brokers)| {
            (cluster_id.clone(), brokers.iter().map(|b| b.id).collect::<Vec<_>>())
        })
        .collect::<Vec<_>>()
    });

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/internals/cache/metrics?<timestamp>")]
pub fn cache_metrics(cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let result_data = cache.metrics.lock_iter(|metrics_cache_entry| {
        metrics_cache_entry
            .map(|(&(ref cluster_id, ref topic_id), metrics)| {
                (cluster_id.clone(), topic_id.clone(), metrics.brokers.len())
            }).collect::<Vec<_>>()
    });

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/internals/live_consumers?<timestamp>")]
pub fn live_consumers(live_consumers: State<LiveConsumerStore>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let result_data = live_consumers.consumers().iter()
        .map(|consumer| (consumer.id(), consumer.cluster_id().to_owned(), consumer.topic().to_owned(),
                          consumer.last_poll().elapsed().as_secs()))
        .collect::<Vec<_>>();
    CompressedJSON(json!({"data": result_data}))
}
<commit_msg>Fix issue #12: wrong group API<commit_after>use futures::{future, Future};
use futures_cpupool::Builder;
use rdkafka::error::KafkaResult;
use rdkafka::consumer::Consumer;
use regex::Regex;
use rocket::State;
use rocket::http::RawStr;

use cache::Cache;
use error::*;
use live_consumer::LiveConsumerStore;
use metadata::{CONSUMERS, ClusterId, TopicName};
use offsets::OffsetStore;
use web_server::pages::omnisearch::OmnisearchFormParams;
use utils::CompressedJSON;

use std::collections::{HashMap, HashSet};

//
// ********** TOPICS LIST **********
//

#[derive(Serialize)]
struct TopicDetails {
    topic_name: String,
    partition_count: usize,
    errors: String,
    b_rate_15: f64,
    m_rate_15: f64
}

#[get("/api/clusters/<cluster_id>/topics?<timestamp>")]
pub fn cluster_topics(cluster_id: ClusterId, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let result_data = cache.topics
        .filter_clone(|&(ref c, _)| c == &cluster_id)
        .into_iter()
        .map(|((_, topic_name), partitions)| {
            let metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.to_owned()))
                .unwrap_or_default()
                .aggregate_broker_metrics();
            TopicDetails {
                topic_name: topic_name,
                partition_count: partitions.len(),
                errors: partitions.into_iter().filter_map(|p| p.error)
                    .collect::<Vec<_>>().join(","),
                b_rate_15: metrics.b_rate_15.round(),
                m_rate_15: metrics.m_rate_15.round(),
            }
        })
        .collect::<Vec<_>>();

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** BROKERS LIST **********
//

#[get("/api/clusters/<cluster_id>/brokers?<timestamp>")]
pub fn brokers(cluster_id: ClusterId, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let brokers = brokers.unwrap();
    let broker_metrics = cache.metrics.get(&(cluster_id.to_owned(), "__TOTAL__".to_owned()))
        .unwrap_or_default();
    let mut result_data = Vec::with_capacity(brokers.len());
    for broker in brokers {
        let metric = broker_metrics.brokers
            .get(&broker.id)
            .cloned()
            .unwrap_or_default();
        result_data.push(json!((broker.id, broker.hostname, metric.b_rate_15.round(), metric.m_rate_15.round())));
    }

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** GROUP **********
//

#[derive(Debug)]
struct GroupInfo {
    state: String,
    members: usize,
    topics: HashSet<TopicName>,
}

impl GroupInfo {
    fn new(state: String, members: usize) -> GroupInfo {
        GroupInfo { state: state, members: members, topics: HashSet::new() }
    }

    fn new_empty() -> GroupInfo {
        GroupInfo { state: "Offsets only".to_owned(), members: 0, topics: HashSet::new() }
    }

    fn add_topic(&mut self, topic_name: TopicName) {
        self.topics.insert(topic_name);
    }
}

// TODO: add doc
// TODO: add limit
fn build_group_list<F>(cache: &Cache, filter: F) -> HashMap<(ClusterId, String), GroupInfo>
        where F: Fn(&ClusterId, &String) -> bool {
    let mut groups: HashMap<(ClusterId, String), GroupInfo> = cache.groups
        .lock_iter(|iter| {
            iter.filter(|&(&(ref c, ref g), _)| filter(c, g))
                .map(|(&(ref c, _), g)| ((c.clone(), g.name.clone()), GroupInfo::new(g.state.clone(), g.members.len())))
                .collect()
        });

    let offsets = cache.offsets.filter_clone_k(|&(ref c, ref g, _)| filter(c, g));
    for (cluster_id, group, t) in offsets {
        groups.entry((cluster_id, group)).or_insert_with(GroupInfo::new_empty).add_topic(t);
    }

    groups
}

#[get("/api/clusters/<cluster_id>/groups?<timestamp>")]
pub fn cluster_groups(cluster_id: ClusterId, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let groups = build_group_list(cache.inner(), |c, _| c == &cluster_id);

    let mut result_data = Vec::with_capacity(groups.len());
    for ((_cluster_id, group_name), info) in groups {
        result_data.push(json!((group_name, info.state, info.members, info.topics.len())));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/clusters/<cluster_id>/topics/<topic_name>/groups?<timestamp>")]
pub fn topic_groups(cluster_id: ClusterId, topic_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let brokers = cache.brokers.get(&cluster_id);
    if brokers.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let groups = build_group_list(cache.inner(), |c, _| c == &cluster_id);

    let mut result_data = Vec::with_capacity(groups.len());
    for ((_cluster_id, group_name), info) in groups {
        if !info.topics.contains(&topic_name.to_string()) {
            continue;
        }
        result_data.push(json!((group_name, info.state, info.members, info.topics.len())));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/clusters/<cluster_id>/groups/<group_name>/members?<timestamp>")]
pub fn group_members(cluster_id: ClusterId, group_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let group = cache.groups.get(&(cluster_id.clone(), group_name.to_string()));
    if group.is_none() {  // TODO: Improve here
        return CompressedJSON(json!({"data": []}));
    }

    let group = group.unwrap();

    let mut result_data = Vec::with_capacity(group.members.len());
    for member in group.members {
        result_data.push(json!((member.id, member.client_id, member.client_host)));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/clusters/<cluster_id>/groups/<group_name>/offsets?<timestamp>")]
pub fn group_offsets(cluster_id: ClusterId, group_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let offsets = cache.offsets_by_cluster_group(&cluster_id, group_name.as_str());

    let wms = time!("fetch wms", fetch_watermarks(&cluster_id, &offsets));
    let wms = match wms {
        Ok(wms) => wms,
        Err(e) => {
            error!("Error while fetching watermarks: {}", e);
            return CompressedJSON(json!({"data": []}));
        }
    };

    let mut result_data = Vec::with_capacity(offsets.len());
    for ((_cluster_id, _group, topic), partitions) in offsets {
        for (partition_id, &curr_offset) in partitions.iter().enumerate() {
            let (low, high) = match wms.get(&(topic.clone(), partition_id as i32)) {
                Some(&Ok((low_mark, high_mark))) => (low_mark, high_mark),
                _ => (-1, -1),
            };
            let (lag_shown, percentage_shown) = match (high - low, high - curr_offset) {
                (0, _) => ("Empty topic".to_owned(), "0.0%".to_owned()),
                (size, lag) if lag > size => ("Out of retention".to_owned(), "".to_owned()),
                (size, lag) => (lag.to_string(), format!("{:.1}%", (lag as f64) / (size as f64) * 100.0))
            };
            result_data.push(json!((topic.clone(), partition_id, high-low, low, high, curr_offset, lag_shown, percentage_shown)));
        }
    }

    CompressedJSON(json!({"data": result_data}))
}

fn fetch_watermarks(cluster_id: &ClusterId, offsets: &[((ClusterId, String, TopicName), Vec<i64>)])
        -> Result<HashMap<(TopicName, i32), KafkaResult<(i64, i64)>>> {
    let consumer = CONSUMERS.get_err(cluster_id)?;

    let cpu_pool = Builder::new().pool_size(32).create();

    let mut futures = Vec::new();

    for &((_, _, ref topic), ref partitions) in offsets {
        for partition_id in 0..partitions.len() {
            let consumer_clone = consumer.clone();
            let topic_clone = topic.clone();
            let wm_future = cpu_pool.spawn_fn(move || {
                let wms = consumer_clone.fetch_watermarks(&topic_clone, partition_id as i32, 10000);
                Ok::<_, ()>(((topic_clone, partition_id as i32), wms))  // never fail
            });
            futures.push(wm_future);
        }
    }

    let watermarks = future::join_all(futures).wait().unwrap()
        .into_iter()
        .collect::<HashMap<_, _>>();

    Ok(watermarks)
}

//
// ********** TOPIC TOPOLOGY **********
//

#[get("/api/clusters/<cluster_id>/topics/<topic_name>/topology?<timestamp>")]
pub fn topic_topology(cluster_id: ClusterId, topic_name: &RawStr, cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let partitions = cache.topics.get(&(cluster_id.to_owned(), topic_name.to_string()));
    if partitions.is_none() {
        return CompressedJSON(json!({"data": []}));
    }

    let topic_metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.to_string()))
        .unwrap_or_default();
    let partitions = partitions.unwrap();

    let mut result_data = Vec::with_capacity(partitions.len());
    for p in partitions {
        let partition_metrics = topic_metrics.brokers.get(&p.leader)
            .and_then(|broker_metrics| broker_metrics.partitions.get(p.id as usize))
            .cloned()
            .unwrap_or_default();
        result_data.push(json!((p.id, partition_metrics.size_bytes, p.leader, p.replicas, p.isr, p.error)));
    }

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** SEARCH **********
//

#[get("/api/search/consumer?<search>")]
pub fn consumer_search(search: OmnisearchFormParams, cache: State<Cache>) -> CompressedJSON {
    let groups = if search.regex {
        Regex::new(&search.string)
            .map(|r| build_group_list(&cache, |_, g| r.is_match(g)))
            .unwrap_or_default()
    } else {
        build_group_list(&cache, |_, g| g.contains(&search.string))
    };

    let mut result_data = Vec::with_capacity(groups.len());
    for ((cluster_id, group_name), info) in groups {
        result_data.push(json!((cluster_id, group_name, info.state, info.members, info.topics.len())));
    }

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/search/topic?<search>")]
pub fn topic_search(search: OmnisearchFormParams, cache: State<Cache>) -> CompressedJSON {
    let topics = if search.regex {
        Regex::new(&search.string)
            .map(|r| cache.topics.filter_clone(|&(_, ref name)| r.is_match(name)))
            .unwrap_or_default()
    } else {
        cache.topics.filter_clone(|&(_, ref name)| name.contains(&search.string))
    };

    let mut result_data = Vec::new();
    for ((cluster_id, topic_name), partitions) in topics {
        let metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.clone()))
            .unwrap_or_default()
            .aggregate_broker_metrics();
        let errors = partitions.iter().find(|p| p.error.is_some());
        result_data.push(json!((cluster_id, topic_name, partitions.len(), errors, metrics.b_rate_15, metrics.m_rate_15)));
    }

    CompressedJSON(json!({"data": result_data}))
}

//
// ********** INTERNALS **********
//

#[get("/api/internals/cache/brokers?<timestamp>")]
pub fn cache_brokers(cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let result_data = cache.brokers.lock_iter(|brokers_cache_entry| {
        brokers_cache_entry.map(|(cluster_id, brokers)| {
            (cluster_id.clone(), brokers.iter().map(|b| b.id).collect::<Vec<_>>())
        })
        .collect::<Vec<_>>()
    });

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/internals/cache/metrics?<timestamp>")]
pub fn cache_metrics(cache: State<Cache>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let result_data = cache.metrics.lock_iter(|metrics_cache_entry| {
        metrics_cache_entry
            .map(|(&(ref cluster_id, ref topic_id), metrics)| {
                (cluster_id.clone(), topic_id.clone(), metrics.brokers.len())
            }).collect::<Vec<_>>()
    });

    CompressedJSON(json!({"data": result_data}))
}

#[get("/api/internals/live_consumers?<timestamp>")]
pub fn live_consumers(live_consumers: State<LiveConsumerStore>, timestamp: &str) -> CompressedJSON {
    let _ = timestamp;
    let result_data = live_consumers.consumers().iter()
        .map(|consumer| (consumer.id(), consumer.cluster_id().to_owned(), consumer.topic().to_owned(),
                          consumer.last_poll().elapsed().as_secs()))
        .collect::<Vec<_>>();
    CompressedJSON(json!({"data": result_data}))
}
<|endoftext|>


<commit_before>use prelude::*;

use BytesError;

use winapi::shared::guiddef;

impl Uuid {
    /// Attempts to create a [`Uuid`] from a winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    fn from_guid(guid: guiddef::GUID) -> Result<Uuid, BytesError> {
        Uuid::from_fields(
            guid.Data1 as u32,
            guid.Data2 as u16,
            guid.Data3 as u16,
            &(guid.Data4 as [u8; 8]),
        )
    }

    /// Converts a [`Uuid`] into a little endian winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    fn to_guid(&self) -> guiddef::GUID {
        let (data1, data2, data3, data4) = self.to_fields_le();

        guiddef::GUID {
            Data1: data1,
            Data2: data2,
            Data3: data3,
            Data4: data4,
        }
    }
}
<commit_msg>fixed formatting<commit_after>use prelude::*;

use BytesError;

use winapi::shared::guiddef;

impl Uuid {
    /// Attempts to create a [`Uuid`] from a winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    fn from_guid(guid: guiddef::GUID) -> Result<Uuid, BytesError> {
        Uuid::from_fields(
            guid.Data1 as u32,
            guid.Data2 as u16,
            guid.Data3 as u16,
            &(guid.Data4 as [u8; 8]),
        )
    }

    /// Converts a [`Uuid`] into a little endian winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    fn to_guid(&self) -> guiddef::GUID {
        let (data1, data2, data3, data4) = self.to_fields_le();

        guiddef::GUID {
            Data1: data1,
            Data2: data2,
            Data3: data3,
            Data4: data4,
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>fix(compiler): use Display formatting of Input in hir::Id<commit_after><|endoftext|>


<commit_before>//! Utility functionality for integrating git hooks in the store
//!
//! Contains primitives to create a repository within the store path

pub fn mkrepo(store: &Store) -> Result<()> {
    unimplemented!()
}

pub fn hasrepo(store: &Store) -> bool {
    Repository::open(store.path()).is_ok()
}

<commit_msg>Implement util::mkrepo()<commit_after>//! Utility functionality for integrating git hooks in the store
//!
//! Contains primitives to create a repository within the store path

use git2::Repository;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;

pub fn mkrepo(store: &Store) -> Result<()> {
    let mut opts = RepositoryInitOptions::new();
    opts.bare(false);
    opts.no_reinit(true);
    opts.mkdir(false);
    opts.external_template(false);
    Repository::init_opts(store.path(), &opts)
        .map(|_| ())
        .map_err_into(GHEK::MkRepo)
}

pub fn hasrepo(store: &Store) -> bool {
    Repository::open(store.path()).is_ok()
}

<|endoftext|>


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

use target::TargetOptions;
use std::default::Default;

pub fn opts() -> TargetOptions {
    TargetOptions {
        pre_link_args: vec![
            // We want to be able to strip as much executable code as possible
            // from the linker command line, and this flag indicates to the
            // linker that it can avoid linking in dynamic libraries that don't
            // actually satisfy any symbols up to that point (as with many other
            // resolutions the linker does). This option only applies to all
            // following libraries so we're sure to pass it as one of the first
            // arguments.
            "-Wl,--as-needed".to_string(),

            // Always enable NX protection when it is available
            "-Wl,-z,noexecstack".to_string(),

            // Do not link libc
            "-nostdlib".to_string(),

            // Static link
            "-static".to_string()
        ],
        executables: true,
        relocation_model: "static".to_string(),
        disable_redzone: true,
        eliminate_frame_pointer: false,
        target_family: Some("redox".to_string()),
        linker_is_gnu: true,
        no_default_libraries: true,
        has_elf_tls: true,
        .. Default::default()
    }
}
<commit_msg>Use panic abort by default<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use PanicStrategy;
use target::TargetOptions;
use std::default::Default;

pub fn opts() -> TargetOptions {
    TargetOptions {
        pre_link_args: vec![
            // We want to be able to strip as much executable code as possible
            // from the linker command line, and this flag indicates to the
            // linker that it can avoid linking in dynamic libraries that don't
            // actually satisfy any symbols up to that point (as with many other
            // resolutions the linker does). This option only applies to all
            // following libraries so we're sure to pass it as one of the first
            // arguments.
            "-Wl,--as-needed".to_string(),

            // Always enable NX protection when it is available
            "-Wl,-z,noexecstack".to_string(),

            // Do not link libc
            "-nostdlib".to_string(),

            // Static link
            "-static".to_string()
        ],
        executables: true,
        relocation_model: "static".to_string(),
        disable_redzone: true,
        eliminate_frame_pointer: false,
        target_family: Some("redox".to_string()),
        linker_is_gnu: true,
        no_default_libraries: true,
        has_elf_tls: true,
        panic_strategy: PanicStrategy::Abort,
        .. Default::default()
    }
}
<|endoftext|>


<commit_before>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

const NAME: &'static str = "Alcibiades";
const AUTHOR: &'static str = "Evgeni Pandurski";
const VERSION: &'static str = "0.1";
const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    // The duration of the search in milliseconds.
    pub duration_millis: u64,
}

impl Default for SearchStatus {
    fn default() -> Self {
        SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
        }
    }
}

enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says so.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

struct Engine<T: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<T::HashTable>,
    position: T::SearchNode,
    searcher: T,
    queue: VecDeque<EngineReply>,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Nodes per second statistics.
    nps_stats: (u64, u64, u64),

    // Helps the engine decide when to show periodic progress reports.
    silent_since: SystemTime,

    // Whether the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // Tells the engine when it must stop thinking and play the best move.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,
}

impl<T: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<T> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(T::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<T> {
        let tt = Arc::new(T::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: T::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: T::new(tt),
            queue: VecDeque::new(),
            started_at: SystemTime::now(),
            status: SearchStatus { done: true, ..Default::default() },
            nps_stats: (0, 0, 0),
            silent_since: SystemTime::now(),
            is_pondering: false,
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        T::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = T::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: &GoParams) {
        self.terminate();

        // Validate `params.searchmoves`.
        //
        // TODO: What should we do with "mate"?
        let searchmoves = {
            let mut moves = vec![];
            let legal_moves = self.position.legal_moves();
            if !params.searchmoves.is_empty() {
                let mut v = params.searchmoves.clone();
                v.sort();
                for m in legal_moves.iter() {
                    if v.binary_search(&m.notation()).is_ok() {
                        moves.push(*m);
                    }
                }
            };
            if moves.is_empty() {
                legal_moves
            } else {
                moves
            }
        };

        // Start a new search.
        self.tt.new_search();
        self.started_at = SystemTime::now();
        self.status = Default::default();
        self.nps_stats = (self.nps_stats.0, 0, 0);
        self.silent_since = self.started_at;
        self.is_pondering = params.ponder;
        self.play_when = if params.infinite {
            PlayWhen::Never
        } else if params.movetime.is_some() {
            PlayWhen::MoveTime(params.movetime.unwrap())
        } else if params.nodes.is_some() {
            PlayWhen::Nodes(params.nodes.unwrap())
        } else if params.depth.is_some() {
            PlayWhen::Depth(min(params.depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      params.wtime,
                                                      params.btime,
                                                      params.winc,
                                                      params.binc,
                                                      params.movestogo))
        };
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let is_thinking = !self.status.done;

            // Wait for the search thread to do some work, and
            // hopefully update the status. (We must do this even when
            // the engine is not thinking -- in that case the next
            // line will just yield the CPU to another process.)
            self.wait_status_update(duration);

            // See if we must stop thinking and play.
            if is_thinking && !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => self.status.done || tm.must_play(),
                PlayWhen::MoveTime(t) => self.status.done || self.status.duration_millis >= t,
                PlayWhen::Nodes(n) => self.status.done || self.status.searched_nodes >= n,
                PlayWhen::Depth(d) => self.status.done || self.status.depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<T: SearchExecutor<ReportData = Vec<Variation>>> Engine<T> {
    fn queue_progress_info(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
        ]));
    }

    fn queue_pv(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, .. } = self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut pv = String::new();
            for m in moves {
                pv.push_str(&m.notation());
                pv.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
                InfoItem { info_type: "pv".to_string(), data: pv },
            ]));
        }
    }

    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If there is no best move, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
    }

    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(&r)
        }
    }

    fn process_report(&mut self, report: &SearchReport<Vec<Variation>>) {
        let duration_millis = {
            let d = self.started_at.elapsed().unwrap();
            1000 * d.as_secs() + (d.subsec_nanos() / 1_000_000) as u64
        };
        self.status = SearchStatus {
            done: report.done,
            depth: report.depth,
            searched_nodes: report.searched_nodes,
            duration_millis: duration_millis,
        };

        // Update `self.nps_stats` every 1000 milliseconds.
        let elapsed_millis = duration_millis - self.nps_stats.2;
        if elapsed_millis >= 1000 {
            let nodes = report.searched_nodes - self.nps_stats.1;
            self.nps_stats = (1000 * nodes / elapsed_millis, report.searched_nodes, duration_millis)
        }

        // Inform the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(report);
        }

        // If primary variations are provided with the report, show them.
        if !report.data.is_empty() {
            self.queue_pv(&report.data);
            self.silent_since = SystemTime::now();
        }

        // If nothing has happened for a while, show progress info.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_info();
            self.silent_since = SystemTime::now();
        }
    }
}

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<T: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<T>>()
}
<commit_msg>beautify<commit_after>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

const NAME: &'static str = "Alcibiades";
const AUTHOR: &'static str = "Evgeni Pandurski";

struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    // The duration of the search in milliseconds.
    pub duration_millis: u64,
}

impl Default for SearchStatus {
    fn default() -> Self {
        SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
        }
    }
}

enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says so.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

struct Engine<T: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<T::HashTable>,
    position: T::SearchNode,
    searcher: T,
    queue: VecDeque<EngineReply>,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Nodes per second statistics.
    nps_stats: (u64, u64, u64),

    // Helps the engine decide when to show periodic progress reports.
    silent_since: SystemTime,

    // Whether the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // Tells the engine when it must stop thinking and play the best move.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,
}

impl<T: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<T> {
    fn name() -> String {
        NAME.to_string()
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(T::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<T> {
        const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";
        let tt = Arc::new(T::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: T::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: T::new(tt),
            queue: VecDeque::new(),
            started_at: SystemTime::now(),
            status: SearchStatus { done: true, ..Default::default() },
            nps_stats: (0, 0, 0),
            silent_since: SystemTime::now(),
            is_pondering: false,
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        T::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = T::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: &GoParams) {
        self.terminate();

        // Validate `params.searchmoves`.
        //
        // TODO: What should we do with "mate"?
        let searchmoves = {
            let mut moves = vec![];
            let legal_moves = self.position.legal_moves();
            if !params.searchmoves.is_empty() {
                let mut v = params.searchmoves.clone();
                v.sort();
                for m in legal_moves.iter() {
                    if v.binary_search(&m.notation()).is_ok() {
                        moves.push(*m);
                    }
                }
            };
            if moves.is_empty() {
                legal_moves
            } else {
                moves
            }
        };

        // Start a new search.
        self.tt.new_search();
        self.started_at = SystemTime::now();
        self.status = Default::default();
        self.nps_stats = (self.nps_stats.0, 0, 0);
        self.silent_since = self.started_at;
        self.is_pondering = params.ponder;
        self.play_when = if params.infinite {
            PlayWhen::Never
        } else if params.movetime.is_some() {
            PlayWhen::MoveTime(params.movetime.unwrap())
        } else if params.nodes.is_some() {
            PlayWhen::Nodes(params.nodes.unwrap())
        } else if params.depth.is_some() {
            PlayWhen::Depth(min(params.depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      params.wtime,
                                                      params.btime,
                                                      params.winc,
                                                      params.binc,
                                                      params.movestogo))
        };
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let is_thinking = !self.status.done;

            // Wait for the search thread to do some work, and
            // hopefully update the status. (We must do this even when
            // the engine is not thinking -- in that case the next
            // line will just yield the CPU to another process.)
            self.wait_status_update(duration);

            // See if we must stop thinking and play.
            if is_thinking && !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => self.status.done || tm.must_play(),
                PlayWhen::MoveTime(t) => self.status.done || self.status.duration_millis >= t,
                PlayWhen::Nodes(n) => self.status.done || self.status.searched_nodes >= n,
                PlayWhen::Depth(d) => self.status.done || self.status.depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<T: SearchExecutor<ReportData = Vec<Variation>>> Engine<T> {
    fn queue_progress_info(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
        ]));
    }

    fn queue_pv(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, .. } = self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut pv = String::new();
            for m in moves {
                pv.push_str(&m.notation());
                pv.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
                InfoItem { info_type: "pv".to_string(), data: pv },
            ]));
        }
    }

    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If there is no best move, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
    }

    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(&r)
        }
    }

    fn process_report(&mut self, report: &SearchReport<Vec<Variation>>) {
        let duration_millis = {
            let d = self.started_at.elapsed().unwrap();
            1000 * d.as_secs() + (d.subsec_nanos() / 1_000_000) as u64
        };
        self.status = SearchStatus {
            done: report.done,
            depth: report.depth,
            searched_nodes: report.searched_nodes,
            duration_millis: duration_millis,
        };

        // Update `self.nps_stats` every 1000 milliseconds.
        let elapsed_millis = duration_millis - self.nps_stats.2;
        if elapsed_millis >= 1000 {
            let nodes = report.searched_nodes - self.nps_stats.1;
            self.nps_stats = (1000 * nodes / elapsed_millis, report.searched_nodes, duration_millis)
        }

        // Inform the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(report);
        }

        // If primary variations are provided with the report, show them.
        if !report.data.is_empty() {
            self.queue_pv(&report.data);
            self.silent_since = SystemTime::now();
        }

        // If nothing has happened for a while, show progress info.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_info();
            self.silent_since = SystemTime::now();
        }
    }
}

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<T: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<T>>()
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::uint;
use error::{MioResult, MioError};
use handler::Handler;
use io::IoHandle;
use net::{Socket, SockAddr};
use notify::Notify;
use os;
use poll::{Poll, IoEvent};
use timer::{Timer, Timeout, TimerResult};
use token::Token;

/// A lightweight event loop.
///
/// TODO:
/// - Enforce private tokens

#[deriving(Clone, Show)]
pub struct EventLoopConfig {
    pub io_poll_timeout_ms: uint,

    // == Notifications ==
    pub notify_capacity: uint,
    pub messages_per_tick: uint,

    // == Timer ==
    pub timer_tick_ms: u64,
    pub timer_wheel_size: uint,
    pub timer_capacity: uint,
}

impl Default for EventLoopConfig {
    fn default() -> EventLoopConfig {
        EventLoopConfig {
            io_poll_timeout_ms: 1_000,
            notify_capacity: 1_024,
            messages_per_tick: 64,
            timer_tick_ms: 100,
            timer_wheel_size: 1_024,
            timer_capacity: 65_536,
        }
    }
}

pub struct EventLoop<T, M: Send> {
    run: bool,
    poll: Poll,
    timer: Timer<T>,
    notify: Notify<M>,
    config: EventLoopConfig,
}

// Token used to represent notifications
const NOTIFY: Token = Token(uint::MAX);

impl<T, M: Send> EventLoop<T, M> {
    /// Initializes a new event loop. The event loop will not be running yet.
    pub fn new() -> MioResult<EventLoop<T, M>> {
        EventLoop::configured(Default::default())
    }

    pub fn configured(config: EventLoopConfig) -> MioResult<EventLoop<T, M>> {
        // Create the IO poller
        let mut poll = try!(Poll::new());

        // Create the timer
        let mut timer = Timer::new(
            config.timer_tick_ms,
            config.timer_wheel_size,
            config.timer_capacity);

        // Create cross thread notification queue
        let notify = try!(Notify::with_capacity(config.notify_capacity));

        // Register the notification wakeup FD with the IO poller
        try!(poll.register(&notify, NOTIFY));

        // Set the timer's starting time reference point
        timer.setup();

        Ok(EventLoop {
            run: true,
            poll: poll,
            timer: timer,
            notify: notify,
            config: config,
        })
    }

    /// Returns a sender that allows sending messages to the event loop in a
    /// thread-safe way, waking up the event loop if needed.
    pub fn channel(&self) -> EventLoopSender<M> {
        EventLoopSender::new(self.notify.clone())
    }

    /// After the requested time interval, the handler's `timeout` function
    /// will be called with the supplied token.
    pub fn timeout_ms(&mut self, token: T, delay: u64) -> TimerResult<Timeout> {
        self.timer.timeout_ms(token, delay)
    }

    /// If the supplied timeout has not been triggered, cancel it such that it
    /// will not be triggered in the future.
    pub fn clear_timeout(&mut self, timeout: Timeout) -> bool {
        self.timer.clear(timeout)
    }

    /// Tells the event loop to exit after it is done handling all events in the
    /// current iteration.
    pub fn shutdown(&mut self) {
        self.run = false;
    }

    /// Registers an IO handle with the event loop.
    pub fn register<H: IoHandle>(&mut self, io: &H, token: Token) -> MioResult<()> {
        self.poll.register(io, token)
    }

    /// Connects the socket to the specified address. When the operation
    /// completes, the handler will be notified with the supplied token.
    ///
    /// The goal of this method is to ensure that the event loop will always
    /// notify about the connection, even if the connection happens
    /// immediately. Otherwise, every consumer of the event loop would have
    /// to worry about possibly-immediate connection.
    pub fn connect<S: Socket>(&mut self, io: &S, addr: &SockAddr, token: Token) -> MioResult<()> {
        debug!("socket connect; addr={}", addr);

        // Attempt establishing the context. This may not complete immediately.
        if try!(os::connect(io.desc(), addr)) {
            // On some OSs, connecting to localhost succeeds immediately. In
            // this case, queue the writable callback for execution during the
            // next event loop tick.
            debug!("socket connected immediately; addr={}", addr);
        }

        // Register interest with socket on the event loop
        try!(self.register(io, token));

        Ok(())
    }

    /// Keep spinning the event loop indefinitely, and notify the handler whenever
    /// any of the registered handles are ready.
    pub fn run<H: Handler<T, M>>(&mut self, mut handler: H) -> EventLoopResult<H> {
        self.run = true;

        while self.run {
            // Execute ticks as long as the event loop is running
            match self.tick(&mut handler) {
                Err(e) => return Err(EventLoopError::new(handler, e)),
                _ => {}
            }
        }

        Ok(handler)
    }

    /// Spin the event loop once, with a timeout of one second, and notify the
    /// handler if any of the registered handles become ready during that
    /// time.
    pub fn run_once<H: Handler<T, M>>(&mut self, mut handler: H) -> EventLoopResult<H> {
        // Execute a single tick
        match self.tick(&mut handler) {
            Err(e) => return Err(EventLoopError::new(handler, e)),
            _ => {}
        }

        Ok(handler)
    }

    // Executes a single run of the event loop loop
    fn tick<H: Handler<T, M>>(&mut self, handler: &mut H) -> MioResult<()> {
        let mut messages;
        let mut pending;

        debug!("event loop tick");

        // Check the notify channel for any pending messages. If there are any,
        // avoid blocking when polling for IO events. Messages will be
        // processed after IO events.
        messages = self.notify.check(self.config.messages_per_tick, true);
        pending = messages > 0;

        // Check the registered IO handles for any new events. Each poll
        // is for one second, so a shutdown request can last as long as
        // one second before it takes effect.
        let events = try!(self.io_poll(pending));

        if !pending {
            // Indicate that the sleep period is over, also grab any additional
            // messages
            let remaining = self.config.messages_per_tick - messages;
            messages += self.notify.check(remaining, false);
        }

        self.io_process(handler, events);
        self.notify(handler, messages);
        self.timer_process(handler);

        Ok(())
    }

    #[inline]
    fn io_poll(&mut self, immediate: bool) -> MioResult<uint> {
        if immediate {
            self.poll.poll(0)
        } else {
            let mut sleep = self.timer.next_tick_in_ms() as uint;

            if sleep > self.config.io_poll_timeout_ms {
                sleep = self.config.io_poll_timeout_ms;
            }

            self.poll.poll(sleep)
        }
    }

    // Process IO events that have been previously polled
    fn io_process<H: Handler<T, M>>(&mut self, handler: &mut H, cnt: uint) {
        let mut i = 0u;

        // Iterate over the notifications. Each event provides the token
        // it was registered with (which usually represents, at least, the
        // handle that the event is about) as well as information about
        // what kind of event occurred (readable, writable, signal, etc.)
        while i < cnt {
            let evt = self.poll.event(i);

            debug!("event={}", evt);

            match evt.token() {
                NOTIFY => self.notify.cleanup(),
                _      => self.io_event(handler, evt)
            }

            i += 1;
        }
    }

    fn io_event<H: Handler<T, M>>(&mut self, handler: &mut H, evt: IoEvent) {
        let tok = evt.token();

        if evt.is_readable() {
            handler.readable(self, tok, evt.read_hint());
        }

        if evt.is_writable() {
            handler.writable(self, tok);
        }

        if evt.is_error() {
            println!(" + ERROR");
        }
    }

    fn notify<H: Handler<T, M>>(&mut self, handler: &mut H, mut cnt: uint) {
        while cnt > 0 {
            let msg = self.notify.poll()
                .expect("[BUG] at this point there should always be a message");

            handler.notify(self, msg);
            cnt -= 1;
        }
    }

    fn timer_process<H: Handler<T, M>>(&mut self, handler: &mut H) {
        let now = self.timer.now();

        loop {
            match self.timer.tick_to(now) {
                Some(t) => handler.timeout(self, t),
                _ => return
            }
        }
    }
}

#[deriving(Clone)]
pub struct EventLoopSender<M: Send> {
    notify: Notify<M>
}

impl<M: Send> EventLoopSender<M> {
    fn new(notify: Notify<M>) -> EventLoopSender<M> {
        EventLoopSender { notify: notify }
    }

    pub fn send(&self, msg: M) -> Result<(), M> {
        self.notify.notify(msg)
    }
}

pub type EventLoopResult<H> = Result<H, EventLoopError<H>>;

pub struct EventLoopError<H> {
    pub handler: H,
    pub error: MioError
}

impl<H> EventLoopError<H> {
    fn new(handler: H, error: MioError) -> EventLoopError<H> {
        EventLoopError {
            handler: handler,
            error: error
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str;
    use std::sync::Arc;
    use std::sync::atomics::{AtomicInt, SeqCst};
    use super::EventLoop;
    use io::{IoWriter, IoReader};
    use {io, buf, Buf, Handler, Token, ReadHint};

    type TestEventLoop = EventLoop<uint, ()>;

    struct Funtimes {
        rcount: Arc<AtomicInt>,
        wcount: Arc<AtomicInt>
    }

    impl Funtimes {
        fn new(rcount: Arc<AtomicInt>, wcount: Arc<AtomicInt>) -> Funtimes {
            Funtimes {
                rcount: rcount,
                wcount: wcount
            }
        }
    }

    impl Handler<uint, ()> for Funtimes {
        fn readable(&mut self, _event_loop: &mut TestEventLoop, token: Token, _hint: ReadHint) {
            (*self.rcount).fetch_add(1, SeqCst);
            assert_eq!(token, Token(10));
        }
    }

    #[test]
    fn test_readable() {
        let mut event_loop = EventLoop::new().ok().expect("Couldn't make event loop");

        let (mut reader, mut writer) = io::pipe().unwrap();

        let rcount = Arc::new(AtomicInt::new(0));
        let wcount = Arc::new(AtomicInt::new(0));
        let handler = Funtimes::new(rcount.clone(), wcount.clone());

        writer.write(&mut buf::wrap("hello".as_bytes())).unwrap();
        event_loop.register(&reader, Token(10)).unwrap();

        let _ = event_loop.run_once(handler);
        let mut b = buf::ByteBuf::new(16);

        assert_eq!((*rcount).load(SeqCst), 1);

        reader.read(&mut b).unwrap();
        b.flip();

        assert_eq!(str::from_utf8(b.bytes()).unwrap(), "hello");
    }
}
<commit_msg>Track Rust master<commit_after>use std::default::Default;
use std::uint;
use error::{MioResult, MioError};
use handler::Handler;
use io::IoHandle;
use net::{Socket, SockAddr};
use notify::Notify;
use os;
use poll::{Poll, IoEvent};
use timer::{Timer, Timeout, TimerResult};
use token::Token;

/// A lightweight event loop.
///
/// TODO:
/// - Enforce private tokens

#[deriving(Clone, Show)]
pub struct EventLoopConfig {
    pub io_poll_timeout_ms: uint,

    // == Notifications ==
    pub notify_capacity: uint,
    pub messages_per_tick: uint,

    // == Timer ==
    pub timer_tick_ms: u64,
    pub timer_wheel_size: uint,
    pub timer_capacity: uint,
}

impl Default for EventLoopConfig {
    fn default() -> EventLoopConfig {
        EventLoopConfig {
            io_poll_timeout_ms: 1_000,
            notify_capacity: 1_024,
            messages_per_tick: 64,
            timer_tick_ms: 100,
            timer_wheel_size: 1_024,
            timer_capacity: 65_536,
        }
    }
}

pub struct EventLoop<T, M: Send> {
    run: bool,
    poll: Poll,
    timer: Timer<T>,
    notify: Notify<M>,
    config: EventLoopConfig,
}

// Token used to represent notifications
const NOTIFY: Token = Token(uint::MAX);

impl<T, M: Send> EventLoop<T, M> {
    /// Initializes a new event loop. The event loop will not be running yet.
    pub fn new() -> MioResult<EventLoop<T, M>> {
        EventLoop::configured(Default::default())
    }

    pub fn configured(config: EventLoopConfig) -> MioResult<EventLoop<T, M>> {
        // Create the IO poller
        let mut poll = try!(Poll::new());

        // Create the timer
        let mut timer = Timer::new(
            config.timer_tick_ms,
            config.timer_wheel_size,
            config.timer_capacity);

        // Create cross thread notification queue
        let notify = try!(Notify::with_capacity(config.notify_capacity));

        // Register the notification wakeup FD with the IO poller
        try!(poll.register(&notify, NOTIFY));

        // Set the timer's starting time reference point
        timer.setup();

        Ok(EventLoop {
            run: true,
            poll: poll,
            timer: timer,
            notify: notify,
            config: config,
        })
    }

    /// Returns a sender that allows sending messages to the event loop in a
    /// thread-safe way, waking up the event loop if needed.
    pub fn channel(&self) -> EventLoopSender<M> {
        EventLoopSender::new(self.notify.clone())
    }

    /// After the requested time interval, the handler's `timeout` function
    /// will be called with the supplied token.
    pub fn timeout_ms(&mut self, token: T, delay: u64) -> TimerResult<Timeout> {
        self.timer.timeout_ms(token, delay)
    }

    /// If the supplied timeout has not been triggered, cancel it such that it
    /// will not be triggered in the future.
    pub fn clear_timeout(&mut self, timeout: Timeout) -> bool {
        self.timer.clear(timeout)
    }

    /// Tells the event loop to exit after it is done handling all events in the
    /// current iteration.
    pub fn shutdown(&mut self) {
        self.run = false;
    }

    /// Registers an IO handle with the event loop.
    pub fn register<H: IoHandle>(&mut self, io: &H, token: Token) -> MioResult<()> {
        self.poll.register(io, token)
    }

    /// Connects the socket to the specified address. When the operation
    /// completes, the handler will be notified with the supplied token.
    ///
    /// The goal of this method is to ensure that the event loop will always
    /// notify about the connection, even if the connection happens
    /// immediately. Otherwise, every consumer of the event loop would have
    /// to worry about possibly-immediate connection.
    pub fn connect<S: Socket>(&mut self, io: &S, addr: &SockAddr, token: Token) -> MioResult<()> {
        debug!("socket connect; addr={}", addr);

        // Attempt establishing the context. This may not complete immediately.
        if try!(os::connect(io.desc(), addr)) {
            // On some OSs, connecting to localhost succeeds immediately. In
            // this case, queue the writable callback for execution during the
            // next event loop tick.
            debug!("socket connected immediately; addr={}", addr);
        }

        // Register interest with socket on the event loop
        try!(self.register(io, token));

        Ok(())
    }

    /// Keep spinning the event loop indefinitely, and notify the handler whenever
    /// any of the registered handles are ready.
    pub fn run<H: Handler<T, M>>(&mut self, mut handler: H) -> EventLoopResult<H> {
        self.run = true;

        while self.run {
            // Execute ticks as long as the event loop is running
            match self.tick(&mut handler) {
                Err(e) => return Err(EventLoopError::new(handler, e)),
                _ => {}
            }
        }

        Ok(handler)
    }

    /// Spin the event loop once, with a timeout of one second, and notify the
    /// handler if any of the registered handles become ready during that
    /// time.
    pub fn run_once<H: Handler<T, M>>(&mut self, mut handler: H) -> EventLoopResult<H> {
        // Execute a single tick
        match self.tick(&mut handler) {
            Err(e) => return Err(EventLoopError::new(handler, e)),
            _ => {}
        }

        Ok(handler)
    }

    // Executes a single run of the event loop loop
    fn tick<H: Handler<T, M>>(&mut self, handler: &mut H) -> MioResult<()> {
        let mut messages;
        let mut pending;

        debug!("event loop tick");

        // Check the notify channel for any pending messages. If there are any,
        // avoid blocking when polling for IO events. Messages will be
        // processed after IO events.
        messages = self.notify.check(self.config.messages_per_tick, true);
        pending = messages > 0;

        // Check the registered IO handles for any new events. Each poll
        // is for one second, so a shutdown request can last as long as
        // one second before it takes effect.
        let events = try!(self.io_poll(pending));

        if !pending {
            // Indicate that the sleep period is over, also grab any additional
            // messages
            let remaining = self.config.messages_per_tick - messages;
            messages += self.notify.check(remaining, false);
        }

        self.io_process(handler, events);
        self.notify(handler, messages);
        self.timer_process(handler);

        Ok(())
    }

    #[inline]
    fn io_poll(&mut self, immediate: bool) -> MioResult<uint> {
        if immediate {
            self.poll.poll(0)
        } else {
            let mut sleep = self.timer.next_tick_in_ms() as uint;

            if sleep > self.config.io_poll_timeout_ms {
                sleep = self.config.io_poll_timeout_ms;
            }

            self.poll.poll(sleep)
        }
    }

    // Process IO events that have been previously polled
    fn io_process<H: Handler<T, M>>(&mut self, handler: &mut H, cnt: uint) {
        let mut i = 0u;

        // Iterate over the notifications. Each event provides the token
        // it was registered with (which usually represents, at least, the
        // handle that the event is about) as well as information about
        // what kind of event occurred (readable, writable, signal, etc.)
        while i < cnt {
            let evt = self.poll.event(i);

            debug!("event={}", evt);

            match evt.token() {
                NOTIFY => self.notify.cleanup(),
                _      => self.io_event(handler, evt)
            }

            i += 1;
        }
    }

    fn io_event<H: Handler<T, M>>(&mut self, handler: &mut H, evt: IoEvent) {
        let tok = evt.token();

        if evt.is_readable() {
            handler.readable(self, tok, evt.read_hint());
        }

        if evt.is_writable() {
            handler.writable(self, tok);
        }

        if evt.is_error() {
            println!(" + ERROR");
        }
    }

    fn notify<H: Handler<T, M>>(&mut self, handler: &mut H, mut cnt: uint) {
        while cnt > 0 {
            let msg = self.notify.poll()
                .expect("[BUG] at this point there should always be a message");

            handler.notify(self, msg);
            cnt -= 1;
        }
    }

    fn timer_process<H: Handler<T, M>>(&mut self, handler: &mut H) {
        let now = self.timer.now();

        loop {
            match self.timer.tick_to(now) {
                Some(t) => handler.timeout(self, t),
                _ => return
            }
        }
    }
}

#[deriving(Clone)]
pub struct EventLoopSender<M: Send> {
    notify: Notify<M>
}

impl<M: Send> EventLoopSender<M> {
    fn new(notify: Notify<M>) -> EventLoopSender<M> {
        EventLoopSender { notify: notify }
    }

    pub fn send(&self, msg: M) -> Result<(), M> {
        self.notify.notify(msg)
    }
}

pub type EventLoopResult<H> = Result<H, EventLoopError<H>>;

pub struct EventLoopError<H> {
    pub handler: H,
    pub error: MioError
}

impl<H> EventLoopError<H> {
    fn new(handler: H, error: MioError) -> EventLoopError<H> {
        EventLoopError {
            handler: handler,
            error: error
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicInt, SeqCst};
    use super::EventLoop;
    use io::{IoWriter, IoReader};
    use {io, buf, Buf, Handler, Token, ReadHint};

    type TestEventLoop = EventLoop<uint, ()>;

    struct Funtimes {
        rcount: Arc<AtomicInt>,
        wcount: Arc<AtomicInt>
    }

    impl Funtimes {
        fn new(rcount: Arc<AtomicInt>, wcount: Arc<AtomicInt>) -> Funtimes {
            Funtimes {
                rcount: rcount,
                wcount: wcount
            }
        }
    }

    impl Handler<uint, ()> for Funtimes {
        fn readable(&mut self, _event_loop: &mut TestEventLoop, token: Token, _hint: ReadHint) {
            (*self.rcount).fetch_add(1, SeqCst);
            assert_eq!(token, Token(10));
        }
    }

    #[test]
    fn test_readable() {
        let mut event_loop = EventLoop::new().ok().expect("Couldn't make event loop");

        let (mut reader, mut writer) = io::pipe().unwrap();

        let rcount = Arc::new(AtomicInt::new(0));
        let wcount = Arc::new(AtomicInt::new(0));
        let handler = Funtimes::new(rcount.clone(), wcount.clone());

        writer.write(&mut buf::wrap("hello".as_bytes())).unwrap();
        event_loop.register(&reader, Token(10)).unwrap();

        let _ = event_loop.run_once(handler);
        let mut b = buf::ByteBuf::new(16);

        assert_eq!((*rcount).load(SeqCst), 1);

        reader.read(&mut b).unwrap();
        b.flip();

        assert_eq!(str::from_utf8(b.bytes()).unwrap(), "hello");
    }
}
<|endoftext|>


<commit_before>/// The event system will have a trigger function that takes an
/// argument that decides if the event will be triggered this tick
/// or the next tick. The default should be next tick to prevent
/// inadvertent infinite loop bugs.
///
/// The event system can then schedule all events handlers that
/// do not mutate components to be run in parallel.
/// All handlers that do mutate the component list can be run
/// sequentially after that. Perhaps we can even schedule
/// those with non-overlapping mutable component lists to
/// run in parallel.

use std::collections::HashMap;
use std::any::Any;
use std::mem;
use shared_mutex::{ SharedMutex };

pub mod example;

lazy_static! {
	pub static ref EventQueues: SharedMutex<HashMap<String, &'static mut usize>> = SharedMutex::new(HashMap::new());
}

/// The event loop should trigger every n ms and execute any
/// events that are queued up. Every event has a function
/// that is called by the trigger! macro that puts the event
/// on the queue.
pub fn run_loop() {

}

pub fn event_queue_push<T>(event_name: &String, event: T) {
	let map = &mut EventQueues.write().unwrap();
	if map.contains_key(event_name) {
		let queue = transmute_from_generic_ref(map.get(event_name).unwrap());
		queue.push(event)
	} else {
		let queue = Box::new(vec![event]);
		map.insert(event_name.clone(), transmute_to_generic_ref(queue));
	}
}

/// src/events/mod.rs:45:38: 45:52 error: mutating transmuted &mut T from &T may cause undefined behavior,consider instead using an UnsafeCell, #[deny(mutable_transmutes)] on by default
fn transmute_from_generic_ref<'a,T>(value: &'a &mut usize) -> &'a mut Box<Vec<T>> {
	let r : &mut Box<Vec<T>> = unsafe { mem::transmute(value) };
	return r;
}

fn transmute_to_generic_ref<'a,T>(value: Box<Vec<T>>) -> &'a mut usize {
	let r : &'a mut usize = unsafe { mem::transmute(value) };
	return r;
}

///

/// Defines an event.
macro_rules! event {
	() => ()
}

/// Queues an event to be dispatched.
/// This means that the argument is put into the trigger queue for the
/// event and the event handlers will be invoked either at the next run loop.
macro_rules! trigger {
	//
	// Implementation idea: since we can't initialize events implicitly
	// in any way (not even through a rustc plugin atm) maybe we can
	// do it like lazy_static does it and initialize the event on the
	// first invocation of trigger.
	//
	// Initialisation that needs to happen:
	//   The trigger queue for this particular event needs to be initialized
	//   The run loop needs to be handed a reference to the trigger queue
	//
	() => ()
}

<commit_msg>try to simplify<commit_after>/// The event system will have a trigger function that takes an
/// argument that decides if the event will be triggered this tick
/// or the next tick. The default should be next tick to prevent
/// inadvertent infinite loop bugs.
///
/// The event system can then schedule all events handlers that
/// do not mutate components to be run in parallel.
/// All handlers that do mutate the component list can be run
/// sequentially after that. Perhaps we can even schedule
/// those with non-overlapping mutable component lists to
/// run in parallel.

use std::collections::HashMap;
use std::any::Any;
use std::mem;
use shared_mutex::{ SharedMutex };

pub mod example;

lazy_static! {
	pub static ref EventQueues: SharedMutex<HashMap<String, usize>> = SharedMutex::new(HashMap::new());
}

/// The event loop should trigger every n ms and execute any
/// events that are queued up. Every event has a function
/// that is called by the trigger! macro that puts the event
/// on the queue.
pub fn run_loop() {

}

pub fn event_queue_push<T>(event_name: &String, event: T) {
	let map = &mut EventQueues.write().unwrap();
	if map.contains_key(event_name) {
		let queue = transmute_from_generic_ref(*map.get_mut(event_name).unwrap());
		(*queue).push(event)
	} else {
		let queue = Box::new(vec![event]);
		map.insert(event_name.clone(), transmute_to_generic_ref(queue));
	}
}

/// What do I want?
/// I want to have a hashmap of references to a mutable vec of T
/// a reference is of size usize, so instead we store usizes that
/// we transmute to references to the Vec. A Box is a reference to
/// a Vec, and we want our queues to be allocated on the heap so
/// we use boxes.

/// src/events/mod.rs:45:38: 45:52 error: mutating transmuted &mut T from &T may cause undefined behavior,
/// consider instead using an UnsafeCell, #[deny(mutable_transmutes)] on by default
fn transmute_from_generic_ref<T>(value: usize) -> Box<Vec<T>> {
	let r : Box<Vec<T>> = unsafe { mem::transmute(value) };
	return r;
}

fn transmute_to_generic_ref<T>(value: Box<Vec<T>>) -> usize {
	let r : usize = unsafe { mem::transmute(value) };
	return r;
}

///

/// Defines an event.
macro_rules! event {
	() => ()
}

/// Queues an event to be dispatched.
/// This means that the argument is put into the trigger queue for the
/// event and the event handlers will be invoked either at the next run loop.
macro_rules! trigger {
	//
	// Implementation idea: since we can't initialize events implicitly
	// in any way (not even through a rustc plugin atm) maybe we can
	// do it like lazy_static does it and initialize the event on the
	// first invocation of trigger.
	//
	// Initialisation that needs to happen:
	//   The trigger queue for this particular event needs to be initialized
	//   The run loop needs to be handed a reference to the trigger queue
	//
	() => ()
}

<|endoftext|>


<commit_before>use common::R;
use dense_matrix::DenseMatrix;
use sparse_matrix::SparseMatrix;

use std::libc::{c_double, c_ulong, c_int, c_uint, c_void, malloc, calloc, realloc, free};
use std::cast;
use std::vec;

pub type lapack_int = c_int; // Adjust according to whether LP64 or ILP64 libraries are being linked.
pub type mkl_int = c_int;    // Adjust according to whether LP64 or ILP64 libraries are being linked.

#[fixed_stack_segment]
#[inline(never)]
pub fn init() {
  unsafe {
    init_allocator(cast::transmute(malloc), cast::transmute(calloc), cast::transmute(realloc), cast::transmute(free));
  }
}

#[fixed_stack_segment]
#[inline(never)]
pub fn solve_sparse_structurally_symmetric(sys: &SparseMatrix, rhs: &DenseMatrix, sym: bool) -> ~[R] {
  let n = sys.num_rows();
  unsafe {
    let (a, ia, ja) = sys.csr3_ptrs();
    let mut sol = vec::from_elem(n, 0 as R);
    let cpu_cores = 4; // TODO

    let stat =
      if sym {
        solve_sparse_symmetric_as_ut_csr3(n as mkl_int, ia, ja, a,
                                          rhs.col_maj_data_ptr(), rhs.num_cols as mkl_int,
                                          vec::raw::to_mut_ptr(sol),
                                          cpu_cores)
      } else {
        solve_sparse_structurally_symmetric_csr3(n as mkl_int, ia, ja, a,
                                                 rhs.col_maj_data_ptr(), rhs.num_cols as mkl_int,
                                                 vec::raw::to_mut_ptr(sol),
                                                 cpu_cores)
      };

    if stat != 0 {
      fail!(format!("solve_sparse_symmetric_as_ut_csr3 failed with error {:d}", stat));
    }

    sol
  }
}

#[link_args = "-Llib/mkl lib/lapack.o -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -lmkl_core -lmkl_intel_thread -lmkl_core -liomp5"]
extern {

  pub fn init_allocator(malloc_fn: *c_void, calloc_fn: *c_void, realloc_fn: *c_void, free_fn: *c_void);

  pub fn alloc_doubles(num_doubles: c_ulong) -> *mut c_double;

  pub fn free_doubles(mem: *mut c_double);

  pub fn copy_matrix(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  pub fn copy_upper_triangle(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  /* Dense symmetric matrix system solver. */
  pub fn solve_symmetric_as_col_maj_with_ut_sys(a: *mut c_double,
                                                n: lapack_int,
                                                b: *mut c_double,
                                                nrhs: lapack_int,
                                                ipiv: *mut lapack_int) -> lapack_int;

  /* Sparse symmetric matrix system solver. */
  pub fn solve_sparse_symmetric_as_ut_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                           b: *c_double, nrhs: mkl_int,
                                           x: *mut c_double,
                                           num_cpu_cores: c_uint) -> mkl_int;

  /* Sparse structurally symmetric matrix system solver. */
  pub fn solve_sparse_structurally_symmetric_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                                  b: *c_double, nrhs: mkl_int,
                                                  x: *mut c_double,
                                                  num_cpu_cores: c_uint) -> mkl_int;
}

<commit_msg>Add pthread linker arg.<commit_after>use common::R;
use dense_matrix::DenseMatrix;
use sparse_matrix::SparseMatrix;

use std::libc::{c_double, c_ulong, c_int, c_uint, c_void, malloc, calloc, realloc, free};
use std::cast;
use std::vec;

pub type lapack_int = c_int; // Adjust according to whether LP64 or ILP64 libraries are being linked.
pub type mkl_int = c_int;    // Adjust according to whether LP64 or ILP64 libraries are being linked.

#[fixed_stack_segment]
#[inline(never)]
pub fn init() {
  unsafe {
    init_allocator(cast::transmute(malloc), cast::transmute(calloc), cast::transmute(realloc), cast::transmute(free));
  }
}

#[fixed_stack_segment]
#[inline(never)]
pub fn solve_sparse_structurally_symmetric(sys: &SparseMatrix, rhs: &DenseMatrix, sym: bool) -> ~[R] {
  let n = sys.num_rows();

  unsafe {
    let (a, ia, ja) = sys.csr3_ptrs();
    let mut sol = vec::from_elem(n, 0 as R);
    let cpu_cores = 4; // TODO

    let stat =
      if sym {
        solve_sparse_symmetric_as_ut_csr3(n as mkl_int, ia, ja, a,
                                          rhs.col_maj_data_ptr(), rhs.num_cols as mkl_int,
                                          vec::raw::to_mut_ptr(sol),
                                          cpu_cores)
      } else {
        solve_sparse_structurally_symmetric_csr3(n as mkl_int, ia, ja, a,
                                                 rhs.col_maj_data_ptr(), rhs.num_cols as mkl_int,
                                                 vec::raw::to_mut_ptr(sol),
                                                 cpu_cores)
      };

    if stat != 0 {
      fail!(format!("solve_sparse_symmetric_as_ut_csr3 failed with error {:d}", stat));
    }

    sol
  }
}

#[link_args = "-Llib/mkl lib/lapack.o -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -lmkl_core -lmkl_intel_thread -lmkl_core -liomp5 -lpthread"]
extern {

  pub fn init_allocator(malloc_fn: *c_void, calloc_fn: *c_void, realloc_fn: *c_void, free_fn: *c_void);

  pub fn alloc_doubles(num_doubles: c_ulong) -> *mut c_double;

  pub fn free_doubles(mem: *mut c_double);

  pub fn copy_matrix(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  pub fn copy_upper_triangle(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  /* Dense symmetric matrix system solver. */
  pub fn solve_symmetric_as_col_maj_with_ut_sys(a: *mut c_double,
                                                n: lapack_int,
                                                b: *mut c_double,
                                                nrhs: lapack_int,
                                                ipiv: *mut lapack_int) -> lapack_int;

  /* Sparse symmetric matrix system solver. */
  pub fn solve_sparse_symmetric_as_ut_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                           b: *c_double, nrhs: mkl_int,
                                           x: *mut c_double,
                                           num_cpu_cores: c_uint) -> mkl_int;

  /* Sparse structurally symmetric matrix system solver. */
  pub fn solve_sparse_structurally_symmetric_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                                  b: *c_double, nrhs: mkl_int,
                                                  x: *mut c_double,
                                                  num_cpu_cores: c_uint) -> mkl_int;
}

<|endoftext|>