<commit_before>//! Manages the sliding windows.  Each time a tick is pushed in, the max/min price of each
//! of the monitored time perdiods is updated.  When ticks become older than the period of
//! the greatest window-newest tick, they are dropped off the structure.
//!
//! NOTE: This algorithm could be more efficiently impelented by a joint linked-list/binary-heap.
//! Traversing a Vector is currently used instead due to the complexity of implementing the
//! alternative.  However, if the latency and inefficniecy caused ever becomes a problem,
//! there are options available.

use std::collections::{HashMap, VecDeque};
use test;

use algobot_util::tick::Tick;

/// Contains the ticks with the max/min price, and the tick with the oldest timestamp that fits
/// in the window along with its index in the master VecDeque.
type Window = ((Option<Tick>, Option<Tick>), Option<(i64, Tick)>);

pub struct WindowManager {
    pub data: VecDeque<Tick>,
    pub windows: HashMap<i64, Window>,
    pub max_period: Option<i64>
}

/// Determines whether a tick is out of range of a window or not
fn out_of_range(period: i64, oldest_tick: Option<(i64, Tick)>, newest_tick: Tick) -> bool {
    if oldest_tick.is_none() {
        return false
    }
    newest_tick.timestamp - oldest_tick.unwrap().1.timestamp > period
}

impl WindowManager {
    pub fn new() -> WindowManager {
        WindowManager {
            data: VecDeque::new(),
            windows: HashMap::new(),
            max_period: None
        }
    }

    pub fn add_period(&mut self, period: i64) {
        self.windows.insert(period, ((None, None), None));
        // update max period
        match self.max_period {
            Some(_max_period) => {
                if period > _max_period {
                    self.max_period = Some(period);
                }
            },
            None => self.max_period = Some(period)
        }
    }

    pub fn remove_period(&mut self, period: i64) {
        let mut o: Option<i64> = None;
        for (k, _) in self.windows.iter() {
            if *k == period {
                o = Some(*k);
                break;
            }
        }
        if o.is_some() {
            self.windows.remove(&o.unwrap());
            // assume that max_period is already set since we're removing an element
            if self.max_period.unwrap() == period {
                // calculate new max period
                let mut max: Option<i64> = None;
                for (k, _) in self.windows.iter() {
                    if max.is_none() || *k > max.unwrap() {
                        max = Some(*k);
                    }
                }
                self.max_period = max;
            }
        }
    }

    pub fn push(&mut self, t: Tick) {
        // data is pushed on the back and popped off the front
        self.data.push_back(t);
        self.trim_overflow(t);

        // TODO: return info about new mins/maxes for periods
    }

    /// Removes ticks from the front of the queue until all timestamps are within
    /// the maximum period of the WindowManager.  For each tick that is removed,
    /// check to see if it was the maximum/minimum of any windows.  If it was,
    /// update that window.
    fn trim_overflow(&mut self, newest_tick: Tick) {
        // iterate over all windows and check if the tick is out of their range
        //
        // NOTE: Currently windows are iterated over in an arbitrary order making it impossible to
        // do optimizations like use the max of smaller windows to avoid having to iterate over
        // those parts of larger windows.
        for (&period, &((mut max_price_t, mut min_price_t), mut oldest_t_opt)) in self.windows.iter() {
            // if our current oldest tick is no longer in range of the window
            if out_of_range(period, oldest_t_opt, newest_tick) {
                oldest_t_opt = None;
                // index of previous oldest tick
                let mut i = oldest_t_opt.unwrap().0 + 1;
                // loop through ticks until we find one that's inside the window
                while let Some(newer_tick) = self.data.get(i as usize) {
                    i += 1;
                    if !out_of_range(period, Some((i, *newer_tick)), newest_tick) {
                        oldest_t_opt = Some((i, *newer_tick));
                        break;
                    } else {
                        // update the max/min prices if they were in an out-of-range tick
                        if max_price_t.is_some() && max_price_t.unwrap() == *newer_tick {
                            max_price_t = None
                        }
                        if min_price_t.is_some() && min_price_t.unwrap() == *newer_tick {
                            min_price_t = None
                        }
                    }
                }
                // at this point the oldest tick for the window is either None or accurate
                // (not accounting for the possibility of the newest tick)
            }

            if min_price_t == None || max_price_t == None {
                // must be at least one tick so we can .unwrap()
                let (min_price_t, max_price_t) = self.get_extreme_ticks(period).unwrap();
            } else {
                // check if newest_tick is a new max/min tick for the period
                if newest_tick.mid() < min_price_t.unwrap().mid() {
                    min_price_t = Some(newest_tick);
                }
                if newest_tick.mid() > max_price_t.unwrap().mid() {
                    max_price_t = Some(newest_tick);
                }
            }
        }

        // drop ticks that are outside of the range of the largest window
        while out_of_range(self.max_period.unwrap(), Some((0, *self.data.front().unwrap())), newest_tick) {
            self.data.pop_front();
        }
    }

    /// Gets the max and min tick in a window by iterating over every tick it contains.
    ///
    /// Returns Some(min_tick, max_tick)
    fn get_extreme_ticks(&self, period: i64) -> Option<(Tick, Tick)> {
        let newest_timestamp = self.data.back().unwrap().timestamp;
        let mut max_tick = Tick::null();
        let mut min_tick = Tick::null();
        let mut max_price = 0f64;
        let mut min_price = 0f64;

        for t in self.data.iter() {
            if newest_timestamp - t.timestamp > period {
                break;
            }

            let price = t.mid();
            if price > max_price {
                max_tick = *t;
                max_price = price;
            }

            if price < min_price || min_price == 0f64 {
                min_tick = *t;
                min_price = price;
            }
        }

        if min_price == 0f64 {
            return None
        }

        Some((min_tick, max_tick))
    }
}

#[test]
fn period_addition_removal() {
    let mut wm = WindowManager::new();
    wm.add_period(2);
    wm.add_period(4);
    assert_eq!(wm.windows.len(), 2);
    wm.remove_period(2);
    wm.remove_period(4);
}

#[test]
fn min_max_accuracy() {
    let mut wm = WindowManager::new();
    wm.add_period(5);
    // TODO
}
<commit_msg>Continued work on the WindowManager  - Output the periods that have changed as well as the new max, min,    oldest tick  - Convert the ((),_) Window to a struct<commit_after>//! Manages the sliding windows.  Each time a tick is pushed in, the max/min price of each
//! of the monitored time perdiods is updated.  When ticks become older than the period of
//! the greatest window-newest tick, they are dropped off the structure.
//!
//! NOTE: This algorithm could be more efficiently impelented by a joint linked-list/binary-heap.
//! Traversing a Vector is currently used instead due to the complexity of implementing the
//! alternative.  However, if the latency and inefficniecy caused ever becomes a problem,
//! there are options available.

use std::collections::{HashMap, VecDeque};
use test;

use algobot_util::tick::Tick;

/// Contains the computed information about the ticks in each time monitored time frame.
#[derive(Debug, Copy, Clone)]
pub struct Window {
    pub min_price_t: Option<Tick>,
    pub max_price_t: Option<Tick>,
    pub oldest_t:    Option<(i64, Tick)>
}

impl Window {
    pub fn new_empty() -> Window {
        Window {
            min_price_t: None,
            max_price_t: None,
            oldest_t:   None
        }
    }
}

pub struct WindowManager {
    pub data: VecDeque<Tick>,
    pub windows: HashMap<i64, Window>,
    pub max_period: Option<i64>
}

/// Determines whether a tick is out of range of a window or not
fn out_of_range(period: i64, oldest_tick: Tick, newest_tick: Tick) -> bool {
    newest_tick.timestamp - oldest_tick.timestamp > period
}

impl WindowManager {
    pub fn new() -> WindowManager {
        WindowManager {
            data: VecDeque::new(),
            windows: HashMap::new(),
            max_period: None
        }
    }

    pub fn add_period(&mut self, period: i64) {
        self.windows.insert(period, Window::new_empty());
        // update max period
        match self.max_period {
            Some(_max_period) => {
                if period > _max_period {
                    self.max_period = Some(period);
                }
            },
            None => self.max_period = Some(period)
        }
    }

    pub fn remove_period(&mut self, period: i64) {
        let mut o: Option<i64> = None;
        for (k, _) in self.windows.iter() {
            if *k == period {
                o = Some(*k);
                break;
            }
        }
        if o.is_some() {
            self.windows.remove(&o.unwrap());
            // assume that max_period is already set since we're removing an element
            if self.max_period.unwrap() == period {
                // calculate new max period
                let mut max: Option<i64> = None;
                for (k, _) in self.windows.iter() {
                    if max.is_none() || *k > max.unwrap() {
                        max = Some(*k);
                    }
                }
                self.max_period = max;
            }
        }
    }

    // Processes a new tick and returns a Vec containing periods whose mins/maxes have changed.
    pub fn push(&mut self, t: Tick) -> Vec<(i64, Window)> {
        // data is pushed on the back and popped off the front
        self.data.push_back(t);
        self.trim_overflow(t)
    }

    /// Removes ticks from the front of the queue until all timestamps are within
    /// the maximum period of the WindowManager.  For each tick that is removed,
    /// check to see if it was the maximum/minimum of any windows.  If it was,
    /// update that window.
    fn trim_overflow(&mut self, newest_tick: Tick) -> Vec<(i64, Window)> {
        let mut altered_periods = Vec::new();

        // iterate over all windows and check if the tick is out of their range
        //
        // NOTE: Currently windows are iterated over in an arbitrary order making it impossible to
        // do optimizations like use the max of smaller windows to avoid having to iterate over
        // those parts of larger windows.
        for (&period, &window) in self.windows.iter() {
            let mut window = window;
            // println!("Window before: {:?}", window);
            let mut altered = false;
            // if it's the first tick
            if window.oldest_t.is_none() {
                window.oldest_t = Some((0, newest_tick));
            } else {
                // if our current oldest tick is no longer in range of the window
                if out_of_range(period, window.oldest_t.unwrap().1, newest_tick) {
                    // index of previous oldest tick
                    let mut i = window.oldest_t.unwrap().0 + 1;

                    // loop through ticks until we find one that's inside the window
                    while let Some(newer_tick) = self.data.get(i as usize) {
                        i += 1;
                        if !out_of_range(period, *newer_tick, newest_tick) {
                            window.oldest_t = Some((i, *newer_tick));
                            break;
                        } else {
                            // update the max/min prices if they were in an out-of-range tick
                            if window.max_price_t.is_some() && window.max_price_t.unwrap() == *newer_tick {
                                window.max_price_t = None;
                                altered = true;
                            }
                            if window.min_price_t.is_some() && window.min_price_t.unwrap() == *newer_tick {
                                window.min_price_t = None;
                                altered = true;
                            }
                        }
                    }
                    // at this point the oldest tick for the window is either None or accurate
                    // (not accounting for the possibility of the newest tick)
                }
            }

            if window.min_price_t == None || window.max_price_t == None {
                // there is at least one tick so we can .unwrap()
                let extremes = self.get_extreme_ticks(period).unwrap();
                window.min_price_t = Some(extremes.0);
                window.max_price_t = Some(extremes.1);
                altered = true;
            } else {
                // check if newest_tick is a new max/min tick for the period
                if newest_tick.mid() < window.min_price_t.unwrap().mid() {
                    window.min_price_t = Some(newest_tick);
                    altered = true;
                }
                if newest_tick.mid() > window.max_price_t.unwrap().mid() {
                    window.max_price_t = Some(newest_tick);
                    altered = true;
                }
            }

            if altered {
                // Add the mutated window to the alteration list
                altered_periods.push((period, window));
                // println!("Window after: {:?}", window);
            }
        }

        // make the alterations queued in the loop
        for &(period, window) in altered_periods.iter() {
            self.windows.insert(period, window);
        }

        // drop ticks that are outside of the range of the largest window
        while out_of_range(self.max_period.unwrap(), *self.data.front().unwrap(), newest_tick) {
            self.data.pop_front();
        }

        altered_periods
    }

