2020-05-27 15:58:57 +00:00
//!
//!
//!
//! (cd network/examples/async_recv && RUST_BACKTRACE=1 cargo run)
2020-01-13 16:53:28 +00:00
use crate ::{
2020-06-08 09:47:39 +00:00
message ::{ self , partial_eq_bincode , IncomingMessage , MessageBuffer , OutgoingMessage } ,
2020-03-22 13:47:21 +00:00
scheduler ::Scheduler ,
2020-05-15 12:29:17 +00:00
types ::{ Mid , Pid , Prio , Promises , Sid } ,
2020-01-13 16:53:28 +00:00
} ;
2020-04-08 14:26:42 +00:00
use async_std ::{ io , sync ::RwLock , task } ;
2020-03-22 13:47:21 +00:00
use futures ::{
channel ::{ mpsc , oneshot } ,
sink ::SinkExt ,
stream ::StreamExt ,
2019-12-20 13:56:01 +00:00
} ;
2020-04-24 10:56:04 +00:00
use prometheus ::Registry ;
2020-03-22 13:47:21 +00:00
use serde ::{ de ::DeserializeOwned , Serialize } ;
2020-02-04 15:42:04 +00:00
use std ::{
collections ::HashMap ,
2020-05-26 13:06:03 +00:00
net ::SocketAddr ,
2020-03-22 13:47:21 +00:00
sync ::{
2020-05-22 14:00:08 +00:00
atomic ::{ AtomicBool , Ordering } ,
2020-03-22 13:47:21 +00:00
Arc ,
} ,
2020-02-04 15:42:04 +00:00
} ;
2020-01-13 16:53:28 +00:00
use tracing ::* ;
2020-03-22 13:47:21 +00:00
use tracing_futures ::Instrument ;
2019-12-20 13:56:01 +00:00
2020-05-10 02:07:46 +00:00
/// Represents a Tcp or Udp or Mpsc address
2020-03-22 13:47:21 +00:00
#[ derive(Clone, Debug, Hash, PartialEq, Eq) ]
2019-12-20 13:56:01 +00:00
pub enum Address {
2020-05-26 13:06:03 +00:00
Tcp ( SocketAddr ) ,
Udp ( SocketAddr ) ,
2020-03-10 00:07:36 +00:00
Mpsc ( u64 ) ,
2019-12-20 13:56:01 +00:00
}
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
2019-12-20 13:56:01 +00:00
pub struct Participant {
2020-03-22 13:47:21 +00:00
local_pid : Pid ,
2020-02-10 17:25:47 +00:00
remote_pid : Pid ,
2020-05-15 12:29:17 +00:00
a2b_steam_open_s : RwLock < mpsc ::UnboundedSender < ( Prio , Promises , oneshot ::Sender < Stream > ) > > ,
b2a_stream_opened_r : RwLock < mpsc ::UnboundedReceiver < Stream > > ,
2020-03-22 13:47:21 +00:00
closed : AtomicBool ,
2020-05-15 12:29:17 +00:00
a2s_disconnect_s :
Option < mpsc ::UnboundedSender < ( Pid , oneshot ::Sender < async_std ::io ::Result < ( ) > > ) > > ,
2019-12-20 13:56:01 +00:00
}
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`.
///
/// [`Networks`]: crate::api::Network
/// [`open`]: Participant::open
/// [`opened`]: Participant::opened
2020-03-22 13:47:21 +00:00
#[ derive(Debug) ]
2020-02-04 15:42:04 +00:00
pub struct Stream {
2020-03-22 13:47:21 +00:00
pid : Pid ,
2020-02-04 15:42:04 +00:00
sid : Sid ,
2020-03-22 13:47:21 +00:00
mid : Mid ,
prio : Prio ,
promises : Promises ,
2020-07-03 19:55:00 +00:00
a2b_msg_s : crossbeam_channel ::Sender < ( Prio , Sid , OutgoingMessage ) > ,
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
b2a_msg_recv_r : mpsc ::UnboundedReceiver < IncomingMessage > ,
2020-04-08 14:26:42 +00:00
closed : Arc < AtomicBool > ,
2020-05-15 12:29:17 +00:00
a2b_close_stream_s : Option < mpsc ::UnboundedSender < Sid > > ,
2020-02-04 15:42:04 +00:00
}
2019-12-20 13:56:01 +00:00
2020-05-10 02:07:46 +00:00
/// Error type thrown by [`Networks`](Network) methods
2020-04-08 14:26:42 +00:00
#[ derive(Debug) ]
pub enum NetworkError {
NetworkClosed ,
ListenFailed ( std ::io ::Error ) ,
2020-07-04 10:17:33 +00:00
ConnectFailed ( std ::io ::Error ) ,
GracefulDisconnectFailed ( std ::io ::Error ) ,
2020-04-08 14:26:42 +00:00
}
2020-03-22 13:47:21 +00:00
2020-05-10 02:07:46 +00:00
/// Error type thrown by [`Participants`](Participant) methods
2020-04-08 14:26:42 +00:00
#[ derive(Debug, PartialEq) ]
pub enum ParticipantError {
ParticipantClosed ,
}
2020-03-22 13:47:21 +00:00
2020-05-10 02:07:46 +00:00
/// Error type thrown by [`Streams`](Stream) methods
2020-06-08 09:47:39 +00:00
#[ derive(Debug) ]
2020-04-08 14:26:42 +00:00
pub enum StreamError {
StreamClosed ,
2020-06-08 09:47:39 +00:00
DeserializeError ( Box < bincode ::ErrorKind > ) ,
2020-04-08 14:26:42 +00:00
}
2020-03-22 13:47:21 +00:00
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
2020-05-26 13:06:03 +00:00
/// use veloren_network::{Network, Address, Pid};
/// use futures::executor::block_on;
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, listen on port `2999` to accept connections and connect to port `8080` to connect to a (pseudo) database Application
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
2020-05-26 13:06:03 +00:00
/// block_on(async{
/// # //setup pseudo database!
2020-06-08 09:47:39 +00:00
/// # let (database, fd) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fd);
2020-05-26 13:06:03 +00:00
/// # database.listen(Address::Tcp("127.0.0.1:8080".parse().unwrap())).await?;
2020-05-27 15:58:57 +00:00
/// network.listen(Address::Tcp("127.0.0.1:2999".parse().unwrap())).await?;
2020-05-26 13:06:03 +00:00
/// let database = network.connect(Address::Tcp("127.0.0.1:8080".parse().unwrap())).await?;
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`Participants`]: crate::api::Participant
/// [`connect`]: Network::connect
/// [`disconnect`]: Network::disconnect
2020-02-21 15:10:55 +00:00
pub struct Network {
2020-03-22 13:47:21 +00:00
local_pid : Pid ,
participants : RwLock < HashMap < Pid , Arc < Participant > > > ,
2020-04-08 14:26:42 +00:00
listen_sender :
RwLock < mpsc ::UnboundedSender < ( Address , oneshot ::Sender < async_std ::io ::Result < ( ) > > ) > > ,
connect_sender :
RwLock < mpsc ::UnboundedSender < ( Address , oneshot ::Sender < io ::Result < Participant > > ) > > ,
2020-03-22 13:47:21 +00:00
connected_receiver : RwLock < mpsc ::UnboundedReceiver < Participant > > ,
shutdown_sender : Option < oneshot ::Sender < ( ) > > ,
2019-12-20 13:56:01 +00:00
}
2020-02-21 15:10:55 +00:00
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`
/// * `registry` - Provide a Registy in order to collect Prometheus metrics
/// by this `Network`, `None` will deactivate Tracing. Tracing is done via
/// [`prometheus`]
///
2020-06-08 09:47:39 +00:00
/// # Result
/// * `Self` - returns a `Network` which can be `Send` to multiple areas of
/// your code, including multiple threads. This is the base strct of this
/// crate.
/// * `FnOnce` - you need to run the returning FnOnce exactly once, probably
/// in it's own thread. this is NOT done internally, so that you are free
/// to choose the threadpool implementation of your choice. We recommend
/// using [`ThreadPool`] from [`uvth`] crate. This fn will runn the
/// Scheduler to handle all `Network` internals. Additional threads will
/// be allocated on an internal async-aware threadpool
///
2020-05-10 02:07:46 +00:00
/// # Examples
/// ```rust
2020-06-08 09:47:39 +00:00
/// //Example with uvth
2020-05-10 02:07:46 +00:00
/// use uvth::ThreadPoolBuilder;
2020-05-26 13:06:03 +00:00
/// use veloren_network::{Address, Network, Pid};
2020-05-10 02:07:46 +00:00
///
2020-06-08 09:47:39 +00:00
/// let pool = ThreadPoolBuilder::new().build();
/// let (network, f) = Network::new(Pid::new(), None);
/// pool.execute(f);
2020-05-10 02:07:46 +00:00
/// ```
///
2020-06-08 09:47:39 +00:00
/// ```rust
/// //Example with std::thread
/// use veloren_network::{Address, Network, Pid};
///
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// ```
///
/// Usually you only create a single `Network` for an appliregistrycation,
/// except when client and server are in the same application, then you
/// will want 2. However there are no technical limitations from
/// creating more.
2020-05-10 02:07:46 +00:00
///
/// [`Pid::new()`]: crate::types::Pid::new
2020-06-08 09:47:39 +00:00
/// [`ThreadPool`]: https://docs.rs/uvth/newest/uvth/struct.ThreadPool.html
/// [`uvth`]: https://docs.rs/uvth
pub fn new (
participant_id : Pid ,
registry : Option < & Registry > ,
) -> ( Self , impl std ::ops ::FnOnce ( ) ) {
2020-03-22 13:47:21 +00:00
let p = participant_id ;
2020-05-15 12:29:17 +00:00
debug! ( ? p , " starting Network " ) ;
2020-03-22 13:47:21 +00:00
let ( scheduler , listen_sender , connect_sender , connected_receiver , shutdown_sender ) =
2020-04-24 10:56:04 +00:00
Scheduler ::new ( participant_id , registry ) ;
2020-06-08 09:47:39 +00:00
(
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 ) ,
} ,
move | | {
trace! ( ? p , " starting sheduler in own thread " ) ;
let _handle = task ::block_on (
scheduler
. run ( )
. instrument ( tracing ::info_span! ( " scheduler " , ? p ) ) ,
) ;
trace! ( ? p , " stopping sheduler and his own thread " ) ;
} ,
)
2019-12-20 13:56:01 +00:00
}
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
2020-05-26 13:06:03 +00:00
/// use futures::executor::block_on;
/// use veloren_network::{Address, Network, Pid};
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-10 02:07:46 +00:00
/// // Create a Network, listen on port `2000` TCP on all NICs and `2001` UDP locally
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
2020-05-10 02:07:46 +00:00
/// 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?;
2020-05-26 13:06:03 +00:00
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`connected`]: Network::connected
2020-04-08 14:26:42 +00:00
pub async fn listen ( & self , address : Address ) -> Result < ( ) , NetworkError > {
2020-05-15 12:29:17 +00:00
let ( s2a_result_s , s2a_result_r ) = oneshot ::channel ::< async_std ::io ::Result < ( ) > > ( ) ;
debug! ( ? address , " listening on address " ) ;
2020-04-08 14:26:42 +00:00
self . listen_sender
. write ( )
. await
2020-05-15 12:29:17 +00:00
. send ( ( address , s2a_result_s ) )
2020-04-08 14:26:42 +00:00
. await ? ;
2020-05-15 12:29:17 +00:00
match s2a_result_r . await ? {
2020-04-08 14:26:42 +00:00
//waiting guarantees that we either listened sucessfully or get an error like port in
// use
Ok ( ( ) ) = > Ok ( ( ) ) ,
Err ( e ) = > Err ( NetworkError ::ListenFailed ( e ) ) ,
}
2019-12-20 13:56:01 +00:00
}
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
2020-05-26 13:06:03 +00:00
/// use futures::executor::block_on;
/// use veloren_network::{Address, Network, Pid};
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, connect on port `2010` TCP and `2011` UDP like listening above
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-10 02:07:46 +00:00
/// block_on(async {
2020-05-27 15:58:57 +00:00
/// # remote.listen(Address::Tcp("0.0.0.0:2010".parse().unwrap())).await?;
/// # remote.listen(Address::Udp("0.0.0.0:2011".parse().unwrap())).await?;
2020-05-10 02:07:46 +00:00
/// let p1 = network
2020-05-27 15:58:57 +00:00
/// .connect(Address::Tcp("127.0.0.1:2010".parse().unwrap()))
2020-05-10 02:07:46 +00:00
/// .await?;
2020-05-26 13:06:03 +00:00
/// # //this doesn't work yet, so skip the test
/// # //TODO fixme!
/// # return Ok(());
2020-05-10 02:07:46 +00:00
/// let p2 = network
2020-05-27 15:58:57 +00:00
/// .connect(Address::Udp("127.0.0.1:2011".parse().unwrap()))
2020-05-10 02:07:46 +00:00
/// .await?;
2020-05-26 13:06:03 +00:00
/// assert!(std::sync::Arc::ptr_eq(&p1, &p2));
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
/// 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
2020-03-22 13:47:21 +00:00
pub async fn connect ( & self , address : Address ) -> Result < Arc < Participant > , NetworkError > {
2020-04-08 14:26:42 +00:00
let ( pid_sender , pid_receiver ) = oneshot ::channel ::< io ::Result < Participant > > ( ) ;
2020-05-15 12:29:17 +00:00
debug! ( ? address , " connect to address " ) ;
2020-03-22 13:47:21 +00:00
self . connect_sender
. write ( )
. await
. send ( ( address , pid_sender ) )
2020-04-08 14:26:42 +00:00
. await ? ;
2020-07-04 10:17:33 +00:00
let participant = match pid_receiver . await ? {
Ok ( p ) = > p ,
Err ( e ) = > return Err ( NetworkError ::ConnectFailed ( e ) ) ,
} ;
2020-04-08 14:26:42 +00:00
let pid = participant . remote_pid ;
debug! (
? pid ,
" received Participant id from remote and return to user "
) ;
let participant = Arc ::new ( participant ) ;
self . participants
. write ( )
2020-03-22 13:47:21 +00:00
. await
2020-04-08 14:26:42 +00:00
. insert ( participant . remote_pid , participant . clone ( ) ) ;
Ok ( participant )
2020-03-22 13:47:21 +00:00
}
2020-03-10 00:07:36 +00:00
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
2020-05-26 13:06:03 +00:00
/// use futures::executor::block_on;
/// use veloren_network::{Address, Network, Pid};
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, listen on port `2020` TCP and opens returns their Pid
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-10 02:07:46 +00:00
/// block_on(async {
/// network
2020-05-27 15:58:57 +00:00
/// .listen(Address::Tcp("0.0.0.0:2020".parse().unwrap()))
2020-05-10 02:07:46 +00:00
/// .await?;
2020-05-27 15:58:57 +00:00
/// # remote.connect(Address::Tcp("0.0.0.0:2020".parse().unwrap())).await?;
2020-05-26 13:06:03 +00:00
/// while let Ok(participant) = network.connected().await {
2020-05-10 02:07:46 +00:00
/// println!("Participant connected: {}", participant.remote_pid());
2020-05-26 13:06:03 +00:00
/// # //skip test here as it would be a endless loop
/// # break;
2020-05-10 02:07:46 +00:00
/// }
2020-05-26 13:06:03 +00:00
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`Streams`]: crate::api::Stream
/// [`listen`]: crate::api::Network::listen
2020-03-22 13:47:21 +00:00
pub async fn connected ( & self ) -> Result < Arc < Participant > , NetworkError > {
2020-04-08 14:26:42 +00:00
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 )
2019-12-20 13:56:01 +00:00
}
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.
2020-05-15 12:29:17 +00:00
///
2020-05-10 02:07:46 +00:00
/// This function will wait for all [`Streams`] to properly close, including
2020-05-15 12:29:17 +00:00
/// all messages to be send before closing. If an error occurs with one
2020-05-27 15:58:57 +00:00
/// of the messages.
/// Except if the remote side already dropped the [`Participant`]
/// simultaneously, then messages won't be sended
2020-05-10 02:07:46 +00:00
///
2020-06-08 09:47:39 +00:00
/// There is NO `disconnected` function in `Network`, if a [`Participant`]
/// is no longer reachable (e.g. as the network cable was unplugged) the
/// [`Participant`] will fail all action, but needs to be manually
/// disconected, using this function.
///
2020-05-10 02:07:46 +00:00
/// # Examples
/// ```rust
2020-05-26 13:06:03 +00:00
/// use futures::executor::block_on;
/// use veloren_network::{Address, Network, Pid};
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, listen on port `2030` TCP and opens returns their Pid and close connection.
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-10 02:07:46 +00:00
/// block_on(async {
/// network
2020-05-27 15:58:57 +00:00
/// .listen(Address::Tcp("0.0.0.0:2030".parse().unwrap()))
2020-05-10 02:07:46 +00:00
/// .await?;
2020-05-27 15:58:57 +00:00
/// # remote.connect(Address::Tcp("0.0.0.0:2030".parse().unwrap())).await?;
2020-05-26 13:06:03 +00:00
/// while let Ok(participant) = network.connected().await {
2020-05-10 02:07:46 +00:00
/// println!("Participant connected: {}", participant.remote_pid());
/// network.disconnect(participant).await?;
2020-05-26 13:06:03 +00:00
/// # //skip test here as it would be a endless loop
/// # break;
2020-05-10 02:07:46 +00:00
/// }
2020-05-26 13:06:03 +00:00
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`Arc<Participant>`]: crate::api::Participant
/// [`Streams`]: crate::api::Stream
/// [`participants`]: Network::participants
/// [`Arc`]: std::sync::Arc
2020-03-22 13:47:21 +00:00
pub async fn disconnect ( & self , participant : Arc < Participant > ) -> Result < ( ) , NetworkError > {
// Remove, Close and try_unwrap error when unwrap fails!
2020-04-08 14:26:42 +00:00
let pid = participant . remote_pid ;
debug! ( ? pid , " removing participant from network " ) ;
self . participants . write ( ) . await . remove ( & pid ) ? ;
2020-03-22 13:47:21 +00:00
participant . closed . store ( true , Ordering ::Relaxed ) ;
2020-01-13 16:53:28 +00:00
2020-05-15 12:29:17 +00:00
match Arc ::try_unwrap ( participant ) {
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 "
) ;
2020-07-04 10:17:33 +00:00
Ok ( ( ) )
2020-05-15 12:29:17 +00:00
} ,
Ok ( mut participant ) = > {
trace! ( " waiting now for participant to close " ) ;
let ( finished_sender , finished_receiver ) = oneshot ::channel ( ) ;
// we are deleting here asyncly before DROP is called. Because this is done
// nativly async, while drop needs an BLOCK! Drop will recognis
// that it has been delete here and don't try another double delete.
participant
. a2s_disconnect_s
. take ( )
. unwrap ( )
. send ( ( pid , finished_sender ) )
. await
. expect ( " something is wrong in internal scheduler coding " ) ;
2020-07-04 10:17:33 +00:00
match finished_receiver . await {
Ok ( Ok ( ( ) ) ) = > {
trace! ( ? pid , " Participant is now closed " ) ;
Ok ( ( ) )
} ,
Ok ( Err ( e ) ) = > {
trace! (
? e ,
" Error occured during shutdown of participant and is propagated to \
User "
) ;
Err ( NetworkError ::GracefulDisconnectFailed ( e ) )
} ,
Err ( e ) = > {
error! (
? pid ,
? e ,
" Failed to get a message back from the scheduler, closing the network "
) ;
Err ( NetworkError ::NetworkClosed )
} ,
}
2020-05-15 12:29:17 +00:00
} ,
2020-07-04 10:17:33 +00:00
}
2020-02-10 17:25:47 +00:00
}
2020-06-08 09:47:39 +00:00
/// returns a copy of all current connected [`Participants`],
/// including ones, which can't send data anymore as the underlying sockets
/// are closed already but haven't been [`disconnected`] yet.
2020-05-10 02:07:46 +00:00
///
/// [`Participants`]: crate::api::Participant
2020-06-08 09:47:39 +00:00
/// [`disconnected`]: Network::disconnect
2020-04-08 14:26:42 +00:00
pub async fn participants ( & self ) -> HashMap < Pid , Arc < Participant > > {
self . participants . read ( ) . await . clone ( )
}
}
2020-02-10 17:25:47 +00:00
2020-03-22 13:47:21 +00:00
impl Participant {
pub ( crate ) fn new (
local_pid : Pid ,
remote_pid : Pid ,
2020-05-15 12:29:17 +00:00
a2b_steam_open_s : mpsc ::UnboundedSender < ( Prio , Promises , oneshot ::Sender < Stream > ) > ,
b2a_stream_opened_r : mpsc ::UnboundedReceiver < Stream > ,
a2s_disconnect_s : mpsc ::UnboundedSender < ( Pid , oneshot ::Sender < async_std ::io ::Result < ( ) > > ) > ,
2020-03-22 13:47:21 +00:00
) -> Self {
Self {
local_pid ,
remote_pid ,
2020-05-15 12:29:17 +00:00
a2b_steam_open_s : RwLock ::new ( a2b_steam_open_s ) ,
b2a_stream_opened_r : RwLock ::new ( b2a_stream_opened_r ) ,
2020-03-22 13:47:21 +00:00
closed : AtomicBool ::new ( false ) ,
2020-05-15 12:29:17 +00:00
a2s_disconnect_s : Some ( a2s_disconnect_s ) ,
2020-03-04 15:52:30 +00:00
}
2020-02-10 17:25:47 +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
2020-05-26 13:06:03 +00:00
/// use futures::executor::block_on;
/// use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED};
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, connect on port 2100 and open a stream
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-10 02:07:46 +00:00
/// block_on(async {
2020-05-27 15:58:57 +00:00
/// # remote.listen(Address::Tcp("0.0.0.0:2100".parse().unwrap())).await?;
2020-05-10 02:07:46 +00:00
/// let p1 = network
2020-05-27 15:58:57 +00:00
/// .connect(Address::Tcp("127.0.0.1:2100".parse().unwrap()))
2020-05-10 02:07:46 +00:00
/// .await?;
2020-05-26 13:06:03 +00:00
/// let _s1 = p1.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?;
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`Streams`]: crate::api::Stream
2020-03-22 13:47:21 +00:00
pub async fn open ( & self , prio : u8 , promises : Promises ) -> Result < Stream , ParticipantError > {
2020-04-08 14:26:42 +00:00
//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
2020-05-15 12:29:17 +00:00
let mut a2b_steam_open_s = self . a2b_steam_open_s . write ( ) . await ;
2020-04-08 14:26:42 +00:00
if self . closed . load ( Ordering ::Relaxed ) {
warn! ( ? self . remote_pid , " participant is closed but another open is tried on it " ) ;
return Err ( ParticipantError ::ParticipantClosed ) ;
}
2020-05-15 12:29:17 +00:00
let ( p2a_return_stream_s , p2a_return_stream_r ) = oneshot ::channel ( ) ;
if a2b_steam_open_s
. send ( ( prio , promises , p2a_return_stream_s ) )
2020-03-22 13:47:21 +00:00
. await
2020-04-08 14:26:42 +00:00
. is_err ( )
{
2020-05-15 12:29:17 +00:00
debug! ( ? self . remote_pid , " stream_open_sender failed, closing participant " ) ;
2020-04-08 14:26:42 +00:00
self . closed . store ( true , Ordering ::Relaxed ) ;
return Err ( ParticipantError ::ParticipantClosed ) ;
}
2020-05-15 12:29:17 +00:00
match p2a_return_stream_r . await {
2020-03-22 13:47:21 +00:00
Ok ( stream ) = > {
let sid = stream . sid ;
2020-05-15 12:29:17 +00:00
debug! ( ? sid , ? self . remote_pid , " opened stream " ) ;
2020-03-22 13:47:21 +00:00
Ok ( stream )
} ,
2020-04-08 14:26:42 +00:00
Err ( _ ) = > {
2020-05-15 12:29:17 +00:00
debug! ( ? self . remote_pid , " p2a_return_stream_r failed, closing participant " ) ;
2020-04-08 14:26:42 +00:00
self . closed . store ( true , Ordering ::Relaxed ) ;
Err ( ParticipantError ::ParticipantClosed )
} ,
2020-03-04 15:52:30 +00:00
}
}
2020-02-21 15:10:55 +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
2020-05-26 13:06:03 +00:00
/// use veloren_network::{Network, Pid, Address, PROMISES_ORDERED, PROMISES_CONSISTENCY};
/// use futures::executor::block_on;
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, connect on port 2110 and wait for the other side to open a stream
2020-05-10 02:07:46 +00:00
/// // 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.
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-10 02:07:46 +00:00
/// block_on(async {
2020-05-27 15:58:57 +00:00
/// # remote.listen(Address::Tcp("0.0.0.0:2110".parse().unwrap())).await?;
/// let p1 = network.connect(Address::Tcp("127.0.0.1:2110".parse().unwrap())).await?;
2020-05-26 13:06:03 +00:00
/// # let p2 = remote.connected().await?;
/// # p2.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?;
2020-05-10 02:07:46 +00:00
/// let _s1 = p1.opened().await?;
2020-05-26 13:06:03 +00:00
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`Streams`]: crate::api::Stream
/// [`connected`]: Network::connected
/// [`open`]: Participant::open
2020-02-21 15:10:55 +00:00
pub async fn opened ( & self ) -> Result < Stream , ParticipantError > {
2020-04-08 14:26:42 +00:00
//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
2020-05-15 12:29:17 +00:00
let mut stream_opened_receiver = self . b2a_stream_opened_r . write ( ) . await ;
2020-04-08 14:26:42 +00:00
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-02-21 15:10:55 +00:00
}
2020-02-10 17:25:47 +00:00
}
2020-04-08 14:26:42 +00:00
2020-05-10 02:07:46 +00:00
/// Returns the remote [`Pid`]
2020-04-08 14:26:42 +00:00
pub fn remote_pid ( & self ) -> Pid { self . remote_pid }
2020-02-10 17:25:47 +00:00
}
impl Stream {
2020-06-08 09:47:39 +00:00
#[ allow(clippy::too_many_arguments) ]
2020-03-10 00:07:36 +00:00
pub ( crate ) fn new (
2020-03-22 13:47:21 +00:00
pid : Pid ,
2020-03-10 00:07:36 +00:00
sid : Sid ,
2020-03-22 13:47:21 +00:00
prio : Prio ,
promises : Promises ,
2020-07-03 19:55:00 +00:00
a2b_msg_s : crossbeam_channel ::Sender < ( Prio , Sid , OutgoingMessage ) > ,
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
b2a_msg_recv_r : mpsc ::UnboundedReceiver < IncomingMessage > ,
2020-04-08 14:26:42 +00:00
closed : Arc < AtomicBool > ,
2020-05-15 12:29:17 +00:00
a2b_close_stream_s : mpsc ::UnboundedSender < Sid > ,
2020-03-10 00:07:36 +00:00
) -> Self {
Self {
2020-03-22 13:47:21 +00:00
pid ,
2020-03-10 00:07:36 +00:00
sid ,
2020-03-22 13:47:21 +00:00
mid : 0 ,
prio ,
promises ,
2020-05-15 12:29:17 +00:00
a2b_msg_s ,
b2a_msg_recv_r ,
2020-04-08 14:26:42 +00:00
closed ,
2020-05-15 12:29:17 +00:00
a2b_close_stream_s : Some ( a2b_close_stream_s ) ,
2020-03-10 00:07:36 +00:00
}
}
2020-02-10 17:25:47 +00:00
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.
///
2020-05-27 15:58:57 +00:00
/// Note when a `Stream` is dropped locally, 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. If the `Stream` is dropped from remote side no further
/// messages are send, because the remote side has no way of listening
/// to them either way. If the last channel is destroyed (e.g. by losing
/// the internet connection or non-gracefull shutdown, pending messages
/// are also dropped.
2020-05-10 02:07:46 +00:00
///
/// # Example
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
/// ```
2020-05-26 13:06:03 +00:00
/// use veloren_network::{Network, Address, Pid};
/// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY};
2020-05-10 02:07:46 +00:00
/// use futures::executor::block_on;
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-05-27 15:58:57 +00:00
/// // Create a Network, listen on Port `2200` and wait for a Stream to be opened, then answer `Hello World`
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-10 02:07:46 +00:00
/// block_on(async {
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
/// network.listen(Address::Tcp("127.0.0.1:2200".parse().unwrap())).await?;
/// # let remote_p = remote.connect(Address::Tcp("127.0.0.1:2200".parse().unwrap())).await?;
/// # // keep it alive
/// # let _stream_p = remote_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?;
/// let participant_a = network.connected().await?;
/// let mut stream_a = participant_a.opened().await?;
2020-05-10 02:07:46 +00:00
/// //Send Message
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
/// stream_a.send("Hello World")?;
/// # Ok(())
2020-05-26 13:06:03 +00:00
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`send_raw`]: Stream::send_raw
/// [`recv`]: Stream::recv
/// [`Serialized`]: Serialize
2020-05-22 14:00:08 +00:00
#[ inline ]
2020-04-08 14:26:42 +00:00
pub fn send < M : Serialize > ( & mut self , msg : M ) -> Result < ( ) , StreamError > {
2020-04-24 10:56:04 +00:00
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
2020-05-26 13:06:03 +00:00
/// use veloren_network::{Network, Address, Pid, MessageBuffer};
/// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY};
2020-05-10 02:07:46 +00:00
/// use futures::executor::block_on;
2020-05-26 13:06:03 +00:00
/// use bincode;
/// use std::sync::Arc;
2020-05-10 02:07:46 +00:00
///
2020-05-26 13:06:03 +00:00
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote1, fr1) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr1);
/// # let (remote2, fr2) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr2);
2020-05-10 02:07:46 +00:00
/// block_on(async {
2020-05-27 15:58:57 +00:00
/// network.listen(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?;
/// # let remote1_p = remote1.connect(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?;
/// # let remote2_p = remote2.connect(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?;
2020-05-26 13:06:03 +00:00
/// # assert_eq!(remote1_p.remote_pid(), remote2_p.remote_pid());
/// # remote1_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?;
/// # remote2_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?;
/// 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_b.opened().await?;
2020-05-10 02:07:46 +00:00
///
/// //Prepare Message and decode it
/// let msg = "Hello World";
2020-05-26 13:06:03 +00:00
/// let raw_msg = Arc::new(MessageBuffer{
2020-05-10 02:07:46 +00:00
/// 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());
2020-05-26 13:06:03 +00:00
/// # Ok(())
/// })
/// # }
2020-05-10 02:07:46 +00:00
/// ```
///
/// [`send`]: Stream::send
/// [`Participants`]: crate::api::Participant
2020-04-24 10:56:04 +00:00
pub fn send_raw ( & mut self , messagebuffer : Arc < MessageBuffer > ) -> Result < ( ) , StreamError > {
2020-04-08 14:26:42 +00:00
if self . closed . load ( Ordering ::Relaxed ) {
return Err ( StreamError ::StreamClosed ) ;
}
2020-05-15 12:29:17 +00:00
//debug!(?messagebuffer, "sending a message");
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
self . a2b_msg_s . send ( ( self . prio , self . sid , OutgoingMessage {
2020-05-22 14:00:08 +00:00
buffer : messagebuffer ,
cursor : 0 ,
mid : self . mid ,
sid : self . sid ,
} ) ) ? ;
2020-03-22 13:47:21 +00:00
self . mid + = 1 ;
2020-02-21 15:10:55 +00:00
Ok ( ( ) )
2020-02-10 17:25:47 +00:00
}
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-05-27 15:58:57 +00:00
///
/// # Example
Fixing the DEADLOCK in handshake -> channel creation
- this bug was initially called imbris bug, as it happened on his runners and i couldn't reproduce it locally at fist :)
- When in a Handshake a seperate mpsc::Channel was created for (Cid, Frame) transport
however the protocol could already catch non handshake data any more and push in into this
mpsc::Channel.
Then this channel got dropped and a fresh one was created for the network::Channel.
These droped Frames are ofc a BUG!
I tried multiple things to solve this:
- dont create a new mpsc::Channel, but instead bind it to the Protocol itself and always use 1.
This would work theoretically, but in bParticipant side we are using 1 mpsc::Channel<(Cid, Frame)>
to handle ALL the network::channel.
If now ever Protocol would have it's own, and with that every network::Channel had it's own it would no longer work out
Bad Idea...
- using the first method but creating the mpsc::Channel inside the scheduler instead protocol neither works, as the
scheduler doesnt know the remote_pid yet
- i dont want a hack to say the protocol only listen to 2 messages and then stop no matter what
So i switched over to the simply method now:
- Do everything like before with 2 mpsc::Channels
- after the handshake. close the receiver and listen for all remaining (cid, frame) combinations
- when starting the channel, reapply them to the new sender/listener combination
- added tracing
- switched Protocol RwLock to Mutex, as it's only ever 1
- Additionally changed the layout and introduces the c2w_frame_s and w2s_cid_frame_s name schema
- Fixed a bug in scheduler which WOULD cause a DEADLOCK if handshake would fail
- fixd a but in api_send_send_main, i need to store the stream_p otherwise it's immeadiatly closed and a stream_a.send() isn't guaranteed
- add extra test to verify that a send message is received even if the Stream is already closed
- changed OutGoing to Outgoing
- fixed a bug that `metrics.tick()` was never called
- removed 2 unused nightly features and added `deny_code`
2020-06-03 07:13:00 +00:00
/// ```
2020-05-27 15:58:57 +00:00
/// use veloren_network::{Network, Address, Pid};
/// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY};
/// use futures::executor::block_on;
///
/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
/// // Create a Network, listen on Port `2220` and wait for a Stream to be opened, then listen on it
2020-06-08 09:47:39 +00:00
/// let (network, f) = Network::new(Pid::new(), None);
/// std::thread::spawn(f);
/// # let (remote, fr) = Network::new(Pid::new(), None);
/// # std::thread::spawn(fr);
2020-05-27 15:58:57 +00:00
/// block_on(async {
/// network.listen(Address::Tcp("127.0.0.1:2220".parse().unwrap())).await?;
/// # let remote_p = remote.connect(Address::Tcp("127.0.0.1:2220".parse().unwrap())).await?;
/// # let mut stream_p = remote_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?;
/// # stream_p.send("Hello World");
/// let participant_a = network.connected().await?;
/// let mut stream_a = participant_a.opened().await?;
/// //Send Message
/// println!("{}", stream_a.recv::<String>().await?);
/// # Ok(())
/// })
/// # }
/// ```
2020-05-22 14:00:08 +00:00
#[ inline ]
2020-03-04 10:59:19 +00:00
pub async fn recv < M : DeserializeOwned > ( & mut self ) -> Result < M , StreamError > {
2020-06-08 09:47:39 +00:00
Ok ( message ::deserialize ( self . recv_raw ( ) . await ? ) ? )
2020-04-24 10:56:04 +00:00
}
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
2020-04-24 10:56:04 +00:00
pub async fn recv_raw ( & mut self ) -> Result < MessageBuffer , StreamError > {
2020-04-08 14:26:42 +00:00
//no need to access self.closed here, as when this stream is closed the Channel
// is closed which will trigger a None
2020-05-15 12:29:17 +00:00
let msg = self . b2a_msg_recv_r . next ( ) . await ? ;
2020-05-27 15:58:57 +00:00
//info!(?msg, "delivering a message");
2020-04-24 10:56:04 +00:00
Ok ( msg . buffer )
2020-02-10 17:25:47 +00:00
}
2020-03-22 13:47:21 +00:00
}
2020-03-10 00:07:36 +00:00
2020-03-22 13:47:21 +00:00
impl Drop for Network {
fn drop ( & mut self ) {
2020-04-08 14:26:42 +00:00
let pid = self . local_pid ;
debug! ( ? pid , " shutting down Network " ) ;
2020-05-15 12:29:17 +00:00
debug! (
? pid ,
" shutting down Participants of Network, while we still have metrics "
) ;
task ::block_on ( async {
2020-05-27 15:58:57 +00:00
// we need to carefully shut down here! as otherwise we might call
// Participant::Drop with a2s_disconnect_s here which would open
// another task::block, which would panic! also i can't `.write` on
// `self.participants` as the `disconnect` fn needs it.
let mut participant_clone = self . participants ( ) . await ;
for ( _ , p ) in participant_clone . drain ( ) {
2020-06-08 09:47:39 +00:00
if let Err ( e ) = self . disconnect ( p ) . await {
error! (
? e ,
" error while dropping network, the error occured when dropping a \
participant but can ' t be notified to the user any more "
) ;
2020-05-27 15:58:57 +00:00
}
}
2020-05-15 12:29:17 +00:00
self . participants . write ( ) . await . clear ( ) ;
} ) ;
debug! ( ? pid , " shutting down Scheduler " ) ;
2020-04-08 14:26:42 +00:00
self . shutdown_sender
. take ( )
. unwrap ( )
. send ( ( ) )
. expect ( " scheduler is closed, but nobody other should be able to close it " ) ;
2020-05-15 12:29:17 +00:00
debug! ( ? pid , " participants have shut down! " ) ;
2020-03-10 00:07:36 +00:00
}
2020-03-22 13:47:21 +00:00
}
2020-03-10 00:07:36 +00:00
2020-03-22 13:47:21 +00:00
impl Drop for Participant {
fn drop ( & mut self ) {
2020-04-08 14:26:42 +00:00
// 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 " ) ;
2020-05-15 12:29:17 +00:00
match self . a2s_disconnect_s . take ( ) {
None = > debug! (
? pid ,
" Participant has been shutdown cleanly, no further waiting is requiered! "
) ,
Some ( mut a2s_disconnect_s ) = > {
debug! (
? pid ,
" unclean shutdown detected, active waiting for client to be disconnected "
) ;
task ::block_on ( async {
let ( finished_sender , finished_receiver ) = oneshot ::channel ( ) ;
a2s_disconnect_s
. send ( ( self . remote_pid , finished_sender ) )
. await
. expect ( " something is wrong in internal scheduler coding " ) ;
match finished_receiver . await {
Ok ( Err ( e ) ) = > error! (
? pid ,
? e ,
" Error while dropping the participant, couldn't send all outgoing \
messages , dropping remaining "
) ,
Err ( e ) = > warn! (
? e ,
" //TODO i dont know why the finish doesnt work, i normally would \
expect to have sended a return message from the participant .. . \
ignoring to not caue a panic for now , please fix me "
) ,
_ = > ( ) ,
} ;
} ) ;
} ,
}
debug! ( ? pid , " network dropped " ) ;
2020-03-10 00:07:36 +00:00
}
}
impl Drop for Stream {
fn drop ( & mut self ) {
2020-04-08 14:26:42 +00:00
// a send if closed is unecessary but doesnt hurt, we must not crash here
2020-03-22 13:47:21 +00:00
if ! self . closed . load ( Ordering ::Relaxed ) {
2020-04-08 14:26:42 +00:00
let sid = self . sid ;
let pid = self . pid ;
debug! ( ? pid , ? sid , " shutting down Stream " ) ;
2020-05-15 12:29:17 +00:00
if task ::block_on ( self . a2b_close_stream_s . take ( ) . unwrap ( ) . send ( self . sid ) ) . is_err ( ) {
2020-04-08 14:26:42 +00:00
warn! (
" Other side got already dropped, probably due to timing, other side will \
handle this gracefully "
) ;
} ;
2020-05-15 12:29:17 +00:00
} else {
let sid = self . sid ;
let pid = self . pid ;
debug! ( ? pid , ? sid , " not needed " ) ;
2020-03-22 13:47:21 +00:00
}
2020-03-10 00:07:36 +00:00
}
2020-02-10 17:25:47 +00:00
}
2020-04-08 14:26:42 +00:00
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 ,
)
}
}
2020-07-03 19:55:00 +00:00
impl < T > From < crossbeam_channel ::SendError < T > > for StreamError {
fn from ( _err : crossbeam_channel ::SendError < T > ) -> Self { StreamError ::StreamClosed }
2020-04-08 14:26:42 +00:00
}
2020-07-03 19:55:00 +00:00
impl < T > From < crossbeam_channel ::SendError < T > > for ParticipantError {
fn from ( _err : crossbeam_channel ::SendError < T > ) -> Self { ParticipantError ::ParticipantClosed }
2020-04-08 14:26:42 +00:00
}
2020-07-03 19:55:00 +00:00
impl < T > From < crossbeam_channel ::SendError < T > > for NetworkError {
fn from ( _err : crossbeam_channel ::SendError < T > ) -> Self { NetworkError ::NetworkClosed }
2020-04-08 14:26:42 +00:00
}
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 }
}
2020-05-26 13:06:03 +00:00
2020-06-08 09:47:39 +00:00
impl From < Box < bincode ::ErrorKind > > for StreamError {
fn from ( err : Box < bincode ::ErrorKind > ) -> Self { StreamError ::DeserializeError ( err ) }
}
2020-05-26 13:06:03 +00:00
impl core ::fmt ::Display for StreamError {
2020-06-08 09:47:39 +00:00
fn fmt ( & self , f : & mut core ::fmt ::Formatter < '_ > ) -> core ::fmt ::Result {
2020-05-26 13:06:03 +00:00
match self {
StreamError ::StreamClosed = > write! ( f , " stream closed " ) ,
2020-06-08 09:47:39 +00:00
StreamError ::DeserializeError ( err ) = > {
write! ( f , " deserialize error on message: {} " , err )
} ,
2020-05-26 13:06:03 +00:00
}
}
}
impl core ::fmt ::Display for ParticipantError {
2020-06-08 09:47:39 +00:00
fn fmt ( & self , f : & mut core ::fmt ::Formatter < '_ > ) -> core ::fmt ::Result {
2020-05-26 13:06:03 +00:00
match self {
ParticipantError ::ParticipantClosed = > write! ( f , " participant closed " ) ,
}
}
}
impl core ::fmt ::Display for NetworkError {
2020-06-08 09:47:39 +00:00
fn fmt ( & self , f : & mut core ::fmt ::Formatter < '_ > ) -> core ::fmt ::Result {
2020-05-26 13:06:03 +00:00
match self {
NetworkError ::NetworkClosed = > write! ( f , " network closed " ) ,
NetworkError ::ListenFailed ( _ ) = > write! ( f , " listening failed " ) ,
2020-07-04 10:17:33 +00:00
NetworkError ::ConnectFailed ( _ ) = > write! ( f , " connecting failed " ) ,
NetworkError ::GracefulDisconnectFailed ( _ ) = > write! ( f , " graceful disconnect failed " ) ,
2020-05-26 13:06:03 +00:00
}
}
}
2020-06-08 09:47:39 +00:00
/// implementing PartialEq as it's super convenient in tests
impl core ::cmp ::PartialEq for StreamError {
fn eq ( & self , other : & Self ) -> bool {
match self {
StreamError ::StreamClosed = > match other {
StreamError ::StreamClosed = > true ,
StreamError ::DeserializeError ( _ ) = > false ,
} ,
StreamError ::DeserializeError ( err ) = > match other {
StreamError ::StreamClosed = > false ,
StreamError ::DeserializeError ( other_err ) = > partial_eq_bincode ( err , other_err ) ,
} ,
}
}
}
2020-05-26 13:06:03 +00:00
impl std ::error ::Error for StreamError { }
impl std ::error ::Error for ParticipantError { }
impl std ::error ::Error for NetworkError { }