diff --git a/Cargo.toml b/Cargo.toml index 5b40400..787a4e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ keywords = [ "virtual", "udp", "connection", "message", "queue"] license="MIT/Apache-2.0" [dependencies] -clock_ticks = "0.1.0" rand = "0.3.14" clippy = { version = "*", optional = true } diff --git a/src/server.rs b/src/server.rs index 5949eee..e9c1023 100644 --- a/src/server.rs +++ b/src/server.rs @@ -104,7 +104,7 @@ impl Server { while !self.closed { let tick_start = tick::start(); - let tick_delay = 1000000000 / self.config.send_rate; + let tick_delay = 1000_000_000 / self.config.send_rate; // Receive all incoming UDP packets to our local address let mut bytes_received = 0; @@ -150,7 +150,7 @@ impl Server { // Then feed the packet into the connection object for // parsing connection.receive_packet( - packet, tick_delay / 1000000, self, handler + packet, tick_delay / 1000_000, self, handler ); } diff --git a/src/shared/binary_rate_limiter.rs b/src/shared/binary_rate_limiter.rs index 0613e2b..b133729 100644 --- a/src/shared/binary_rate_limiter.rs +++ b/src/shared/binary_rate_limiter.rs @@ -5,9 +5,9 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate clock_ticks; use std::cmp; +use std::time::{Duration, Instant}; use super::super::{Config, RateLimiter}; /// Minimum time before switching back into good mode in milliseconds. @@ -32,9 +32,9 @@ pub struct BinaryRateLimiter { max_tick: u32, mode: Mode, rtt_threshold: u32, - last_bad_time: u32, - last_good_time: u32, - good_time_duration: u32, + last_bad_time: Instant, + last_good_time: Instant, + good_time_duration: Duration, delay_until_good_mode: u32 } @@ -44,6 +44,7 @@ impl BinaryRateLimiter { pub fn new(config: &Config) -> Box { let rate = config.send_rate as f32; + let now = Instant::now(); Box::new(BinaryRateLimiter { tick: 0, @@ -51,9 +52,9 @@ impl BinaryRateLimiter { max_tick: (rate / (33.0 / (100.0 / rate))) as u32, mode: Mode::Good, rtt_threshold: 250, - last_bad_time: 0, - last_good_time: precise_time_ms(), - good_time_duration: 0, + last_bad_time: now, + last_good_time: now, + good_time_duration: Duration::new(0, 0), delay_until_good_mode: MIN_GOOD_MODE_TIME_DELAY }) @@ -68,14 +69,15 @@ impl RateLimiter for BinaryRateLimiter { // Check current network conditions let conditions = if rtt <= self.rtt_threshold { // Keep track of the time we are in good mode - self.good_time_duration += precise_time_ms() - self.last_good_time; - self.last_good_time = precise_time_ms(); + let now = Instant::now(); + self.good_time_duration += now - self.last_good_time; + self.last_good_time = now; Mode::Good } else { // Remember the last time we were in bad mode - self.last_bad_time = precise_time_ms(); - self.good_time_duration = 0; + self.last_bad_time = Instant::now(); + self.good_time_duration = Duration::new(0, 0); Mode::Bad }; @@ -91,7 +93,7 @@ impl RateLimiter for BinaryRateLimiter { // To avoid rapid toggling between good and bad mode, if we // drop from good mode to bad in under 10 seconds - if time_since(self.last_bad_time) < 10000 { + if self.last_bad_time.elapsed() < Duration::from_millis(10000) { // We double the amount of time before bad mode goes // back to good. @@ -113,8 +115,8 @@ impl RateLimiter for BinaryRateLimiter { // periods of bad behavior, for each 10 seconds the // connection is in good mode, we halve the time before bad // mode goes back to good. - if self.good_time_duration >= 10000 { - self.good_time_duration -= 10000; + if self.good_time_duration >= Duration::from_millis(10000) { + self.good_time_duration -= Duration::from_millis(10000); // We also clamp this at a minimum self.delay_until_good_mode = cmp::max( @@ -132,7 +134,7 @@ impl RateLimiter for BinaryRateLimiter { // If you are in bad mode, and conditions have been good for a // specific length of time return to good mode - if time_since(self.last_bad_time) > self.delay_until_good_mode { + if self.last_bad_time.elapsed() > Duration::from_millis(self.delay_until_good_mode as u64) { self.mode = Mode::Good; } @@ -159,21 +161,14 @@ impl RateLimiter for BinaryRateLimiter { } fn reset(&mut self) { + let now = Instant::now(); self.tick = 0; self.mode = Mode::Good; - self.last_bad_time = 0; - self.last_good_time = precise_time_ms(); - self.good_time_duration = 0; + self.last_bad_time = now; + self.last_good_time = now; + self.good_time_duration = Duration::new(0, 0); self.delay_until_good_mode = MIN_GOOD_MODE_TIME_DELAY; } } -fn precise_time_ms() -> u32 { - (clock_ticks::precise_time_ns() / 1000000) as u32 -} - -fn time_since(t: u32) -> u32 { - precise_time_ms() - t -} - diff --git a/src/shared/connection.rs b/src/shared/connection.rs index 2c94071..8b89047 100644 --- a/src/shared/connection.rs +++ b/src/shared/connection.rs @@ -6,12 +6,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. extern crate rand; -extern crate clock_ticks; use std::cmp; use std::net::SocketAddr; use std::collections::HashMap; use std::collections::VecDeque; +use std::time::{Duration, Instant}; use super::message_queue::{MessageQueue, MessageIterator}; use super::super::traits::socket::Socket; use super::super::{Config, MessageKind, Handler, RateLimiter}; @@ -43,7 +43,7 @@ enum PacketState { #[derive(Debug)] struct SentPacketAck { seq: u32, - time: u32, + time: Instant, state: PacketState, packet: Option> } @@ -124,7 +124,7 @@ pub struct Connection { smoothed_rtt: f32, /// Last time a packet was received - last_receive_time: u32, + last_receive_time: Instant, /// Queue of recently received packets used for ack bitfield construction recv_ack_queue: VecDeque, @@ -187,7 +187,7 @@ impl Connection { local_seq_number: 0, remote_seq_number: 0, smoothed_rtt: 0.0, - last_receive_time: precise_time_ms(), + last_receive_time: Instant::now(), recv_ack_queue: VecDeque::new(), sent_ack_queue: Vec::new(), sent_packets: 0, @@ -333,7 +333,7 @@ impl Connection { } // Update time used for disconnect detection - self.last_receive_time = precise_time_ms(); + self.last_receive_time = Instant::now(); // Read remote sequence number self.remote_seq_number = packet[8] as u32; @@ -353,19 +353,24 @@ impl Connection { if let Some(lost_packet) = { let ack = self.sent_ack_queue.get_mut(i).unwrap(); + let last_receive_since_ack = if self.last_receive_time > ack.time { + dur_as_ms(self.last_receive_time - ack.time) + } else { + 0 + }; // Calculate the roundtrip time from acknowledged packets if seq_was_acked(ack.seq, ack_seq_number, bitfield) { self.acked_packets = self.acked_packets.wrapping_add(1); self.smoothed_rtt = moving_average( self.smoothed_rtt, - (cmp::max(self.last_receive_time - ack.time, tick_delay) - tick_delay) as f32 + (cmp::max(last_receive_since_ack, tick_delay) - tick_delay) as f32 ); ack.state = PacketState::Acked; None // Extract data from lost packets - } else if self.last_receive_time - ack.time + } else if last_receive_since_ack > self.config.packet_drop_threshold { self.lost_packets = self.lost_packets.wrapping_add(1); @@ -546,7 +551,7 @@ impl Connection { if self.send_ack_required(self.local_seq_number) { self.sent_ack_queue.push(SentPacketAck { seq: self.local_seq_number, - time: precise_time_ms(), + time: Instant::now(), state: PacketState::Unknown, packet: Some(packet) }); @@ -576,7 +581,7 @@ impl Connection { self.local_seq_number = 0; self.remote_seq_number = 0; self.smoothed_rtt = 0.0; - self.last_receive_time = precise_time_ms(); + self.last_receive_time = Instant::now(); self.recv_ack_queue.clear(); self.sent_ack_queue.clear(); self.sent_packets = 0; @@ -653,7 +658,7 @@ impl Connection { ) -> bool { // Calculate time since last received packet - let inactive_time = precise_time_ms() - self.last_receive_time; + let inactive_time = dur_as_ms(self.last_receive_time.elapsed()); match self.state { @@ -743,7 +748,7 @@ fn seq_was_acked(seq: u32, ack: u32, bitfield: u32) -> bool { } } -fn precise_time_ms() -> u32 { - (clock_ticks::precise_time_ns() / 1000000) as u32 +fn dur_as_ms(dur: Duration) -> u32 { + (dur.as_secs() as u32 * 1000) + (dur.subsec_nanos() / 1000_000) } diff --git a/src/tests/mock.rs b/src/tests/mock.rs index c2e347d..46bdb1a 100644 --- a/src/tests/mock.rs +++ b/src/tests/mock.rs @@ -5,13 +5,12 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate clock_ticks; use std::cmp; use std::net; use std::thread; use std::io::Error; -use std::time::Duration; +use std::time::{Duration, Instant}; use std::net::ToSocketAddrs; use std::collections::HashMap; @@ -270,7 +269,7 @@ pub struct MockTickRecorder { load_ticks: u32, tick_delay: u32, tick_count: u32, - last_tick_time: u32, + last_tick_time: Instant, expected_time: u32, last_sleep_duration: u32, accumulated: i32 @@ -284,7 +283,7 @@ impl MockTickRecorder { load_ticks: load_ticks, tick_delay: 1000 / send_rate, tick_count: 0, - last_tick_time: 0, + last_tick_time: Instant::now(), expected_time: expected_time, last_sleep_duration: 0, accumulated: 0 @@ -292,17 +291,19 @@ impl MockTickRecorder { } fn init(&mut self) { - self.last_tick_time = precise_time_ms(); + self.last_tick_time = Instant::now(); } fn tick(&mut self) -> bool { if self.tick_count > 1 { - let delay = (precise_time_ms() - self.last_tick_time) as i32 - (self.last_sleep_duration as i32 - self.tick_delay as i32 * 2); + let elapsed = self.last_tick_time.elapsed(); + let delay = elapsed.as_secs() as i32 * 1000 + elapsed.subsec_nanos() as i32 / 1000_000 - + (self.last_sleep_duration as i32 - self.tick_delay as i32 * 2); self.accumulated += delay; } - self.last_tick_time = precise_time_ms(); + self.last_tick_time = Instant::now(); self.tick_count += 1; if self.tick_count == self.max_ticks + 2 { @@ -315,9 +316,10 @@ impl MockTickRecorder { // Fake load by waiting sleeping twice the normal tick delay } else if self.tick_count > 1 && self.tick_count <= self.load_ticks + 1 { - let before = precise_time_ms(); + let before = Instant::now(); thread::sleep(Duration::from_millis((self.tick_delay * 2) as u64)); - self.last_sleep_duration = precise_time_ms() - before; + let elapsed = before.elapsed(); + self.last_sleep_duration = elapsed.as_secs() as u32 * 1000 + elapsed.subsec_nanos() / 1000_000; false } else { @@ -566,10 +568,6 @@ fn to_socket_addr(address: T) -> net::SocketAddr { address.to_socket_addrs().unwrap().nth(0).unwrap() } -fn precise_time_ms() -> u32 { - (clock_ticks::precise_time_ns() / 1000000) as u32 -} - pub fn create_connection(config: Option) -> (Connection, MockOwner, MockOwnerHandler) { let config = config.unwrap_or_else(||Config::default()); let local_address: net::SocketAddr = "127.0.0.1:1234".parse().unwrap(); diff --git a/src/tick.rs b/src/tick.rs index 8431027..9a98ae0 100644 --- a/src/tick.rs +++ b/src/tick.rs @@ -5,27 +5,27 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate clock_ticks; - use std::cmp; use std::thread; -use std::time::Duration; +use std::time::{Duration, Instant}; use super::Config; -pub fn start() -> u64 { - clock_ticks::precise_time_ns() +pub fn start() -> Instant { + Instant::now() } pub fn end( tick_delay: u32, - tick_start: u64, + tick_start: Instant, overflow: &mut u32, config: &Config ) { // Actual time taken by the tick - let time_taken = (clock_ticks::precise_time_ns() - tick_start) as u32; + let elapsed = tick_start.elapsed(); + assert!(elapsed.as_secs() == 0, "tick exceeded 1 second"); + let time_taken = elapsed.subsec_nanos(); // Required delay reduction to keep tick rate let mut reduction = cmp::min(time_taken, tick_delay);