    /// Gets the max and min tick in a window by iterating over every tick it contains.
    ///
    /// Returns Some(min_tick, max_tick)
    fn get_extreme_ticks(&self, period: i64) -> Option<(Tick, Tick)> {
        let newest_timestamp = self.data.back().unwrap().timestamp;
        let mut max_tick = Tick::null();
        let mut min_tick = Tick::null();
        let mut max_price = 0f64;
        let mut min_price = 0f64;

        for t in self.data.iter() {
            if newest_timestamp - t.timestamp > period {
                break;
            }

            let price = t.mid();
            if price > max_price {
                max_tick = *t;
                max_price = price;
            }

            if price < min_price || min_price == 0f64 {
                min_tick = *t;
                min_price = price;
            }
        }

        if min_price == 0f64 {
            return None
        }

        Some((min_tick, max_tick))
    }
}

#[test]
fn period_addition_removal() {
    let mut wm = WindowManager::new();
    wm.add_period(2);
    wm.add_period(4);
    assert_eq!(wm.windows.len(), 2);
    wm.remove_period(2);
    wm.remove_period(4);
}

#[test]
fn min_max_accuracy() {
    let mut wm = WindowManager::new();
    wm.add_period(5);
    wm.add_period(10);
    println!("result of push: {:?}", wm.push(Tick{timestamp: 1i64, bid: 1f64, ask: 1f64}));
    println!("result of push: {:?}", wm.push(Tick{timestamp: 3i64, bid: 2f64, ask: 2f64}));
    // println!("result of push: {:?}", wm.push(Tick{timestamp: 1i64, bid: 1f64, ask: 1f64}));
    // println!("result of push: {:?}", wm.push(Tick{timestamp: 1i64, bid: 1f64, ask: 1f64}));
    panic!("{:?}", "s");
}

#[bench]
fn large_wm_processing_speed(b: &mut test::Bencher) {
    let mut wm = WindowManager::new();
    wm.add_period(5000000);
    // preload with 500,000 ticks
    for i in 0..5000000 {
        wm.push(Tick{timestamp: i as i64, bid: 1f64, ask: 1f64});
    }
    let mut i = 5000000;
    b.iter(|| {
        i = i + 1;
        wm.push(Tick{timestamp: i as i64, bid: 1f64, ask: 1f64});
    });
}
<|endoftext|>


<commit_before>use std::io::{Error, ErrorKind, Result};
use std::ffi::{CString};
use libc;
use message::Message;
use std::cmp::Ordering;
use std::ptr;
use std::boxed::Box;
use std::rc::Rc;
use std::ops::Deref;

enum CLcm {}

/// An LCM instance that hnadles publishing and subscribing, as well as encoding and decoding messages.
pub struct Lcm {
    lcm: *mut CLcm,
    subscriptions: Vec<Rc<LcmSubscription>>
}

type CLcmHandler = extern fn(*const CLcmRecvBuf, *const libc::c_char, *const libc::c_void);

#[derive(Eq, PartialEq, Hash)]
enum CLcmSubscription {}
pub struct LcmSubscription {
    subscription: *mut CLcmSubscription,
    handler: Box<FnMut(*const CLcmRecvBuf)>
}

#[repr(C)]
#[derive(Debug)]
struct CLcmRecvBuf {
    data: *const libc::c_void,
    data_size: libc::uint32_t,
    recv_utime: libc::int64_t,
    lcm: *const CLcm
}

#[link(name = "lcm")]
extern {
    fn lcm_create(provider: *const libc::c_char) -> *mut CLcm;

    fn lcm_destroy(lcm: *mut CLcm);

    fn lcm_get_fileno(lcm: *mut CLcm) -> libc::c_int;

    fn lcm_subscribe(lcm: *mut CLcm, channel: *const libc::c_char, handler: CLcmHandler, userdata: *const libc::c_void) -> *mut CLcmSubscription;

    fn lcm_unsubscribe(lcm: *mut CLcm, handler: *const CLcmSubscription) -> libc::c_int;

    fn lcm_publish(lcm: *mut CLcm, channel: *const libc::c_char, data: *const libc::c_void, datalen: libc::c_uint) -> libc::c_int;

    fn lcm_handle(lcm: *mut CLcm) -> libc::c_int;

    fn lcm_handle_timeout(lcm: *mut CLcm, timeout_millis: libc::c_int) -> libc::c_int;

    fn lcm_subscription_set_queue_capacity(handler: *const CLcmSubscription, num_messages: libc::c_int) -> libc::c_int;
}

impl Lcm {
    /// Creates a new `Lcm` instance.
    ///
    /// ```
    /// use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// ```
    pub fn new() -> Result<Lcm> {
        let lcm = unsafe { lcm_create(ptr::null()) };
        match lcm.is_null() {
            true => Err(Error::new(ErrorKind::Other, "Failed to initialize LCM.")),
            false => Ok(Lcm{
                lcm: lcm,
                subscriptions: Vec::new()
            })
        }
    }

    pub fn get_fileno(&self) -> libc::c_int {
        unsafe { lcm_get_fileno(self.lcm) }
    }

    /// Subscribes a callback to a particular topic.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// use std::sync::mpsc::channel;
    ///
    /// let mut lcm = Lcm::new().unwrap();
    /// let (tx, rx) = channel::<exlcm::ExampleT>();
    /// lcm.subscribe("POSITION", |pos| { tx.send(pos).unwrap(); }
    /// ```
    pub fn subscribe<M, F>(&mut self, channel: &str, mut callback: Box<F>) -> Rc<LcmSubscription>
        where M: Message + Default, F: FnMut(M) + 'static {
        let channel = CString::new(channel).unwrap();

        let handler = Box::new(move |rbuf: *const CLcmRecvBuf| {
            let mut msg = M::default();
            let buf = unsafe {
                let ref rbuf = *rbuf;
                let data = rbuf.data as *mut u8;
                let len = rbuf.data_size as usize;
                Vec::from_raw_parts(data, len, len)
            };
            match msg.decode_with_hash(&mut buf.as_slice()) {
                Ok(()) => callback(msg),
                Err(_) => {}
            }
        });

        let mut subscription = Rc::new(LcmSubscription{
            subscription: ptr::null_mut(),
            handler: handler
        });

        let user_data = (subscription.deref() as *const LcmSubscription) as *const libc::c_void;

        let c_subscription = unsafe { lcm_subscribe(self.lcm, channel.as_ptr(), Lcm::handler_callback::<M>, user_data) };

        Rc::get_mut(&mut subscription).unwrap().subscription = c_subscription;
        self.subscriptions.push(subscription.clone());

        subscription
    }

    /// Unsubscribes a message handler.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("POSITION", handler_function);
    /// // ...
    /// lcm.unsubscribe(handler);
    /// ```
    pub fn unsubscribe(&mut self, handler: LcmSubscription) -> Result<()> {
        let result = unsafe { lcm_unsubscribe(self.lcm, handler.subscription) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM: Failed to unsubscribe"))
        }
    }

    /// Publishes a message on the specified channel.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    ///
    /// let mut my_data = exlcm::ExampleT::new();
    /// my_data.timestamp = 0;
    /// my_data.position[0] = 1.0;
    /// my_data.position[1] = 2.0;
    /// my_data.position[2] = 3.0;
    ///
    /// lcm.publish("POSITION", &my_data).unwrap();
    /// ```
    pub fn publish(&mut self, channel: &str, message: &Message) -> Result<()> {
        let channel = CString::new(channel).unwrap();
        let buffer = message.encode_with_hash()?;
        let datalen = buffer.len() as libc::c_uint;
        let result = unsafe { lcm_publish(self.lcm, channel.as_ptr(), buffer.as_ptr() as *mut libc::c_void, datalen) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error"))
        }
    }

    /// Waits for and dispatches the next incoming message.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// loop {
    ///     lcm.handle().unwrap();
    /// }
    /// ```
    pub fn handle(&mut self) -> Result<()> {
        let result = unsafe { lcm_handle(self.lcm) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error"))
        }
    }

    /// Waits for and dispatches the next incoming message, up to a time limit.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// loop {
    ///     lcm.handle_timeout(1000).unwrap();
    /// }
    /// ```
    pub fn handle_timeout(&mut self, timeout_millis: i64) -> Result<()> {
        let timeout = timeout_millis as libc::c_int;
        let result = unsafe { lcm_handle_timeout(self.lcm, timeout) };
        match result.cmp(&0) {
            Ordering::Less => Err(Error::new(ErrorKind::Other, "LCM Error")),
            Ordering::Equal => Err(Error::new(ErrorKind::Other, "LCM Timeout")),
            Ordering::Greater => Ok(())
        }
    }

    /// Adjusts the maximum number of received messages that can be queued up for a subscription.
    /// The default is `30`.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("POSITION", handler_function);
    /// lcm.subscription_set_queue_capacity(handler, 30);
    /// ```
    pub fn subscription_set_queue_capacity(handler: Rc<LcmSubscription>, num_messages: usize) {
        let handler = handler.subscription;
        let num_messages = num_messages as libc::c_int;
        unsafe { lcm_subscription_set_queue_capacity(handler, num_messages) };
    }

    extern fn handler_callback<M>(rbuf: *const CLcmRecvBuf,
                                  _ /*channel*/: *const libc::c_char,
                                  user_data: *const libc::c_void)
        where M: Message {

        let sub = user_data as *mut LcmSubscription;
        let sub = unsafe { &mut *sub };
        (sub.handler)(rbuf);
    }
}

impl Drop for Lcm {
    fn drop(&mut self) {
        unsafe { lcm_destroy(self.lcm) };
    }
}<commit_msg>Small doc update.<commit_after>use std::io::{Error, ErrorKind, Result};
use std::ffi::{CString};
use libc;
use message::Message;
use std::cmp::Ordering;
use std::ptr;
use std::boxed::Box;
use std::rc::Rc;
use std::ops::Deref;

enum CLcm {}

/// An LCM instance that hnadles publishing and subscribing, as well as encoding and decoding messages.
pub struct Lcm {
    lcm: *mut CLcm,
    subscriptions: Vec<Rc<LcmSubscription>>
}

type CLcmHandler = extern fn(*const CLcmRecvBuf, *const libc::c_char, *const libc::c_void);

#[derive(Eq, PartialEq, Hash)]
enum CLcmSubscription {}
pub struct LcmSubscription {
    subscription: *mut CLcmSubscription,
    handler: Box<FnMut(*const CLcmRecvBuf)>
}

#[repr(C)]
#[derive(Debug)]
struct CLcmRecvBuf {
    data: *const libc::c_void,
    data_size: libc::uint32_t,
    recv_utime: libc::int64_t,
    lcm: *const CLcm
}

