use gateway::Shard;
use internal::prelude::*;
use parking_lot::Mutex;
use std::{
collections::{HashMap, VecDeque},
sync::{
mpsc::{
Receiver,
RecvTimeoutError,
Sender},
Arc
},
thread,
time::{Duration, Instant}
};
use super::super::super::EventHandler;
use super::{
ShardId,
ShardManagerMessage,
ShardQueuerMessage,
ShardRunner,
ShardRunnerInfo,
ShardRunnerOptions,
};
use threadpool::ThreadPool;
use typemap::ShareMap;
use ::gateway::ConnectionStage;
#[cfg(feature = "voice")]
use client::bridge::voice::ClientVoiceManager;
#[cfg(feature = "framework")]
use framework::Framework;
const WAIT_BETWEEN_BOOTS_IN_SECONDS: u64 = 5;
pub struct ShardQueuer<H: EventHandler + Send + Sync + 'static> {
pub data: Arc<Mutex<ShareMap>>,
pub event_handler: Arc<H>,
#[cfg(feature = "framework")]
pub framework: Arc<Mutex<Option<Box<Framework + Send>>>>,
pub last_start: Option<Instant>,
pub manager_tx: Sender<ShardManagerMessage>,
pub queue: VecDeque<(u64, u64)>,
pub runners: Arc<Mutex<HashMap<ShardId, ShardRunnerInfo>>>,
pub rx: Receiver<ShardQueuerMessage>,
pub threadpool: ThreadPool,
pub token: Arc<Mutex<String>>,
#[cfg(feature = "voice")]
pub voice_manager: Arc<Mutex<ClientVoiceManager>>,
pub ws_url: Arc<Mutex<String>>,
}
impl<H: EventHandler + Send + Sync + 'static> ShardQueuer<H> {
pub fn run(&mut self) {
let wait_duration = Duration::from_secs(WAIT_BETWEEN_BOOTS_IN_SECONDS);
loop {
match self.rx.recv_timeout(wait_duration) {
Ok(ShardQueuerMessage::Shutdown) => break,
Ok(ShardQueuerMessage::Start(id, total)) => {
self.checked_start(id.0, total.0);
},
Err(RecvTimeoutError::Disconnected) => {
break;
},
Err(RecvTimeoutError::Timeout) => {
if let Some((id, total)) = self.queue.pop_front() {
self.checked_start(id, total);
}
}
}
}
}
fn check_last_start(&mut self) {
let instant = match self.last_start {
Some(instant) => instant,
None => return,
};
let duration = Duration::from_secs(WAIT_BETWEEN_BOOTS_IN_SECONDS);
let elapsed = instant.elapsed();
if elapsed >= duration {
return;
}
let to_sleep = duration - elapsed;
thread::sleep(to_sleep);
}
fn checked_start(&mut self, id: u64, total: u64) {
self.check_last_start();
if let Err(why) = self.start(id, total) {
warn!("Err starting shard {}: {:?}", id, why);
info!("Re-queueing start of shard {}", id);
self.queue.push_back((id, total));
}
self.last_start = Some(Instant::now());
}
fn start(&mut self, shard_id: u64, shard_total: u64) -> Result<()> {
let shard_info = [shard_id, shard_total];
let shard = Shard::new(
Arc::clone(&self.ws_url),
Arc::clone(&self.token),
shard_info,
)?;
let mut runner = ShardRunner::new(ShardRunnerOptions {
data: Arc::clone(&self.data),
event_handler: Arc::clone(&self.event_handler),
#[cfg(feature = "framework")]
framework: Arc::clone(&self.framework),
manager_tx: self.manager_tx.clone(),
threadpool: self.threadpool.clone(),
#[cfg(feature = "voice")]
voice_manager: Arc::clone(&self.voice_manager),
shard,
});
let runner_info = ShardRunnerInfo {
latency: None,
runner_tx: runner.runner_tx(),
stage: ConnectionStage::Disconnected,
};
thread::spawn(move || {
let _ = runner.run();
});
self.runners.lock().insert(ShardId(shard_id), runner_info);
Ok(())
}
}