veloren/network/src/api.rs

746 lines
29 KiB
Rust
Raw Normal View History

use crate::{
message::{self, InCommingMessage, MessageBuffer, OutGoingMessage},
scheduler::Scheduler,
types::{Mid, Pid, Prio, Promises, Requestor::User, Sid},
};
use async_std::{io, sync::RwLock, task};
use futures::{
channel::{mpsc, oneshot},
sink::SinkExt,
stream::StreamExt,
};
use prometheus::Registry;
use serde::{de::DeserializeOwned, Serialize};
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use tracing::*;
use tracing_futures::Instrument;
use uvth::ThreadPool;
2020-05-10 02:07:46 +00:00
/// Represents a Tcp or Udp or Mpsc address
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Address {
Tcp(std::net::SocketAddr),
Udp(std::net::SocketAddr),
Mpsc(u64),
}
2020-05-10 02:07:46 +00:00
/// `Participants` are generated by the [`Network`] and represent a connection
/// to a remote Participant. Look at the [`connect`] and [`connected`] method of
/// [`Networks`] on how to generate `Participants`
///
/// [`Networks`]: crate::api::Network
/// [`connect`]: Network::connect
/// [`connected`]: Network::connected
pub struct Participant {
local_pid: Pid,
remote_pid: Pid,
stream_open_sender: RwLock<mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender<Stream>)>>,
stream_opened_receiver: RwLock<mpsc::UnboundedReceiver<Stream>>,
closed: AtomicBool,
disconnect_sender: Option<mpsc::UnboundedSender<Pid>>,
}
2020-05-10 02:07:46 +00:00
/// `Streams` represents a channel to send `n` messages with a certain priority
/// and [`Promises`]. messages need always to be send between 2 `Streams`.
///
/// `Streams` are generated by the [`Participant`].
/// Look at the [`open`] and [`opened`] method of [`Participant`] on how to
/// generate `Streams`
///
/// Unlike [`Network`] and [`Participant`], `Streams` don't implement interior
/// mutability, as multiple threads don't need access to the same `Stream`.
/// [`Sync`] is not supported! In that case multiple `Streams` should be used
/// instead. However it's still possible to [`Send`] `Streams`.
///
/// [`Networks`]: crate::api::Network
/// [`open`]: Participant::open
/// [`opened`]: Participant::opened
/// [`Send`]: std::marker::Send
/// [`Sync`]: std::marker::Sync
#[derive(Debug)]
pub struct Stream {
pid: Pid,
sid: Sid,
mid: Mid,
prio: Prio,
promises: Promises,
msg_send_sender: std::sync::mpsc::Sender<(Prio, Pid, Sid, OutGoingMessage)>,
msg_recv_receiver: mpsc::UnboundedReceiver<InCommingMessage>,
closed: Arc<AtomicBool>,
shutdown_sender: Option<mpsc::UnboundedSender<Sid>>,
}
2020-05-10 02:07:46 +00:00
/// Error type thrown by [`Networks`](Network) methods
#[derive(Debug)]
pub enum NetworkError {
NetworkClosed,
ListenFailed(std::io::Error),
}
2020-05-10 02:07:46 +00:00
/// Error type thrown by [`Participants`](Participant) methods
#[derive(Debug, PartialEq)]
pub enum ParticipantError {
ParticipantClosed,
}
2020-05-10 02:07:46 +00:00
/// Error type thrown by [`Streams`](Stream) methods
#[derive(Debug, PartialEq)]
pub enum StreamError {
StreamClosed,
}
2020-05-10 02:07:46 +00:00
/// Use the `Network` to create connections to other [`Participants`]
///
/// The `Network` is the single source that handles all connections in your
/// Application. You can pass it around multiple threads in an
/// [`Arc`](std::sync::Arc) as all commands have internal mutability.
///
/// The `Network` has methods to [`connect`] and [`disconnect`] to other
/// [`Participants`] via their [`Address`]. All [`Participants`] will be stored
/// in the Network until explicitly disconnected, which is the only way to close
/// the sockets.
///
/// # Examples
/// ```rust
/// use veloren_network::{Network, Pid};
/// use uvth::ThreadPoolBuilder;
///
/// // Create a Network, listen on port `12345` to accept connections and connect to port `80` to connect to a (pseudo) database Application
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// network.listen(Address::Tcp("127.0.0.1:12345".parse().unwrap())).await?;
/// let database = network.connect(Address::Tcp("127.0.0.1:80".parse().unwrap())).await?;
/// });
/// ```
///
/// [`Participants`]: crate::api::Participant
/// [`connect`]: Network::connect
/// [`disconnect`]: Network::disconnect
pub struct Network {
local_pid: Pid,
participants: RwLock<HashMap<Pid, Arc<Participant>>>,
listen_sender:
RwLock<mpsc::UnboundedSender<(Address, oneshot::Sender<async_std::io::Result<()>>)>>,
connect_sender:
RwLock<mpsc::UnboundedSender<(Address, oneshot::Sender<io::Result<Participant>>)>>,
connected_receiver: RwLock<mpsc::UnboundedReceiver<Participant>>,
shutdown_sender: Option<oneshot::Sender<()>>,
}
impl Network {
2020-05-10 02:07:46 +00:00
/// Generates a new `Network` to handle all connections in an Application
///
/// # Arguments
/// * `participant_id` - provide it by calling [`Pid::new()`], usually you
/// don't want to reuse a Pid for 2 `Networks`
/// * `thread_pool` - you need to provide a [`ThreadPool`] where exactly 1
/// thread will be created to handle all `Network` internals. Additional
/// threads will be allocated on an internal async-aware threadpool
/// * `registry` - Provide a Registy in order to collect Prometheus metrics
/// by this `Network`, `None` will deactivate Tracing. Tracing is done via
/// [`prometheus`]
///
/// # Examples
/// ```rust
/// use uvth::ThreadPoolBuilder;
/// use veloren_network::{Network, Pid};
///
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// ```
///
/// Usually you only create a single `Network` for an application, except
/// when client and server are in the same application, then you will want
/// 2. However there are no technical limitations from creating more.
///
/// [`Pid::new()`]: crate::types::Pid::new
/// [`ThreadPool`]: uvth::ThreadPool
pub fn new(participant_id: Pid, thread_pool: &ThreadPool, registry: Option<&Registry>) -> Self {
let p = participant_id;
debug!(?p, ?User, "starting Network");
let (scheduler, listen_sender, connect_sender, connected_receiver, shutdown_sender) =
Scheduler::new(participant_id, registry);
thread_pool.execute(move || {
trace!(?p, ?User, "starting sheduler in own thread");
let _handle = task::block_on(
scheduler
.run()
.instrument(tracing::info_span!("scheduler", ?p)),
);
trace!(?p, ?User, "stopping sheduler and his own thread");
});
Self {
local_pid: participant_id,
participants: RwLock::new(HashMap::new()),
listen_sender: RwLock::new(listen_sender),
connect_sender: RwLock::new(connect_sender),
connected_receiver: RwLock::new(connected_receiver),
shutdown_sender: Some(shutdown_sender),
}
}
2020-05-10 02:07:46 +00:00
/// starts listening on an [`Address`].
/// When the method returns the `Network` is ready to listen for incoming
/// connections OR has returned a [`NetworkError`] (e.g. port already used).
/// You can call [`connected`] to asynchrony wait for a [`Participant`] to
/// connect. You can call `listen` on multiple addresses, e.g. to
/// support multiple Protocols or NICs.
///
/// # Examples
/// ```rust
/// use uvth::ThreadPoolBuilder;
/// use veloren_network::{Network, Pid};
///
/// // Create a Network, listen on port `2000` TCP on all NICs and `2001` UDP locally
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// network
/// .listen(Address::Tcp("0.0.0.0:2000".parse().unwrap()))
/// .await?;
/// network
/// .listen(Address::Udp("127.0.0.1:2001".parse().unwrap()))
/// .await?;
/// });
/// ```
///
/// [`connected`]: Network::connected
pub async fn listen(&self, address: Address) -> Result<(), NetworkError> {
let (result_sender, result_receiver) = oneshot::channel::<async_std::io::Result<()>>();
debug!(?address, ?User, "listening on address");
self.listen_sender
.write()
.await
.send((address, result_sender))
.await?;
match result_receiver.await? {
//waiting guarantees that we either listened sucessfully or get an error like port in
// use
Ok(()) => Ok(()),
Err(e) => Err(NetworkError::ListenFailed(e)),
}
}
2020-05-10 02:07:46 +00:00
/// starts connectiong to an [`Address`].
/// When the method returns the Network either returns a [`Participant`]
/// ready to open [`Streams`] on OR has returned a [`NetworkError`] (e.g.
/// can't connect, or invalid Handshake) # Examples
/// ```rust
/// use uvth::ThreadPoolBuilder;
/// use veloren_network::{Network, Pid};
///
/// // Create a Network, connect on port `2000` TCP and `2001` UDP like listening above
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// let p1 = network
/// .connect(Address::Tcp("127.0.0.1:2000".parse().unwrap()))
/// .await?;
/// let p2 = network
/// .connect(Address::Udp("127.0.0.1:2001".parse().unwrap()))
/// .await?;
/// assert!(p1.ptr_eq(p2));
/// });
/// ```
/// Usually the `Network` guarantees that a operation on a [`Participant`]
/// succeeds, e.g. by automatic retrying unless it fails completely e.g. by
/// disconnecting from the remote. If 2 [`Addresses`] you `connect` to
/// belongs to the same [`Participant`], you get the same [`Participant`] as
/// a result. This is useful e.g. by connecting to the same
/// [`Participant`] via multiple Protocols.
///
/// [`Streams`]: crate::api::Stream
/// [`Addresses`]: crate::api::Address
pub async fn connect(&self, address: Address) -> Result<Arc<Participant>, NetworkError> {
let (pid_sender, pid_receiver) = oneshot::channel::<io::Result<Participant>>();
debug!(?address, ?User, "connect to address");
self.connect_sender
.write()
.await
.send((address, pid_sender))
.await?;
let participant = pid_receiver.await??;
let pid = participant.remote_pid;
debug!(
?pid,
?User,
"received Participant id from remote and return to user"
);
let participant = Arc::new(participant);
self.participants
.write()
.await
.insert(participant.remote_pid, participant.clone());
Ok(participant)
}
2020-05-10 02:07:46 +00:00
/// returns a [`Participant`] created from a [`Address`] you called
/// [`listen`] on before. This function will either return a working
/// [`Participant`] ready to open [`Streams`] on OR has returned a
/// [`NetworkError`] (e.g. Network got closed)
///
/// # Examples
/// ```rust
/// use uvth::ThreadPoolBuilder;
/// use veloren_network::{Network, Pid};
///
/// // Create a Network, listen on port `2000` TCP and opens returns their Pid
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// network
/// .listen(Address::Tcp("0.0.0.0:2000".parse().unwrap()))
/// .await?;
/// while let Some(participant) = network.connected().await? {
/// println!("Participant connected: {}", participant.remote_pid());
/// }
/// });
/// ```
///
/// [`Streams`]: crate::api::Stream
/// [`listen`]: crate::api::Network::listen
pub async fn connected(&self) -> Result<Arc<Participant>, NetworkError> {
let participant = self.connected_receiver.write().await.next().await?;
let participant = Arc::new(participant);
self.participants
.write()
.await
.insert(participant.remote_pid, participant.clone());
Ok(participant)
}
2020-05-10 02:07:46 +00:00
/// disconnecting a [`Participant`] where you move the last existing
/// [`Arc<Participant>`]. As the [`Network`] also holds [`Arc`] to the
/// [`Participant`], you need to provide the last [`Arc<Participant>`] and
/// are not allowed to keep others. If you do so the [`Participant`]
/// can't be disconnected properly. If you no longer have the respective
/// [`Participant`], try using the [`participants`] method to get it.
/// This function will wait for all [`Streams`] to properly close, including
/// all messages to be send before closing.
///
/// # Examples
/// ```rust
/// use uvth::ThreadPoolBuilder;
/// use veloren_network::{Network, Pid};
///
/// // Create a Network, listen on port `2000` TCP and opens returns their Pid and close connection.
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// network
/// .listen(Address::Tcp("0.0.0.0:2000".parse().unwrap()))
/// .await?;
/// while let Some(participant) = network.connected().await? {
/// println!("Participant connected: {}", participant.remote_pid());
/// network.disconnect(participant).await?;
/// }
/// });
/// ```
///
/// [`Arc<Participant>`]: crate::api::Participant
/// [`Streams`]: crate::api::Stream
/// [`participants`]: Network::participants
/// [`Arc`]: std::sync::Arc
pub async fn disconnect(&self, participant: Arc<Participant>) -> Result<(), NetworkError> {
// Remove, Close and try_unwrap error when unwrap fails!
let pid = participant.remote_pid;
debug!(?pid, "removing participant from network");
self.participants.write().await.remove(&pid)?;
participant.closed.store(true, Ordering::Relaxed);
if Arc::try_unwrap(participant).is_err() {
warn!(
"you are disconnecting and still keeping a reference to this participant, this is \
a bad idea. Participant will only be dropped when you drop your last reference"
);
};
Ok(())
}
2020-05-10 02:07:46 +00:00
/// returns a copy of all current connected [`Participants`]
///
/// [`Participants`]: crate::api::Participant
pub async fn participants(&self) -> HashMap<Pid, Arc<Participant>> {
self.participants.read().await.clone()
}
}
impl Participant {
pub(crate) fn new(
local_pid: Pid,
remote_pid: Pid,
stream_open_sender: mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender<Stream>)>,
stream_opened_receiver: mpsc::UnboundedReceiver<Stream>,
disconnect_sender: mpsc::UnboundedSender<Pid>,
) -> Self {
Self {
local_pid,
remote_pid,
stream_open_sender: RwLock::new(stream_open_sender),
stream_opened_receiver: RwLock::new(stream_opened_receiver),
closed: AtomicBool::new(false),
disconnect_sender: Some(disconnect_sender),
2020-03-04 15:52:30 +00:00
}
}
2020-05-10 02:07:46 +00:00
/// Opens a [`Stream`] on this `Participant` with a certain Priority and
/// [`Promises`]
///
/// # Arguments
/// * `prio` - valid between 0-63. The priority rates the throughput for
/// messages of the [`Stream`] e.g. prio 5 messages will get 1/2 the speed
/// prio0 messages have. Prio10 messages only 1/4 and Prio 15 only 1/8,
/// etc...
/// * `promises` - use a combination of you prefered [`Promises`], see the
/// link for further documentation. You can combine them, e.g.
/// `PROMISES_ORDERED | PROMISES_CONSISTENCY` The Stream will then
/// guarantee that those promisses are met.
///
/// A [`ParticipantError`] might be thrown if the `Participant` is already
/// closed. [`Streams`] can be created without a answer from the remote
/// side, resulting in very fast creation and closing latency.
///
/// # Examples
/// ```rust
/// use uvth::ThreadPoolBuilder;
/// use veloren_network::{Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED};
///
/// // Create a Network, connect on port 2000 and open a stream
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// let p1 = network
/// .connect(Address::Tcp("127.0.0.1:2000".parse().unwrap()))
/// .await?;
/// let _s1 = p1
/// .open(100, PROMISES_ORDERED | PROMISES_CONSISTENCY)
/// .await?;
/// });
/// ```
///
/// [`Streams`]: crate::api::Stream
pub async fn open(&self, prio: u8, promises: Promises) -> Result<Stream, ParticipantError> {
//use this lock for now to make sure that only one open at a time is made,
// TODO: not sure if we can paralise that, check in future
let mut stream_open_sender = self.stream_open_sender.write().await;
if self.closed.load(Ordering::Relaxed) {
warn!(?self.remote_pid, "participant is closed but another open is tried on it");
return Err(ParticipantError::ParticipantClosed);
}
let (sid_sender, sid_receiver) = oneshot::channel();
if stream_open_sender
.send((prio, promises, sid_sender))
.await
.is_err()
{
debug!(?self.remote_pid, ?User, "stream_open_sender failed, closing participant");
self.closed.store(true, Ordering::Relaxed);
return Err(ParticipantError::ParticipantClosed);
}
match sid_receiver.await {
Ok(stream) => {
let sid = stream.sid;
debug!(?sid, ?self.remote_pid, ?User, "opened stream");
Ok(stream)
},
Err(_) => {
debug!(?self.remote_pid, ?User, "sid_receiver failed, closing participant");
self.closed.store(true, Ordering::Relaxed);
Err(ParticipantError::ParticipantClosed)
},
2020-03-04 15:52:30 +00:00
}
}
2020-05-10 02:07:46 +00:00
/// Use this method to handle [`Streams`] opened from remote site, like the
/// [`connected`] method of [`Network`]. This is the associated method
/// to [`open`]. It's guaranteed that the order of [`open`] and `opened`
/// is equal. The `nth` [`Streams`] on one side will represent the `nth` on
/// the other side. A [`ParticipantError`] might be thrown if the
/// `Participant` is already closed.
///
/// # Examples
/// ```rust
/// use veloren_network::{Network, Pid, PROMISES_ORDERED, PROMISES_CONSISTENCY};
/// use uvth::ThreadPoolBuilder;
///
/// // Create a Network, connect on port 2000 and wait for the other side to open a stream
/// // Note: It's quite unusal to activly connect, but then wait on a stream to be connected, usually the Appication taking initiative want's to also create the first Stream.
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// let p1 = network.connect(Address::Tcp("127.0.0.1:2000".parse().unwrap())).await?;
/// let _s1 = p1.opened().await?;
/// });
/// ```
///
/// [`Streams`]: crate::api::Stream
/// [`connected`]: Network::connected
/// [`open`]: Participant::open
pub async fn opened(&self) -> Result<Stream, ParticipantError> {
//use this lock for now to make sure that only one open at a time is made,
// TODO: not sure if we can paralise that, check in future
let mut stream_opened_receiver = self.stream_opened_receiver.write().await;
if self.closed.load(Ordering::Relaxed) {
warn!(?self.remote_pid, "participant is closed but another open is tried on it");
return Err(ParticipantError::ParticipantClosed);
}
match stream_opened_receiver.next().await {
Some(stream) => {
let sid = stream.sid;
debug!(?sid, ?self.remote_pid, "receive opened stream");
Ok(stream)
},
None => {
debug!(?self.remote_pid, "stream_opened_receiver failed, closing participant");
self.closed.store(true, Ordering::Relaxed);
Err(ParticipantError::ParticipantClosed)
},
}
}
2020-05-10 02:07:46 +00:00
/// Returns the remote [`Pid`]
pub fn remote_pid(&self) -> Pid { self.remote_pid }
}
impl Stream {
pub(crate) fn new(
pid: Pid,
sid: Sid,
prio: Prio,
promises: Promises,
msg_send_sender: std::sync::mpsc::Sender<(Prio, Pid, Sid, OutGoingMessage)>,
msg_recv_receiver: mpsc::UnboundedReceiver<InCommingMessage>,
closed: Arc<AtomicBool>,
shutdown_sender: mpsc::UnboundedSender<Sid>,
) -> Self {
Self {
pid,
sid,
mid: 0,
prio,
promises,
msg_send_sender,
msg_recv_receiver,
closed,
shutdown_sender: Some(shutdown_sender),
}
}
2020-05-10 02:07:46 +00:00
/// use to send a arbitrary message to the remote side, by having the remote
/// side also opened a `Stream` linked to this. the message will be
/// [`Serialized`], which actually is quite slow compared to most other
/// calculations done. A faster method [`send_raw`] exists, when extra
/// speed is needed. The other side needs to use the respective [`recv`]
/// function and know the type send.
///
/// `send` is an exception to the `async` messages, as it's probably called
/// quite often so it doesn't wait for execution. Which also means, that
/// no feedback is provided. It's to assume that the Message got `send`
/// correctly. If a error occurred, the next call will return an Error.
/// If the [`Participant`] disconnected it will also be unable to be used
/// any more. A [`StreamError`] will be returned in the error case, e.g.
/// when the `Stream` got closed already.
///
/// Note when a `Stream` is dropped, it will still send all messages, though
/// the `drop` will return immediately, however, when a [`Participant`]
/// gets gracefully shut down, all remaining messages will be send.
///
/// # Example
/// ```rust
/// use futures::executor::block_on;
/// use veloren_network::{Network, Pid};
///
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// let participant_a = network.connected().await;
/// let mut stream_a = participant_a.opened().await;
/// //Send Message
/// stream_a.send("Hello World");
/// });
/// ```
///
/// [`send_raw`]: Stream::send_raw
/// [`recv`]: Stream::recv
/// [`Serialized`]: Serialize
pub fn send<M: Serialize>(&mut self, msg: M) -> Result<(), StreamError> {
self.send_raw(Arc::new(message::serialize(&msg)))
}
2020-05-10 02:07:46 +00:00
/// This methods give the option to skip multiple calls of [`bincode`], e.g.
/// in case the same Message needs to send on multiple `Streams` to multiple
/// [`Participants`]. Other then that, the same rules apply than for
/// [`send`]
///
/// # Example
/// ```rust
/// use bincode;
/// use futures::executor::block_on;
/// use veloren_network::{Network, Pid};
///
/// let network = Network::new(Pid::new(), ThreadPoolBuilder::new().build(), None);
/// block_on(async {
/// let participant_a = network.connected().await;
/// let participant_b = network.connected().await;
/// let mut stream_a = participant_a.opened().await;
/// let mut stream_b = participant_a.opened().await;
///
/// //Prepare Message and decode it
/// let msg = "Hello World";
/// let raw_msg = Arc::new(MessageBuffer {
/// data: bincode::serialize(&msg).unwrap(),
/// });
/// //Send same Message to multiple Streams
/// stream_a.send_raw(raw_msg.clone());
/// stream_b.send_raw(raw_msg.clone());
/// });
/// ```
///
/// [`send`]: Stream::send
/// [`Participants`]: crate::api::Participant
pub fn send_raw(&mut self, messagebuffer: Arc<MessageBuffer>) -> Result<(), StreamError> {
if self.closed.load(Ordering::Relaxed) {
return Err(StreamError::StreamClosed);
}
debug!(?messagebuffer, ?User, "sending a message");
self.msg_send_sender
.send((self.prio, self.pid, self.sid, OutGoingMessage {
2020-03-04 15:52:30 +00:00
buffer: messagebuffer,
2020-03-04 00:37:36 +00:00
cursor: 0,
mid: self.mid,
2020-03-04 00:37:36 +00:00
sid: self.sid,
}))?;
self.mid += 1;
Ok(())
}
2020-05-10 02:07:46 +00:00
/// use `recv` to wait on a Message send from the remote side by their
/// `Stream`. The Message needs to implement [`DeserializeOwned`] and
/// thus, the resulting type must already be known by the receiving side.
/// If this is not know from the Application logic, one could use a `Enum`
/// and then handle the received message via a `match` state.
///
/// A [`StreamError`] will be returned in the error case, e.g. when the
/// `Stream` got closed already.
2020-03-04 10:59:19 +00:00
pub async fn recv<M: DeserializeOwned>(&mut self) -> Result<M, StreamError> {
Ok(message::deserialize(self.recv_raw().await?))
}
2020-05-10 02:07:46 +00:00
/// the equivalent like [`send_raw`] but for [`recv`], no [`bincode`] is
/// executed for performance reasons.
///
/// [`send_raw`]: Stream::send_raw
/// [`recv`]: Stream::recv
pub async fn recv_raw(&mut self) -> Result<MessageBuffer, StreamError> {
//no need to access self.closed here, as when this stream is closed the Channel
// is closed which will trigger a None
let msg = self.msg_recv_receiver.next().await?;
info!(?msg, ?User, "delivering a message");
Ok(msg.buffer)
}
}
impl Drop for Network {
fn drop(&mut self) {
let pid = self.local_pid;
debug!(?pid, "shutting down Network");
self.shutdown_sender
.take()
.unwrap()
.send(())
.expect("scheduler is closed, but nobody other should be able to close it");
}
}
impl Drop for Participant {
fn drop(&mut self) {
// ignore closed, as we need to send it even though we disconnected the
// participant from network
let pid = self.remote_pid;
debug!(?pid, "shutting down Participant");
task::block_on(async {
self.disconnect_sender
.take()
.unwrap()
.send(self.remote_pid)
.await
.expect("something is wrong in internal scheduler coding")
});
}
}
impl Drop for Stream {
fn drop(&mut self) {
// a send if closed is unecessary but doesnt hurt, we must not crash here
if !self.closed.load(Ordering::Relaxed) {
let sid = self.sid;
let pid = self.pid;
debug!(?pid, ?sid, "shutting down Stream");
if task::block_on(self.shutdown_sender.take().unwrap().send(self.sid)).is_err() {
warn!(
"Other side got already dropped, probably due to timing, other side will \
handle this gracefully"
);
};
}
}
}
impl std::fmt::Debug for Participant {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let status = if self.closed.load(Ordering::Relaxed) {
"[CLOSED]"
} else {
"[OPEN]"
};
write!(
f,
"Participant {{{} local_pid: {:?}, remote_pid: {:?} }}",
status, &self.local_pid, &self.remote_pid,
)
}
}
impl<T> From<std::sync::mpsc::SendError<T>> for StreamError {
fn from(_err: std::sync::mpsc::SendError<T>) -> Self { StreamError::StreamClosed }
}
impl<T> From<std::sync::mpsc::SendError<T>> for ParticipantError {
fn from(_err: std::sync::mpsc::SendError<T>) -> Self { ParticipantError::ParticipantClosed }
}
impl<T> From<std::sync::mpsc::SendError<T>> for NetworkError {
fn from(_err: std::sync::mpsc::SendError<T>) -> Self { NetworkError::NetworkClosed }
}
impl From<async_std::io::Error> for NetworkError {
fn from(err: async_std::io::Error) -> Self { NetworkError::ListenFailed(err) }
}
impl From<std::option::NoneError> for StreamError {
fn from(_err: std::option::NoneError) -> Self { StreamError::StreamClosed }
}
impl From<std::option::NoneError> for ParticipantError {
fn from(_err: std::option::NoneError) -> Self { ParticipantError::ParticipantClosed }
}
impl From<std::option::NoneError> for NetworkError {
fn from(_err: std::option::NoneError) -> Self { NetworkError::NetworkClosed }
}
impl From<mpsc::SendError> for ParticipantError {
fn from(_err: mpsc::SendError) -> Self { ParticipantError::ParticipantClosed }
}
impl From<mpsc::SendError> for NetworkError {
fn from(_err: mpsc::SendError) -> Self { NetworkError::NetworkClosed }
}
impl From<oneshot::Canceled> for ParticipantError {
fn from(_err: oneshot::Canceled) -> Self { ParticipantError::ParticipantClosed }
}
impl From<oneshot::Canceled> for NetworkError {
fn from(_err: oneshot::Canceled) -> Self { NetworkError::NetworkClosed }
}