#[link(name = "lcm")]
extern {
    fn lcm_create(provider: *const libc::c_char) -> *mut CLcm;

    fn lcm_destroy(lcm: *mut CLcm);

    fn lcm_get_fileno(lcm: *mut CLcm) -> libc::c_int;

    fn lcm_subscribe(lcm: *mut CLcm, channel: *const libc::c_char, handler: CLcmHandler, userdata: *const libc::c_void) -> *mut CLcmSubscription;

    fn lcm_unsubscribe(lcm: *mut CLcm, handler: *const CLcmSubscription) -> libc::c_int;

    fn lcm_publish(lcm: *mut CLcm, channel: *const libc::c_char, data: *const libc::c_void, datalen: libc::c_uint) -> libc::c_int;

    fn lcm_handle(lcm: *mut CLcm) -> libc::c_int;

    fn lcm_handle_timeout(lcm: *mut CLcm, timeout_millis: libc::c_int) -> libc::c_int;

    fn lcm_subscription_set_queue_capacity(handler: *const CLcmSubscription, num_messages: libc::c_int) -> libc::c_int;
}

impl Lcm {
    /// Creates a new `Lcm` instance.
    ///
    /// ```
    /// use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// ```
    pub fn new() -> Result<Lcm> {
        let lcm = unsafe { lcm_create(ptr::null()) };
        match lcm.is_null() {
            true => Err(Error::new(ErrorKind::Other, "Failed to initialize LCM.")),
            false => Ok(Lcm{
                lcm: lcm,
                subscriptions: Vec::new()
            })
        }
    }

    pub fn get_fileno(&self) -> libc::c_int {
        unsafe { lcm_get_fileno(self.lcm) }
    }

    /// Subscribes a callback to a particular topic.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// use std::sync::mpsc::channel;
    ///
    /// let mut lcm = Lcm::new().unwrap();
    /// let (tx, rx) = channel::<exlcm::Example>();
    /// lcm.subscribe("POSITION", |pos| { tx.send(pos).unwrap(); }
    /// ```
    pub fn subscribe<M, F>(&mut self, channel: &str, mut callback: Box<F>) -> Rc<LcmSubscription>
        where M: Message + Default, F: FnMut(M) + 'static {
        let channel = CString::new(channel).unwrap();

        let handler = Box::new(move |rbuf: *const CLcmRecvBuf| {
            let mut msg = M::default();
            let buf = unsafe {
                let ref rbuf = *rbuf;
                let data = rbuf.data as *mut u8;
                let len = rbuf.data_size as usize;
                Vec::from_raw_parts(data, len, len)
            };
            match msg.decode_with_hash(&mut buf.as_slice()) {
                Ok(()) => callback(msg),
                Err(_) => {}
            }
        });

        let mut subscription = Rc::new(LcmSubscription{
            subscription: ptr::null_mut(),
            handler: handler
        });

        let user_data = (subscription.deref() as *const LcmSubscription) as *const libc::c_void;

        let c_subscription = unsafe { lcm_subscribe(self.lcm, channel.as_ptr(), Lcm::handler_callback::<M>, user_data) };

        Rc::get_mut(&mut subscription).unwrap().subscription = c_subscription;
        self.subscriptions.push(subscription.clone());

        subscription
    }

    /// Unsubscribes a message handler.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("POSITION", handler_function);
    /// // ...
    /// lcm.unsubscribe(handler);
    /// ```
    pub fn unsubscribe(&mut self, handler: LcmSubscription) -> Result<()> {
        let result = unsafe { lcm_unsubscribe(self.lcm, handler.subscription) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM: Failed to unsubscribe"))
        }
    }

    /// Publishes a message on the specified channel.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    ///
    /// let mut my_data = exlcm::Example::new();
    /// my_data.timestamp = 0;
    /// my_data.position[0] = 1.0;
    /// my_data.position[1] = 2.0;
    /// my_data.position[2] = 3.0;
    ///
    /// lcm.publish("POSITION", &my_data).unwrap();
    /// ```
    pub fn publish(&mut self, channel: &str, message: &Message) -> Result<()> {
        let channel = CString::new(channel).unwrap();
        let buffer = message.encode_with_hash()?;
        let datalen = buffer.len() as libc::c_uint;
        let result = unsafe { lcm_publish(self.lcm, channel.as_ptr(), buffer.as_ptr() as *mut libc::c_void, datalen) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error"))
        }
    }

    /// Waits for and dispatches the next incoming message.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// loop {
    ///     lcm.handle().unwrap();
    /// }
    /// ```
    pub fn handle(&mut self) -> Result<()> {
        let result = unsafe { lcm_handle(self.lcm) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error"))
        }
    }

    /// Waits for and dispatches the next incoming message, up to a time limit.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// loop {
    ///     lcm.handle_timeout(1000).unwrap();
    /// }
    /// ```
    pub fn handle_timeout(&mut self, timeout_millis: i64) -> Result<()> {
        let timeout = timeout_millis as libc::c_int;
        let result = unsafe { lcm_handle_timeout(self.lcm, timeout) };
        match result.cmp(&0) {
            Ordering::Less => Err(Error::new(ErrorKind::Other, "LCM Error")),
            Ordering::Equal => Err(Error::new(ErrorKind::Other, "LCM Timeout")),
            Ordering::Greater => Ok(())
        }
    }

    /// Adjusts the maximum number of received messages that can be queued up for a subscription.
    /// The default is `30`.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("POSITION", handler_function);
    /// lcm.subscription_set_queue_capacity(handler, 30);
    /// ```
    pub fn subscription_set_queue_capacity(handler: Rc<LcmSubscription>, num_messages: usize) {
        let handler = handler.subscription;
        let num_messages = num_messages as libc::c_int;
        unsafe { lcm_subscription_set_queue_capacity(handler, num_messages) };
    }

    extern fn handler_callback<M>(rbuf: *const CLcmRecvBuf,
                                  _ /*channel*/: *const libc::c_char,
                                  user_data: *const libc::c_void)
        where M: Message {

        let sub = user_data as *mut LcmSubscription;
        let sub = unsafe { &mut *sub };
        (sub.handler)(rbuf);
    }
}

impl Drop for Lcm {
    fn drop(&mut self) {
        unsafe { lcm_destroy(self.lcm) };
    }
}<|endoftext|>


<commit_before>//! Thread pool for blocking operations

use crate::loom::sync::{Arc, Condvar, Mutex};
use crate::loom::thread;
use crate::runtime::blocking::schedule::NoopSchedule;
use crate::runtime::blocking::shutdown;
use crate::runtime::builder::ThreadNameFn;
use crate::runtime::context;
use crate::runtime::task::{self, JoinHandle};
use crate::runtime::{Builder, Callback, Handle};

use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::time::Duration;

pub(crate) struct BlockingPool {
    spawner: Spawner,
    shutdown_rx: shutdown::Receiver,
}

#[derive(Clone)]
pub(crate) struct Spawner {
    inner: Arc<Inner>,
}

struct Inner {
    /// State shared between worker threads.
    shared: Mutex<Shared>,

    /// Pool threads wait on this.
    condvar: Condvar,

    /// Spawned threads use this name.
    thread_name: ThreadNameFn,

    /// Spawned thread stack size.
    stack_size: Option<usize>,

    /// Call after a thread starts.
    after_start: Option<Callback>,

    /// Call before a thread stops.
    before_stop: Option<Callback>,

    // Maximum number of threads.
    thread_cap: usize,

    // Customizable wait timeout.
    keep_alive: Duration,
}

struct Shared {
    queue: VecDeque<Task>,
    num_th: usize,
    num_idle: u32,
    num_notify: u32,
    shutdown: bool,
    shutdown_tx: Option<shutdown::Sender>,
    /// Prior to shutdown, we clean up JoinHandles by having each timed-out
    /// thread join on the previous timed-out thread. This is not strictly
    /// necessary but helps avoid Valgrind false positives, see
    /// <https://github.com/tokio-rs/tokio/commit/646fbae76535e397ef79dbcaacb945d4c829f666>
    /// for more information.
    last_exiting_thread: Option<thread::JoinHandle<()>>,
    /// This holds the JoinHandles for all running threads; on shutdown, the thread
    /// calling shutdown handles joining on these.
    worker_threads: HashMap<usize, thread::JoinHandle<()>>,
    /// This is a counter used to iterate worker_threads in a consistent order (for loom's
    /// benefit).
    worker_thread_index: usize,
}

pub(crate) struct Task {
    task: task::UnownedTask<NoopSchedule>,
    mandatory: Mandatory,
}

#[derive(PartialEq, Eq)]
pub(crate) enum Mandatory {
    #[cfg_attr(not(fs), allow(dead_code))]
    Mandatory,
    NonMandatory,
}

impl Task {
    pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
        Task { task, mandatory }
    }

    fn run(self) {
        self.task.run();
    }

    fn shutdown_or_run_if_mandatory(self) {
        match self.mandatory {
            Mandatory::NonMandatory => self.task.shutdown(),
            Mandatory::Mandatory => self.task.run(),
        }
    }
}

const KEEP_ALIVE: Duration = Duration::from_secs(10);

/// Runs the provided function on an executor dedicated to blocking operations.
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
/// in case of runtime shutdown.
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    let rt = context::current();
    rt.spawn_blocking(func)
}

cfg_fs! {
    #[cfg_attr(any(
        all(loom, not(test)), // the function is covered by loom tests
        test
    ), allow(dead_code))]
    /// Runs the provided function on an executor dedicated to blocking
    /// operations. Tasks will be scheduled as mandatory, meaning they are
    /// guaranteed to run unless a shutdown is already taking place. In case a
    /// shutdown is already taking place, `None` will be returned.
    pub(crate) fn spawn_mandatory_blocking<F, R>(func: F) -> Option<JoinHandle<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        let rt = context::current();
        rt.spawn_mandatory_blocking(func)
    }
}

// ===== impl BlockingPool =====

impl BlockingPool {
    pub(crate) fn new(builder: &Builder, thread_cap: usize) -> BlockingPool {
        let (shutdown_tx, shutdown_rx) = shutdown::channel();
        let keep_alive = builder.keep_alive.unwrap_or(KEEP_ALIVE);

        BlockingPool {
            spawner: Spawner {
                inner: Arc::new(Inner {
                    shared: Mutex::new(Shared {
                        queue: VecDeque::new(),
                        num_th: 0,
                        num_idle: 0,
                        num_notify: 0,
                        shutdown: false,
                        shutdown_tx: Some(shutdown_tx),
                        last_exiting_thread: None,
                        worker_threads: HashMap::new(),
                        worker_thread_index: 0,
                    }),
                    condvar: Condvar::new(),
                    thread_name: builder.thread_name.clone(),
                    stack_size: builder.thread_stack_size,
                    after_start: builder.after_start.clone(),
                    before_stop: builder.before_stop.clone(),
                    thread_cap,
                    keep_alive,
                }),
            },
            shutdown_rx,
        }
    }

    pub(crate) fn spawner(&self) -> &Spawner {
        &self.spawner
    }

    pub(crate) fn shutdown(&mut self, timeout: Option<Duration>) {
        let mut shared = self.spawner.inner.shared.lock();

        // The function can be called multiple times. First, by explicitly
        // calling `shutdown` then by the drop handler calling `shutdown`. This
        // prevents shutting down twice.
        if shared.shutdown {
            return;
        }

        shared.shutdown = true;
        shared.shutdown_tx = None;
        self.spawner.inner.condvar.notify_all();

        let last_exited_thread = std::mem::take(&mut shared.last_exiting_thread);
        let workers = std::mem::take(&mut shared.worker_threads);

        drop(shared);

        if self.shutdown_rx.wait(timeout) {
            let _ = last_exited_thread.map(|th| th.join());

            // Loom requires that execution be deterministic, so sort by thread ID before joining.
            // (HashMaps use a randomly-seeded hash function, so the order is nondeterministic)
            let mut workers: Vec<(usize, thread::JoinHandle<()>)> = workers.into_iter().collect();
            workers.sort_by_key(|(id, _)| *id);

            for (_id, handle) in workers.into_iter() {
                let _ = handle.join();
            }
        }
    }
}

impl Drop for BlockingPool {
    fn drop(&mut self) {
        self.shutdown(None);
    }
}

impl fmt::Debug for BlockingPool {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("BlockingPool").finish()
    }
}

// ===== impl Spawner =====

impl Spawner {
    pub(crate) fn spawn(&self, task: Task, rt: &Handle) -> Result<(), ()> {
        let mut shared = self.inner.shared.lock();

        if shared.shutdown {
            // Shutdown the task: it's fine to shutdown this task (even if
            // mandatory) because it was scheduled after the shutdown of the
            // runtime began.
            task.task.shutdown();

            // no need to even push this task; it would never get picked up
            return Err(());
        }

        shared.queue.push_back(task);

        if shared.num_idle == 0 {
            // No threads are able to process the task.

            if shared.num_th == self.inner.thread_cap {
                // At max number of threads
            } else {
                assert!(shared.shutdown_tx.is_some());
                let shutdown_tx = shared.shutdown_tx.clone();

                if let Some(shutdown_tx) = shutdown_tx {
                    let id = shared.worker_thread_index;

                    match self.spawn_thread(shutdown_tx, rt, id) {
                        Ok(handle) => {
                            shared.num_th += 1;
                            shared.worker_thread_index += 1;
                            shared.worker_threads.insert(id, handle);
                        }
                        Err(ref e) if is_temporary_os_thread_error(e) && shared.num_th > 0 => {
                            // OS temporarily failed to spawn a new thread.
                            // The task will be picked up eventually by a currently
                            // busy thread.
                        }
                        Err(e) => {
                            // The OS refused to spawn the thread and there is no thread
                            // to pick up the task that has just been pushed to the queue.
                            panic!("OS can't spawn worker thread: {}", e)
                        }
                    }
                }
            }
        } else {
            // Notify an idle worker thread. The notification counter
            // is used to count the needed amount of notifications
            // exactly. Thread libraries may generate spurious
            // wakeups, this counter is used to keep us in a
            // consistent state.
            shared.num_idle -= 1;
            shared.num_notify += 1;
            self.inner.condvar.notify_one();
        }

        Ok(())
    }

    fn spawn_thread(
        &self,
        shutdown_tx: shutdown::Sender,
        rt: &Handle,
        id: usize,
    ) -> std::io::Result<thread::JoinHandle<()>> {
        let mut builder = thread::Builder::new().name((self.inner.thread_name)());

        if let Some(stack_size) = self.inner.stack_size {
            builder = builder.stack_size(stack_size);
        }

        let rt = rt.clone();

        builder.spawn(move || {
            // Only the reference should be moved into the closure
            let _enter = crate::runtime::context::enter(rt.clone());
            rt.blocking_spawner.inner.run(id);
            drop(shutdown_tx);
        })
    }
}

// Tells whether the error when spawning a thread is temporary.
#[inline]
fn is_temporary_os_thread_error(error: &std::io::Error) -> bool {
    matches!(error.kind(), std::io::ErrorKind::WouldBlock)
}

impl Inner {
    fn run(&self, worker_thread_id: usize) {
        if let Some(f) = &self.after_start {
            f()
        }

        let mut shared = self.shared.lock();
        let mut join_on_thread = None;

        'main: loop {
            // BUSY
            while let Some(task) = shared.queue.pop_front() {
                drop(shared);
                task.run();

                shared = self.shared.lock();
            }

            // IDLE
            shared.num_idle += 1;

            while !shared.shutdown {
                let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap();

                shared = lock_result.0;
                let timeout_result = lock_result.1;

                if shared.num_notify != 0 {
                    // We have received a legitimate wakeup,
                    // acknowledge it by decrementing the counter
                    // and transition to the BUSY state.
                    shared.num_notify -= 1;
                    break;
                }

                // Even if the condvar "timed out", if the pool is entering the
                // shutdown phase, we want to perform the cleanup logic.
                if !shared.shutdown && timeout_result.timed_out() {
                    // We'll join the prior timed-out thread's JoinHandle after dropping the lock.
                    // This isn't done when shutting down, because the thread calling shutdown will
                    // handle joining everything.
                    let my_handle = shared.worker_threads.remove(&worker_thread_id);
                    join_on_thread = std::mem::replace(&mut shared.last_exiting_thread, my_handle);

                    break 'main;
                }

                // Spurious wakeup detected, go back to sleep.
            }

            if shared.shutdown {
                // Drain the queue
                while let Some(task) = shared.queue.pop_front() {
                    drop(shared);

                    task.shutdown_or_run_if_mandatory();

                    shared = self.shared.lock();
                }

                // Work was produced, and we "took" it (by decrementing num_notify).
                // This means that num_idle was decremented once for our wakeup.
                // But, since we are exiting, we need to "undo" that, as we'll stay idle.
                shared.num_idle += 1;
                // NOTE: Technically we should also do num_notify++ and notify again,
                // but since we're shutting down anyway, that won't be necessary.
                break;
            }
        }

        // Thread exit
        shared.num_th -= 1;

        // num_idle should now be tracked exactly, panic
        // with a descriptive message if it is not the
        // case.
        shared.num_idle = shared
            .num_idle
            .checked_sub(1)
            .expect("num_idle underflowed on thread exit");

        if shared.shutdown && shared.num_th == 0 {
            self.condvar.notify_one();
        }

        drop(shared);

        if let Some(f) = &self.before_stop {
            f()
        }

        if let Some(handle) = join_on_thread {
            let _ = handle.join();
        }
    }
}

impl fmt::Debug for Spawner {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("blocking::Spawner").finish()
    }
}
<commit_msg>blocking: `#[track_caller]` for spawn_blocking (#4616)<commit_after>//! Thread pool for blocking operations

use crate::loom::sync::{Arc, Condvar, Mutex};
use crate::loom::thread;
use crate::runtime::blocking::schedule::NoopSchedule;
use crate::runtime::blocking::shutdown;
use crate::runtime::builder::ThreadNameFn;
use crate::runtime::context;
use crate::runtime::task::{self, JoinHandle};
use crate::runtime::{Builder, Callback, Handle};

use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::time::Duration;

pub(crate) struct BlockingPool {
    spawner: Spawner,
    shutdown_rx: shutdown::Receiver,
}

#[derive(Clone)]
pub(crate) struct Spawner {
    inner: Arc<Inner>,
}

struct Inner {
    /// State shared between worker threads.
    shared: Mutex<Shared>,

    /// Pool threads wait on this.
    condvar: Condvar,

    /// Spawned threads use this name.
    thread_name: ThreadNameFn,

    /// Spawned thread stack size.
    stack_size: Option<usize>,

    /// Call after a thread starts.
    after_start: Option<Callback>,

    /// Call before a thread stops.
    before_stop: Option<Callback>,

    // Maximum number of threads.
    thread_cap: usize,

    // Customizable wait timeout.
    keep_alive: Duration,
}

struct Shared {
    queue: VecDeque<Task>,
    num_th: usize,
    num_idle: u32,
    num_notify: u32,
    shutdown: bool,
    shutdown_tx: Option<shutdown::Sender>,
    /// Prior to shutdown, we clean up JoinHandles by having each timed-out
    /// thread join on the previous timed-out thread. This is not strictly
    /// necessary but helps avoid Valgrind false positives, see
    /// <https://github.com/tokio-rs/tokio/commit/646fbae76535e397ef79dbcaacb945d4c829f666>
    /// for more information.
    last_exiting_thread: Option<thread::JoinHandle<()>>,
    /// This holds the JoinHandles for all running threads; on shutdown, the thread
    /// calling shutdown handles joining on these.
    worker_threads: HashMap<usize, thread::JoinHandle<()>>,
    /// This is a counter used to iterate worker_threads in a consistent order (for loom's
    /// benefit).
    worker_thread_index: usize,
}

pub(crate) struct Task {
    task: task::UnownedTask<NoopSchedule>,
    mandatory: Mandatory,
}

#[derive(PartialEq, Eq)]
pub(crate) enum Mandatory {
    #[cfg_attr(not(fs), allow(dead_code))]
    Mandatory,
    NonMandatory,
}

impl Task {
    pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
        Task { task, mandatory }
    }

    fn run(self) {
        self.task.run();
    }

    fn shutdown_or_run_if_mandatory(self) {
        match self.mandatory {
            Mandatory::NonMandatory => self.task.shutdown(),
            Mandatory::Mandatory => self.task.run(),
        }
    }
}

const KEEP_ALIVE: Duration = Duration::from_secs(10);

/// Runs the provided function on an executor dedicated to blocking operations.
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
/// in case of runtime shutdown.
#[track_caller]
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    let rt = context::current();
    rt.spawn_blocking(func)
}

cfg_fs! {
    #[cfg_attr(any(
        all(loom, not(test)), // the function is covered by loom tests
        test
    ), allow(dead_code))]
    /// Runs the provided function on an executor dedicated to blocking
    /// operations. Tasks will be scheduled as mandatory, meaning they are
    /// guaranteed to run unless a shutdown is already taking place. In case a
    /// shutdown is already taking place, `None` will be returned.
    pub(crate) fn spawn_mandatory_blocking<F, R>(func: F) -> Option<JoinHandle<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        let rt = context::current();
        rt.spawn_mandatory_blocking(func)
    }
}

// ===== impl BlockingPool =====

impl BlockingPool {
    pub(crate) fn new(builder: &Builder, thread_cap: usize) -> BlockingPool {
        let (shutdown_tx, shutdown_rx) = shutdown::channel();
        let keep_alive = builder.keep_alive.unwrap_or(KEEP_ALIVE);

        BlockingPool {
            spawner: Spawner {
                inner: Arc::new(Inner {
                    shared: Mutex::new(Shared {
                        queue: VecDeque::new(),
                        num_th: 0,
                        num_idle: 0,
                        num_notify: 0,
                        shutdown: false,
                        shutdown_tx: Some(shutdown_tx),
                        last_exiting_thread: None,
                        worker_threads: HashMap::new(),
                        worker_thread_index: 0,
                    }),
                    condvar: Condvar::new(),
                    thread_name: builder.thread_name.clone(),
                    stack_size: builder.thread_stack_size,
                    after_start: builder.after_start.clone(),
                    before_stop: builder.before_stop.clone(),
                    thread_cap,
                    keep_alive,
                }),
            },
            shutdown_rx,
        }
    }

    pub(crate) fn spawner(&self) -> &Spawner {
        &self.spawner
    }

    pub(crate) fn shutdown(&mut self, timeout: Option<Duration>) {
        let mut shared = self.spawner.inner.shared.lock();

        // The function can be called multiple times. First, by explicitly
        // calling `shutdown` then by the drop handler calling `shutdown`. This
        // prevents shutting down twice.
        if shared.shutdown {
            return;
        }

        shared.shutdown = true;
        shared.shutdown_tx = None;
        self.spawner.inner.condvar.notify_all();

        let last_exited_thread = std::mem::take(&mut shared.last_exiting_thread);
        let workers = std::mem::take(&mut shared.worker_threads);

        drop(shared);

        if self.shutdown_rx.wait(timeout) {
            let _ = last_exited_thread.map(|th| th.join());

            // Loom requires that execution be deterministic, so sort by thread ID before joining.
            // (HashMaps use a randomly-seeded hash function, so the order is nondeterministic)
            let mut workers: Vec<(usize, thread::JoinHandle<()>)> = workers.into_iter().collect();
            workers.sort_by_key(|(id, _)| *id);

            for (_id, handle) in workers.into_iter() {
                let _ = handle.join();
            }
        }
    }
}

impl Drop for BlockingPool {
    fn drop(&mut self) {
        self.shutdown(None);
    }
}

impl fmt::Debug for BlockingPool {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("BlockingPool").finish()
    }
}

// ===== impl Spawner =====

impl Spawner {
    pub(crate) fn spawn(&self, task: Task, rt: &Handle) -> Result<(), ()> {
        let mut shared = self.inner.shared.lock();

        if shared.shutdown {
            // Shutdown the task: it's fine to shutdown this task (even if
            // mandatory) because it was scheduled after the shutdown of the
            // runtime began.
            task.task.shutdown();

            // no need to even push this task; it would never get picked up
            return Err(());
        }

        shared.queue.push_back(task);

        if shared.num_idle == 0 {
            // No threads are able to process the task.

            if shared.num_th == self.inner.thread_cap {
                // At max number of threads
            } else {
                assert!(shared.shutdown_tx.is_some());
                let shutdown_tx = shared.shutdown_tx.clone();

                if let Some(shutdown_tx) = shutdown_tx {
                    let id = shared.worker_thread_index;

                    match self.spawn_thread(shutdown_tx, rt, id) {
                        Ok(handle) => {
                            shared.num_th += 1;
                            shared.worker_thread_index += 1;
                            shared.worker_threads.insert(id, handle);
                        }
                        Err(ref e) if is_temporary_os_thread_error(e) && shared.num_th > 0 => {
                            // OS temporarily failed to spawn a new thread.
                            // The task will be picked up eventually by a currently
                            // busy thread.
                        }
                        Err(e) => {
                            // The OS refused to spawn the thread and there is no thread
                            // to pick up the task that has just been pushed to the queue.
                            panic!("OS can't spawn worker thread: {}", e)
                        }
                    }
                }
            }
        } else {
            // Notify an idle worker thread. The notification counter
            // is used to count the needed amount of notifications
            // exactly. Thread libraries may generate spurious
            // wakeups, this counter is used to keep us in a
            // consistent state.
            shared.num_idle -= 1;
            shared.num_notify += 1;
            self.inner.condvar.notify_one();
        }

        Ok(())
    }

    fn spawn_thread(
        &self,
        shutdown_tx: shutdown::Sender,
        rt: &Handle,
        id: usize,
    ) -> std::io::Result<thread::JoinHandle<()>> {
        let mut builder = thread::Builder::new().name((self.inner.thread_name)());

        if let Some(stack_size) = self.inner.stack_size {
            builder = builder.stack_size(stack_size);
        }

        let rt = rt.clone();

        builder.spawn(move || {
            // Only the reference should be moved into the closure
            let _enter = crate::runtime::context::enter(rt.clone());
            rt.blocking_spawner.inner.run(id);
            drop(shutdown_tx);
        })
    }
}

// Tells whether the error when spawning a thread is temporary.
#[inline]
fn is_temporary_os_thread_error(error: &std::io::Error) -> bool {
    matches!(error.kind(), std::io::ErrorKind::WouldBlock)
}

impl Inner {
    fn run(&self, worker_thread_id: usize) {
        if let Some(f) = &self.after_start {
            f()
        }

        let mut shared = self.shared.lock();
        let mut join_on_thread = None;

        'main: loop {
            // BUSY
            while let Some(task) = shared.queue.pop_front() {
                drop(shared);
                task.run();

                shared = self.shared.lock();
            }

            // IDLE
            shared.num_idle += 1;

            while !shared.shutdown {
                let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap();

                shared = lock_result.0;
                let timeout_result = lock_result.1;

                if shared.num_notify != 0 {
                    // We have received a legitimate wakeup,
                    // acknowledge it by decrementing the counter
                    // and transition to the BUSY state.
                    shared.num_notify -= 1;
                    break;
                }

                // Even if the condvar "timed out", if the pool is entering the
                // shutdown phase, we want to perform the cleanup logic.
                if !shared.shutdown && timeout_result.timed_out() {
                    // We'll join the prior timed-out thread's JoinHandle after dropping the lock.
                    // This isn't done when shutting down, because the thread calling shutdown will
                    // handle joining everything.
                    let my_handle = shared.worker_threads.remove(&worker_thread_id);
                    join_on_thread = std::mem::replace(&mut shared.last_exiting_thread, my_handle);

                    break 'main;
                }

                // Spurious wakeup detected, go back to sleep.
            }

            if shared.shutdown {
                // Drain the queue
                while let Some(task) = shared.queue.pop_front() {
                    drop(shared);

                    task.shutdown_or_run_if_mandatory();

                    shared = self.shared.lock();
                }

                // Work was produced, and we "took" it (by decrementing num_notify).
                // This means that num_idle was decremented once for our wakeup.
                // But, since we are exiting, we need to "undo" that, as we'll stay idle.
                shared.num_idle += 1;
                // NOTE: Technically we should also do num_notify++ and notify again,
                // but since we're shutting down anyway, that won't be necessary.
                break;
            }
        }

        // Thread exit
        shared.num_th -= 1;

        // num_idle should now be tracked exactly, panic
        // with a descriptive message if it is not the
        // case.
        shared.num_idle = shared
            .num_idle
            .checked_sub(1)
            .expect("num_idle underflowed on thread exit");

        if shared.shutdown && shared.num_th == 0 {
            self.condvar.notify_one();
        }

        drop(shared);

        if let Some(f) = &self.before_stop {
            f()
        }

        if let Some(handle) = join_on_thread {
            let _ = handle.join();
        }
    }
}

impl fmt::Debug for Spawner {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("blocking::Spawner").finish()
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_id="p2dux#0.1"]
#![crate_type="rlib"]
#![desc = "All UX/Frontend-specific code in the p2d 2D-graphics library"]
#![license = "MIT"]
#![feature(globs)]

extern crate time = "time";
extern crate serialize = "serialize";
extern crate uuid = "uuid";
extern crate collections = "collections";
extern crate sdl2 = "sdl2";
extern crate p2d = "p2d";
use time::precise_time_ns;

pub mod gfx;
pub mod ui;
pub mod view;

pub struct TimeTracker {
    last_time: u64,
    now_time: u64,
    next_fps_time: u64,
    fps_ctr: uint,
    curr_fps: uint
}
impl TimeTracker {
    pub fn new() -> TimeTracker {
        let curr_time = precise_time_ns() / 1000000u64;
        let mut tt = TimeTracker {
            last_time: curr_time,
            now_time: curr_time,
            next_fps_time: curr_time + 1000u64,
            fps_ctr: 0,
            curr_fps: 0
        };
        tt.update();
        tt
    }
    pub fn update(&mut self) {
        self.last_time = self.now_time;
        self.now_time = precise_time_ns() / 1000000u64;
        if self.now_time >= self.next_fps_time {
            self.curr_fps = self.fps_ctr;
            self.fps_ctr = 0;
            self.next_fps_time = self.now_time + 1000u64;
        } else {
            self.fps_ctr += 1;
        }
    }
    pub fn get_curr_fps(&self) -> uint { self.curr_fps }
    pub fn get_ms_since(&self) -> uint { (self.now_time - self.last_time) as uint }
}
<commit_msg>pub fields in timetracker<commit_after>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_id="p2dux#0.1"]
#![crate_type="rlib"]
#![desc = "All UX/Frontend-specific code in the p2d 2D-graphics library"]
#![license = "MIT"]
#![feature(globs)]

extern crate time = "time";
extern crate serialize = "serialize";
extern crate uuid = "uuid";
extern crate collections = "collections";
extern crate sdl2 = "sdl2";
extern crate p2d = "p2d";
use time::precise_time_ns;

pub mod gfx;
pub mod ui;
pub mod view;

pub struct TimeTracker {
    pub last_time: u64,
    pub now_time: u64,
    pub next_fps_time: u64,
    pub fps_ctr: uint,
    pub curr_fps: uint
}
impl TimeTracker {
    pub fn new() -> TimeTracker {
        let curr_time = precise_time_ns() / 1000000u64;
        let mut tt = TimeTracker {
            last_time: curr_time,
            now_time: curr_time,
            next_fps_time: curr_time + 1000u64,
            fps_ctr: 0,
            curr_fps: 0
        };
        tt.update();
        tt
    }
    pub fn update(&mut self) {
        self.last_time = self.now_time;
        self.now_time = precise_time_ns() / 1000000u64;
        if self.now_time >= self.next_fps_time {
            self.curr_fps = self.fps_ctr;
            self.fps_ctr = 0;
            self.next_fps_time = self.now_time + 1000u64;
        } else {
            self.fps_ctr += 1;
        }
    }
    pub fn get_curr_fps(&self) -> uint { self.curr_fps }
    pub fn get_ms_since(&self) -> uint { (self.now_time - self.last_time) as uint }
}
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Node manager.

use super::{encode_contract_name, to_address_vec, to_low_u64_vec};
use super::ContractCallExt;
use libexecutor::executor::Executor;
use rand::{Rng, SeedableRng, StdRng};
use rustc_hex::ToHex;
use std::cmp::Ordering;
use std::iter;
use std::str::FromStr;
use util::{Address, H160};

const LIST_NODE: &'static [u8] = &*b"listNode()";
const LIST_STAKE: &'static [u8] = *&b"listStake()";
/// Each epoch is divided into 1000 slots, each slot represent one opportunity of block proposer
const EPOCH: u64 = 1000;

lazy_static! {
    static ref LIST_NODE_ENCODED: Vec<u8> = encode_contract_name(LIST_NODE);
    static ref LIST_STAKE_ENCODED: Vec<u8> = encode_contract_name(LIST_STAKE);
    static ref CONTRACT_ADDRESS: H160 = H160::from_str("00000000000000000000000000000000013241a2").unwrap();
}

pub fn party_seats<T>(parties: Vec<T>, seats: &[u64]) -> Vec<T>
where
    T: Clone,
{
    let mut party_seats: Vec<T> = Vec::new();
    let seats_len = seats.len();
    for (index, party) in parties.into_iter().enumerate() {
        if index < seats_len {
            party_seats.extend(iter::repeat(party).take(seats[index] as usize));
        }
    }
    party_seats
}

/// https://en.wikipedia.org/wiki/Largest_remainder_method
/// Hare quota
pub fn largest_remainder_electoral(votes: &Vec<u64>, seat_number: u64) -> Vec<u64> {
    let total = votes.iter().fold(0, |acc, &x| acc + x);
    // TODO: PLS fix me!!!
    let hare_quota = total as f64 / seat_number as f64;

    let votes_quota: Vec<f64> = votes
        .iter()
        .map(|vote| (*vote as f64) / hare_quota)
        .collect();

    // calculate automatic seats first
    let mut seats: Vec<u64> = votes_quota.iter().map(|v| v.floor() as u64).collect();

    let mut remainders: Vec<(f64, u64)> = votes_quota
        .iter()
        .enumerate()
        .map(|(i, v)| (v.fract(), i as u64))
        .collect();

    let remaining_seat: u64 = seat_number - seats.iter().fold(0, |acc, &x| acc + x);

    remainders.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
    remainders.reverse();

    let highest_remainder_seats: Vec<u64> = remainders
        .iter()
        .take(remaining_seat as usize)
        .map(|v| v.1)
        .collect();

    for index in highest_remainder_seats {
        seats[index as usize] += 1;
    }

    seats
}

pub struct NodeManager;

impl NodeManager {
    pub fn nodes(executor: &Executor) -> Vec<Address> {
        let output = executor.call_method_latest(&*CONTRACT_ADDRESS, &*LIST_NODE_ENCODED.as_slice());
        trace!(
            "node manager output: {:?}",
            ToHex::to_hex(output.as_slice())
        );

        let nodes: Vec<Address> = to_address_vec(&output);
        trace!("node manager nodes: {:?}", nodes);
        nodes
    }

    pub fn shuffle_node<T>(node_vec: &mut Vec<T>, rng_seed: u64) {
        let seed: &[_] = &[rng_seed as usize];
        let mut rng: StdRng = SeedableRng::from_seed(seed);

        for i in 0..node_vec.len() {
            let j: usize = rng.gen::<usize>() % (i + 1);
            node_vec.swap(i, j);
        }
    }

    pub fn stakes(executor: &Executor) -> Vec<u64> {
        let output = executor.call_method_latest(&*CONTRACT_ADDRESS, &*LIST_STAKE_ENCODED.as_slice());
        trace!("stakes output: {:?}", ToHex::to_hex(output.as_slice()));

        let stakes: Vec<u64> = to_low_u64_vec(&output);
        trace!("node manager stakes: {:?}", stakes);
        stakes
    }

    pub fn stake_nodes(exeuctor: &Executor) -> Vec<Address> {
        let nodes = NodeManager::nodes(&exeuctor);
        let stakes = NodeManager::stakes(&exeuctor);
        let total = stakes.iter().fold(0, |acc, &x| acc + x);
        let stake_nodes: Vec<Address>;

        if total == 0 {
            stake_nodes = nodes;
        } else {
            let total_seats = largest_remainder_electoral(&stakes, EPOCH);
            stake_nodes = party_seats(nodes, &total_seats);
        }
        stake_nodes
    }
=======
        }
    }
>>>>>>> c7fc76f931022b957b9dbb7dc0d8752dc5993729
}

#[cfg(test)]
mod tests {
    extern crate logger;
    extern crate mktemp;

    use super::{largest_remainder_electoral, party_seats, NodeManager};
    use std::str::FromStr;
    use tests::helpers::init_executor;
    use util::H160;

    #[test]
    fn test_node_manager_contract() {
        let executor = init_executor();
        let nodes = NodeManager::nodes(&executor);

        assert_eq!(
            nodes,
            vec![
                H160::from_str("50ad2b9d6946d9c75ae978534043e3021ee1bfb1").unwrap(),
                H160::from_str("eeb3a71c4046f63a941013f826fccc503be26b77").unwrap(),
                H160::from_str("a2bbb65d4f8c3ada29f7471abe416e18061127f3").unwrap(),
                H160::from_str("72eb1e258c9cdccebb7b62930a35cfb6ef4cd24b").unwrap(),
            ]
        )
    }

    #[test]
    fn test_largest_remainder_electoral() {
        let votes = vec![47_000, 16_000, 15_800, 12_000, 6_100, 3_100];
        let seats = 10;
        let total_seats = largest_remainder_electoral(&votes, seats);
        assert_eq!(vec![5, 2, 1, 1, 1, 0], total_seats);
    }

    #[test]
    fn test_largest_remainder_electoral_again() {
        let votes = vec![1500, 1500, 900, 500, 500, 200];
        let seats = 25;
        let total_seats = largest_remainder_electoral(&votes, seats);
        assert_eq!(vec![7, 7, 4, 3, 3, 1], total_seats);

        let new_seats = 26;
        let new_total_seats = largest_remainder_electoral(&votes, new_seats);
        assert_eq!(vec![8, 8, 5, 2, 2, 1], new_total_seats);
    }

    #[test]
    fn test_party_seats() {
        let parties = vec!["a", "b", "c"];
        let seats = vec![3, 5, 2, 2, 1];
        assert_eq!(
            party_seats(parties, &seats),
            vec!["a", "a", "a", "b", "b", "b", "b", "b", "c", "c"]
        );

        let parties = vec!["a", "b"];
        let seats = vec![2, 1];
        assert_eq!(party_seats(parties, &seats), vec!["a", "a", "b"]);

        let parties = vec!["a", "b", "c"];
        let seats = vec![2, 2];
        assert_eq!(party_seats(parties, &seats), vec!["a", "a", "b", "b"]);
    }
}
<commit_msg>remove rebase garbage<commit_after>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Node manager.

use super::{encode_contract_name, to_address_vec, to_low_u64_vec};
use super::ContractCallExt;
use libexecutor::executor::Executor;
use rand::{Rng, SeedableRng, StdRng};
use rustc_hex::ToHex;
use std::cmp::Ordering;
use std::iter;
use std::str::FromStr;
use util::{Address, H160};

const LIST_NODE: &'static [u8] = &*b"listNode()";
const LIST_STAKE: &'static [u8] = *&b"listStake()";
/// Each epoch is divided into 1000 slots, each slot represent one opportunity of block proposer
const EPOCH: u64 = 1000;

lazy_static! {
    static ref LIST_NODE_ENCODED: Vec<u8> = encode_contract_name(LIST_NODE);
    static ref LIST_STAKE_ENCODED: Vec<u8> = encode_contract_name(LIST_STAKE);
    static ref CONTRACT_ADDRESS: H160 = H160::from_str("00000000000000000000000000000000013241a2").unwrap();
}

pub fn party_seats<T>(parties: Vec<T>, seats: &[u64]) -> Vec<T>
where
    T: Clone,
{
    let mut party_seats: Vec<T> = Vec::new();
    let seats_len = seats.len();
    for (index, party) in parties.into_iter().enumerate() {
        if index < seats_len {
            party_seats.extend(iter::repeat(party).take(seats[index] as usize));
        }
    }
    party_seats
}

/// https://en.wikipedia.org/wiki/Largest_remainder_method
/// Hare quota
pub fn largest_remainder_electoral(votes: &Vec<u64>, seat_number: u64) -> Vec<u64> {
    let total = votes.iter().fold(0, |acc, &x| acc + x);
    // TODO: PLS fix me!!!
    let hare_quota = total as f64 / seat_number as f64;

    let votes_quota: Vec<f64> = votes
        .iter()
        .map(|vote| (*vote as f64) / hare_quota)
        .collect();

    // calculate automatic seats first
    let mut seats: Vec<u64> = votes_quota.iter().map(|v| v.floor() as u64).collect();

    let mut remainders: Vec<(f64, u64)> = votes_quota
        .iter()
        .enumerate()
        .map(|(i, v)| (v.fract(), i as u64))
        .collect();

    let remaining_seat: u64 = seat_number - seats.iter().fold(0, |acc, &x| acc + x);

    remainders.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
    remainders.reverse();

    let highest_remainder_seats: Vec<u64> = remainders
        .iter()
        .take(remaining_seat as usize)
        .map(|v| v.1)
        .collect();

    for index in highest_remainder_seats {
        seats[index as usize] += 1;
    }

    seats
}

pub struct NodeManager;

impl NodeManager {
    pub fn nodes(executor: &Executor) -> Vec<Address> {
        let output = executor.call_method_latest(&*CONTRACT_ADDRESS, &*LIST_NODE_ENCODED.as_slice());
        trace!(
            "node manager output: {:?}",
            ToHex::to_hex(output.as_slice())
        );

        let nodes: Vec<Address> = to_address_vec(&output);
        trace!("node manager nodes: {:?}", nodes);
        nodes
    }

    pub fn shuffle_node<T>(node_vec: &mut Vec<T>, rng_seed: u64) {
        let seed: &[_] = &[rng_seed as usize];
        let mut rng: StdRng = SeedableRng::from_seed(seed);

        for i in 0..node_vec.len() {
            let j: usize = rng.gen::<usize>() % (i + 1);
            node_vec.swap(i, j);
        }
    }

    pub fn stakes(executor: &Executor) -> Vec<u64> {
        let output = executor.call_method_latest(&*CONTRACT_ADDRESS, &*LIST_STAKE_ENCODED.as_slice());
        trace!("stakes output: {:?}", ToHex::to_hex(output.as_slice()));

        let stakes: Vec<u64> = to_low_u64_vec(&output);
        trace!("node manager stakes: {:?}", stakes);
        stakes
    }

    pub fn stake_nodes(exeuctor: &Executor) -> Vec<Address> {
        let nodes = NodeManager::nodes(&exeuctor);
        let stakes = NodeManager::stakes(&exeuctor);
        let total = stakes.iter().fold(0, |acc, &x| acc + x);
        let stake_nodes: Vec<Address>;

        if total == 0 {
            stake_nodes = nodes;
        } else {
            let total_seats = largest_remainder_electoral(&stakes, EPOCH);
            stake_nodes = party_seats(nodes, &total_seats);
        }
        stake_nodes
    }

#[cfg(test)]
mod tests {
    extern crate logger;
    extern crate mktemp;

    use super::{largest_remainder_electoral, party_seats, NodeManager};
    use std::str::FromStr;
    use tests::helpers::init_executor;
    use util::H160;

    #[test]
    fn test_node_manager_contract() {
        let executor = init_executor();
        let nodes = NodeManager::nodes(&executor);

        assert_eq!(
            nodes,
            vec![
                H160::from_str("50ad2b9d6946d9c75ae978534043e3021ee1bfb1").unwrap(),
                H160::from_str("eeb3a71c4046f63a941013f826fccc503be26b77").unwrap(),
                H160::from_str("a2bbb65d4f8c3ada29f7471abe416e18061127f3").unwrap(),
                H160::from_str("72eb1e258c9cdccebb7b62930a35cfb6ef4cd24b").unwrap(),
            ]
        )
    }

    #[test]
    fn test_largest_remainder_electoral() {
        let votes = vec![47_000, 16_000, 15_800, 12_000, 6_100, 3_100];
        let seats = 10;
        let total_seats = largest_remainder_electoral(&votes, seats);
        assert_eq!(vec![5, 2, 1, 1, 1, 0], total_seats);
    }

    #[test]
    fn test_largest_remainder_electoral_again() {
        let votes = vec![1500, 1500, 900, 500, 500, 200];
        let seats = 25;
        let total_seats = largest_remainder_electoral(&votes, seats);
        assert_eq!(vec![7, 7, 4, 3, 3, 1], total_seats);

        let new_seats = 26;
        let new_total_seats = largest_remainder_electoral(&votes, new_seats);
        assert_eq!(vec![8, 8, 5, 2, 2, 1], new_total_seats);
    }

    #[test]
    fn test_party_seats() {
        let parties = vec!["a", "b", "c"];
        let seats = vec![3, 5, 2, 2, 1];
        assert_eq!(
            party_seats(parties, &seats),
            vec!["a", "a", "a", "b", "b", "b", "b", "b", "c", "c"]
        );

        let parties = vec!["a", "b"];
        let seats = vec![2, 1];
        assert_eq!(party_seats(parties, &seats), vec!["a", "a", "b"]);

        let parties = vec!["a", "b", "c"];
        let seats = vec![2, 2];
        assert_eq!(party_seats(parties, &seats), vec!["a", "a", "b", "b"]);
    }
}
<|endoftext|>


<commit_before>//! A Timer provides all the capabilities necessary for doing speedrun attempts.

use super::{output_str, output_time, output_time_span, output_vec};
use crate::run::{NullableOwnedRun, OwnedRun};
use crate::shared_timer::OwnedSharedTimer;
use livesplit_core::run::saver;
use livesplit_core::{Run, Time, TimeSpan, Timer, TimerPhase, TimingMethod};
use std::os::raw::c_char;
use std::ptr;

/// type
pub type OwnedTimer = Box<Timer>;
/// type
pub type NullableOwnedTimer = Option<OwnedTimer>;

/// Creates a new Timer based on a Run object storing all the information
/// about the splits. The Run object needs to have at least one segment, so
/// that the Timer can store the final time. If a Run object with no
/// segments is provided, the Timer creation fails and <NULL> is returned.
#[no_mangle]
pub extern "C" fn Timer_new(run: OwnedRun) -> NullableOwnedTimer {
    Timer::new(*run).ok().map(Box::new)
}

/// Consumes the Timer and creates a Shared Timer that can be shared across
/// multiple threads with multiple owners.
#[no_mangle]
pub extern "C" fn Timer_into_shared(this: OwnedTimer) -> OwnedSharedTimer {
    Box::new((*this).into_shared())
}

/// Takes out the Run from the Timer and resets the current attempt if there
/// is one in progress. If the splits are to be updated, all the information
/// of the current attempt is stored in the Run's history. Otherwise the
/// current attempt's information is discarded.
#[no_mangle]
pub extern "C" fn Timer_into_run(this: OwnedTimer, update_splits: bool) -> OwnedRun {
    Box::new((*this).into_run(update_splits))
}

/// drop
#[no_mangle]
pub extern "C" fn Timer_drop(this: OwnedTimer) {
    drop(this);
}

/// Replaces the Run object used by the Timer with the Run object provided. If
/// the Run provided contains no segments, it can't be used for timing and is
/// not being modified. Otherwise the Run that was in use by the Timer gets
/// stored in the Run object provided. Before the Run is returned, the current
/// attempt is reset and the splits are being updated depending on the
/// `update_splits` parameter. The return value indicates whether the Run got
/// replaced successfully.
#[no_mangle]
pub unsafe extern "C" fn Timer_replace_run(
    this: &mut Timer,
    run: &mut Run,
    update_splits: bool,
) -> bool {
    // This working correctly relies on panic = "abort",
    // as a panic would leave the run in an uninitialized state.
    let result = this.replace_run(ptr::read(run), update_splits);
    let was_successful = result.is_ok();
    let result = match result {
        Ok(r) | Err(r) => r,
    };
    ptr::write(run, result);
    was_successful
}

/// Sets the Run object used by the Timer with the Run object provided. If the
/// Run provided contains no segments, it can't be used for timing and gets
/// returned again. If the Run object can be set, the original Run object in use
/// by the Timer is disposed by this method and <NULL> is returned.
#[no_mangle]
pub extern "C" fn Timer_set_run(this: &mut Timer, run: OwnedRun) -> NullableOwnedRun {
    this.set_run(*run).err().map(Box::new)
}

/// Starts the Timer if there is no attempt in progress. If that's not the
/// case, nothing happens.
#[no_mangle]
pub extern "C" fn Timer_start(this: &mut Timer) {
    this.start();
}

/// If an attempt is in progress, stores the current time as the time of the
/// current split. The attempt ends if the last split time is stored.
#[no_mangle]
pub extern "C" fn Timer_split(this: &mut Timer) {
    this.split();
}

/// Starts a new attempt or stores the current time as the time of the
/// current split. The attempt ends if the last split time is stored.
#[no_mangle]
pub extern "C" fn Timer_split_or_start(this: &mut Timer) {
    this.split_or_start();
}

/// Skips the current split if an attempt is in progress and the
/// current split is not the last split.
#[no_mangle]
pub extern "C" fn Timer_skip_split(this: &mut Timer) {
    this.skip_split();
}

/// Removes the split time from the last split if an attempt is in progress
/// and there is a previous split. The Timer Phase also switches to
/// `Running` if it previously was `Ended`.
#[no_mangle]
pub extern "C" fn Timer_undo_split(this: &mut Timer) {
    this.undo_split();
}

/// Resets the current attempt if there is one in progress. If the splits
/// are to be updated, all the information of the current attempt is stored
/// in the Run's history. Otherwise the current attempt's information is
/// discarded.
#[no_mangle]
pub extern "C" fn Timer_reset(this: &mut Timer, update_splits: bool) {
    this.reset(update_splits);
}

/// Resets the current attempt if there is one in progress. The splits are
/// updated such that the current attempt's split times are being stored as
/// the new Personal Best.
#[no_mangle]
pub extern "C" fn Timer_reset_and_set_attempt_as_pb(this: &mut Timer) {
    this.reset_and_set_attempt_as_pb();
}

/// Pauses an active attempt that is not paused.
#[no_mangle]
pub extern "C" fn Timer_pause(this: &mut Timer) {
    this.pause();
}

/// Resumes an attempt that is paused.
#[no_mangle]
pub extern "C" fn Timer_resume(this: &mut Timer) {
    this.resume();
}

/// Toggles an active attempt between `Paused` and `Running`.
#[no_mangle]
pub extern "C" fn Timer_toggle_pause(this: &mut Timer) {
    this.toggle_pause();
}

/// Toggles an active attempt between `Paused` and `Running` or starts an
/// attempt if there's none in progress.
#[no_mangle]
pub extern "C" fn Timer_toggle_pause_or_start(this: &mut Timer) {
    this.toggle_pause_or_start();
}

/// Removes all the pause times from the current time. If the current
/// attempt is paused, it also resumes that attempt. Additionally, if the
/// attempt is finished, the final split time is adjusted to not include the
/// pause times as well.
///
/// # Warning
///
/// This behavior is not entirely optimal, as generally only the final split
/// time is modified, while all other split times are left unmodified, which
/// may not be what actually happened during the run.
#[no_mangle]
pub extern "C" fn Timer_undo_all_pauses(this: &mut Timer) {
    this.undo_all_pauses();
}

/// Returns the currently selected Timing Method.
#[no_mangle]
pub extern "C" fn Timer_current_timing_method(this: &Timer) -> TimingMethod {
    this.current_timing_method()
}

/// Sets the current Timing Method to the Timing Method provided.
#[no_mangle]
pub extern "C" fn Timer_set_current_timing_method(this: &mut Timer, method: TimingMethod) {
    this.set_current_timing_method(method);
}

/// Returns the current comparison that is being compared against. This may
/// be a custom comparison or one of the Comparison Generators.
#[no_mangle]
pub extern "C" fn Timer_current_comparison(this: &Timer) -> *const c_char {
    output_str(this.current_comparison())
}

/// Switches the current comparison to the next comparison in the list.
#[no_mangle]
pub extern "C" fn Timer_switch_to_next_comparison(this: &mut Timer) {
    this.switch_to_next_comparison();
}

/// Switches the current comparison to the previous comparison in the list.
#[no_mangle]
pub extern "C" fn Timer_switch_to_previous_comparison(this: &mut Timer) {
    this.switch_to_previous_comparison();
}

/// Returns whether Game Time is currently initialized. Game Time
/// automatically gets uninitialized for each new attempt.
#[no_mangle]
pub extern "C" fn Timer_is_game_time_initialized(this: &Timer) -> bool {
    this.is_game_time_initialized()
}

/// Initializes Game Time for the current attempt. Game Time automatically
/// gets uninitialized for each new attempt.
#[no_mangle]
pub extern "C" fn Timer_initialize_game_time(this: &mut Timer) {
    this.initialize_game_time();
}

/// Deinitializes Game Time for the current attempt.
#[no_mangle]
pub extern "C" fn Timer_deinitialize_game_time(this: &mut Timer) {
    this.deinitialize_game_time();
}

/// Returns whether the Game Timer is currently paused. If the Game Timer is
/// not paused, it automatically increments similar to Real Time.
#[no_mangle]
pub extern "C" fn Timer_is_game_time_paused(this: &Timer) -> bool {
    this.is_game_time_paused()
}

/// Pauses the Game Timer such that it doesn't automatically increment
/// similar to Real Time.
#[no_mangle]
pub extern "C" fn Timer_pause_game_time(this: &mut Timer) {
    this.pause_game_time();
}

/// Resumes the Game Timer such that it automatically increments similar to
/// Real Time, starting from the Game Time it was paused at.
#[no_mangle]
pub extern "C" fn Timer_resume_game_time(this: &mut Timer) {
    this.resume_game_time();
}

/// Sets the Game Time to the time specified. This also works if the Game
/// Time is paused, which can be used as a way of updating the Game Timer
/// periodically without it automatically moving forward. This ensures that
/// the Game Timer never shows any time that is not coming from the game.
#[no_mangle]
pub extern "C" fn Timer_set_game_time(this: &mut Timer, time: &TimeSpan) {
    this.set_game_time(*time);
}

/// Accesses the loading times. Loading times are defined as Game Time - Real Time.
#[no_mangle]
pub extern "C" fn Timer_loading_times(this: &Timer) -> *const TimeSpan {
    output_time_span(this.loading_times())
}

/// Instead of setting the Game Time directly, this method can be used to
/// just specify the amount of time the game has been loading. The Game Time
/// is then automatically determined by Real Time - Loading Times.
#[no_mangle]
pub extern "C" fn Timer_set_loading_times(this: &mut Timer, time: &TimeSpan) {
    this.set_loading_times(*time);
}

/// Returns the current Timer Phase.
#[no_mangle]
pub extern "C" fn Timer_current_phase(this: &Timer) -> TimerPhase {
    this.current_phase()
}

/// Accesses the Run in use by the Timer.
#[no_mangle]
pub extern "C" fn Timer_get_run(this: &Timer) -> &Run {
    this.run()
}

/// Saves the Run in use by the Timer as a LiveSplit splits file (*.lss).
#[no_mangle]
pub extern "C" fn Timer_save_as_lss(this: &Timer) -> *const c_char {
    output_vec(|o| {
        saver::livesplit::save_timer(this, o).unwrap();
    })
}

/// Marks the Run as unmodified, so that it is known that all the changes
/// have been saved.
#[no_mangle]
pub extern "C" fn Timer_mark_as_unmodified(this: &mut Timer) {
    this.mark_as_unmodified();
}

/// Prints out debug information representing the whole state of the Timer. This
/// is being written to stdout.
#[no_mangle]
pub extern "C" fn Timer_print_debug(this: &Timer) {
    println!("{:#?}", this);
}

/// Returns the current time of the Timer. The Game Time is <NULL> if the Game
/// Time has not been initialized.
#[no_mangle]
pub extern "C" fn Timer_current_time(this: &Timer) -> *const Time {
    output_time(this.snapshot().current_time())
}
<commit_msg>Add Current Split Index to the C API<commit_after>//! A Timer provides all the capabilities necessary for doing speedrun attempts.

use super::{output_str, output_time, output_time_span, output_vec};
use crate::{
    run::{NullableOwnedRun, OwnedRun},
    shared_timer::OwnedSharedTimer,
};
use livesplit_core::{run::saver, Run, Time, TimeSpan, Timer, TimerPhase, TimingMethod};
use std::{os::raw::c_char, ptr};

/// type
pub type OwnedTimer = Box<Timer>;
/// type
pub type NullableOwnedTimer = Option<OwnedTimer>;

/// Creates a new Timer based on a Run object storing all the information
/// about the splits. The Run object needs to have at least one segment, so
/// that the Timer can store the final time. If a Run object with no
/// segments is provided, the Timer creation fails and <NULL> is returned.
#[no_mangle]
pub extern "C" fn Timer_new(run: OwnedRun) -> NullableOwnedTimer {
    Timer::new(*run).ok().map(Box::new)
}

/// Consumes the Timer and creates a Shared Timer that can be shared across
/// multiple threads with multiple owners.
#[no_mangle]
pub extern "C" fn Timer_into_shared(this: OwnedTimer) -> OwnedSharedTimer {
    Box::new((*this).into_shared())
}

/// Takes out the Run from the Timer and resets the current attempt if there
/// is one in progress. If the splits are to be updated, all the information
/// of the current attempt is stored in the Run's history. Otherwise the
/// current attempt's information is discarded.
#[no_mangle]
pub extern "C" fn Timer_into_run(this: OwnedTimer, update_splits: bool) -> OwnedRun {
    Box::new((*this).into_run(update_splits))
}

/// drop
#[no_mangle]
pub extern "C" fn Timer_drop(this: OwnedTimer) {
    drop(this);
}

/// Replaces the Run object used by the Timer with the Run object provided. If
/// the Run provided contains no segments, it can't be used for timing and is
/// not being modified. Otherwise the Run that was in use by the Timer gets
/// stored in the Run object provided. Before the Run is returned, the current
/// attempt is reset and the splits are being updated depending on the
/// `update_splits` parameter. The return value indicates whether the Run got
/// replaced successfully.
#[no_mangle]
pub unsafe extern "C" fn Timer_replace_run(
    this: &mut Timer,
    run: &mut Run,
    update_splits: bool,
) -> bool {
    // This working correctly relies on panic = "abort",
    // as a panic would leave the run in an uninitialized state.
    let result = this.replace_run(ptr::read(run), update_splits);
    let was_successful = result.is_ok();
    let result = match result {
        Ok(r) | Err(r) => r,
    };
    ptr::write(run, result);
    was_successful
}

/// Sets the Run object used by the Timer with the Run object provided. If the
/// Run provided contains no segments, it can't be used for timing and gets
/// returned again. If the Run object can be set, the original Run object in use
/// by the Timer is disposed by this method and <NULL> is returned.
#[no_mangle]
pub extern "C" fn Timer_set_run(this: &mut Timer, run: OwnedRun) -> NullableOwnedRun {
    this.set_run(*run).err().map(Box::new)
}

/// Accesses the index of the split the attempt is currently on. If there's
/// no attempt in progress, `-1` is returned instead. This returns an
/// index that is equal to the amount of segments when the attempt is
/// finished, but has not been reset. So you need to be careful when using
/// this value for indexing.
#[no_mangle]
pub extern "C" fn Timer_current_split_index(this: &Timer) -> isize {
    this.current_split_index().map_or(-1, |i| i as isize)
}

/// Starts the Timer if there is no attempt in progress. If that's not the
/// case, nothing happens.
#[no_mangle]
pub extern "C" fn Timer_start(this: &mut Timer) {
    this.start();
}

/// If an attempt is in progress, stores the current time as the time of the
/// current split. The attempt ends if the last split time is stored.
#[no_mangle]
pub extern "C" fn Timer_split(this: &mut Timer) {
    this.split();
}

/// Starts a new attempt or stores the current time as the time of the
/// current split. The attempt ends if the last split time is stored.
#[no_mangle]
pub extern "C" fn Timer_split_or_start(this: &mut Timer) {
    this.split_or_start();
}

/// Skips the current split if an attempt is in progress and the
/// current split is not the last split.
#[no_mangle]
pub extern "C" fn Timer_skip_split(this: &mut Timer) {
    this.skip_split();
}

/// Removes the split time from the last split if an attempt is in progress
/// and there is a previous split. The Timer Phase also switches to
/// `Running` if it previously was `Ended`.
#[no_mangle]
pub extern "C" fn Timer_undo_split(this: &mut Timer) {
    this.undo_split();
}

/// Resets the current attempt if there is one in progress. If the splits
/// are to be updated, all the information of the current attempt is stored
/// in the Run's history. Otherwise the current attempt's information is
/// discarded.
#[no_mangle]
pub extern "C" fn Timer_reset(this: &mut Timer, update_splits: bool) {
    this.reset(update_splits);
}

/// Resets the current attempt if there is one in progress. The splits are
/// updated such that the current attempt's split times are being stored as
/// the new Personal Best.
#[no_mangle]
pub extern "C" fn Timer_reset_and_set_attempt_as_pb(this: &mut Timer) {
    this.reset_and_set_attempt_as_pb();
}

/// Pauses an active attempt that is not paused.
#[no_mangle]
pub extern "C" fn Timer_pause(this: &mut Timer) {
    this.pause();
}

/// Resumes an attempt that is paused.
#[no_mangle]
pub extern "C" fn Timer_resume(this: &mut Timer) {
    this.resume();
}

/// Toggles an active attempt between `Paused` and `Running`.
#[no_mangle]
pub extern "C" fn Timer_toggle_pause(this: &mut Timer) {
    this.toggle_pause();
}

/// Toggles an active attempt between `Paused` and `Running` or starts an
/// attempt if there's none in progress.
#[no_mangle]
pub extern "C" fn Timer_toggle_pause_or_start(this: &mut Timer) {
    this.toggle_pause_or_start();
}

/// Removes all the pause times from the current time. If the current
/// attempt is paused, it also resumes that attempt. Additionally, if the
/// attempt is finished, the final split time is adjusted to not include the
/// pause times as well.
///
/// # Warning
///
/// This behavior is not entirely optimal, as generally only the final split
/// time is modified, while all other split times are left unmodified, which
/// may not be what actually happened during the run.
#[no_mangle]
pub extern "C" fn Timer_undo_all_pauses(this: &mut Timer) {
    this.undo_all_pauses();
}

/// Returns the currently selected Timing Method.
#[no_mangle]
pub extern "C" fn Timer_current_timing_method(this: &Timer) -> TimingMethod {
    this.current_timing_method()
}

/// Sets the current Timing Method to the Timing Method provided.
#[no_mangle]
pub extern "C" fn Timer_set_current_timing_method(this: &mut Timer, method: TimingMethod) {
    this.set_current_timing_method(method);
}

/// Returns the current comparison that is being compared against. This may
/// be a custom comparison or one of the Comparison Generators.
#[no_mangle]
pub extern "C" fn Timer_current_comparison(this: &Timer) -> *const c_char {
    output_str(this.current_comparison())
}

/// Switches the current comparison to the next comparison in the list.
#[no_mangle]
pub extern "C" fn Timer_switch_to_next_comparison(this: &mut Timer) {
    this.switch_to_next_comparison();
}

/// Switches the current comparison to the previous comparison in the list.
#[no_mangle]
pub extern "C" fn Timer_switch_to_previous_comparison(this: &mut Timer) {
    this.switch_to_previous_comparison();
}

/// Returns whether Game Time is currently initialized. Game Time
/// automatically gets uninitialized for each new attempt.
#[no_mangle]
pub extern "C" fn Timer_is_game_time_initialized(this: &Timer) -> bool {
    this.is_game_time_initialized()
}

/// Initializes Game Time for the current attempt. Game Time automatically
/// gets uninitialized for each new attempt.
#[no_mangle]
pub extern "C" fn Timer_initialize_game_time(this: &mut Timer) {
    this.initialize_game_time();
}

/// Deinitializes Game Time for the current attempt.
#[no_mangle]
pub extern "C" fn Timer_deinitialize_game_time(this: &mut Timer) {
    this.deinitialize_game_time();
}

/// Returns whether the Game Timer is currently paused. If the Game Timer is
/// not paused, it automatically increments similar to Real Time.
#[no_mangle]
pub extern "C" fn Timer_is_game_time_paused(this: &Timer) -> bool {
    this.is_game_time_paused()
}

/// Pauses the Game Timer such that it doesn't automatically increment
/// similar to Real Time.
#[no_mangle]
pub extern "C" fn Timer_pause_game_time(this: &mut Timer) {
    this.pause_game_time();
}

/// Resumes the Game Timer such that it automatically increments similar to
/// Real Time, starting from the Game Time it was paused at.
#[no_mangle]
pub extern "C" fn Timer_resume_game_time(this: &mut Timer) {
    this.resume_game_time();
}

/// Sets the Game Time to the time specified. This also works if the Game
/// Time is paused, which can be used as a way of updating the Game Timer
/// periodically without it automatically moving forward. This ensures that
/// the Game Timer never shows any time that is not coming from the game.
#[no_mangle]
pub extern "C" fn Timer_set_game_time(this: &mut Timer, time: &TimeSpan) {
    this.set_game_time(*time);
}

/// Accesses the loading times. Loading times are defined as Game Time - Real Time.
#[no_mangle]
pub extern "C" fn Timer_loading_times(this: &Timer) -> *const TimeSpan {
    output_time_span(this.loading_times())
}

/// Instead of setting the Game Time directly, this method can be used to
/// just specify the amount of time the game has been loading. The Game Time
/// is then automatically determined by Real Time - Loading Times.
#[no_mangle]
pub extern "C" fn Timer_set_loading_times(this: &mut Timer, time: &TimeSpan) {
    this.set_loading_times(*time);
}

/// Returns the current Timer Phase.
#[no_mangle]
pub extern "C" fn Timer_current_phase(this: &Timer) -> TimerPhase {
    this.current_phase()
}

/// Accesses the Run in use by the Timer.
#[no_mangle]
pub extern "C" fn Timer_get_run(this: &Timer) -> &Run {
    this.run()
}

/// Saves the Run in use by the Timer as a LiveSplit splits file (*.lss).
#[no_mangle]
pub extern "C" fn Timer_save_as_lss(this: &Timer) -> *const c_char {
    output_vec(|o| {
        saver::livesplit::save_timer(this, o).unwrap();
    })
}

/// Marks the Run as unmodified, so that it is known that all the changes
/// have been saved.
#[no_mangle]
pub extern "C" fn Timer_mark_as_unmodified(this: &mut Timer) {
    this.mark_as_unmodified();
}

/// Prints out debug information representing the whole state of the Timer. This
/// is being written to stdout.
#[no_mangle]
pub extern "C" fn Timer_print_debug(this: &Timer) {
    println!("{:#?}", this);
}

/// Returns the current time of the Timer. The Game Time is <NULL> if the Game
/// Time has not been initialized.
#[no_mangle]
pub extern "C" fn Timer_current_time(this: &Timer) -> *const Time {
    output_time(this.snapshot().current_time())
}
<|endoftext|>