1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
//! Remote async functions and closures.
//!
//! This module contains wrappers around async functions and closures to make them
//! callable from a remote endpoint.
//! Since Rust differentiates between immutable, mutable and by-value functions,
//! remote wrappers for all three kinds of functions are provided here.
//!
//! All wrappers take between zero and ten arguments, but you can use a tuple as
//! argument if you need more than that.
//! The arguments and return type of the function must be [remote sendable](crate::RemoteSend).
//!
//! Each wrapper spawns an async tasks that processes function execution requests
//! from the remote endpoint.
//!
//! # Usage
//!
//! Create a wrapper locally and send it to a remote endpoint, for example over a
//! channel from the [rch](crate::rch) module.
//! You must use the `new_n` method where `n` is the number of arguments of the function.
//! You can also send the wrapper as part of a larger object, such as a struct, tuple
//! or enum.
//! Then use the `call` method on the remote endpoint to remotely invoke the local function.
//!
//! Note that the function is executed locally.
//! Only the arguments and return value are transmitted from and to the remote endpoint.
//!
//! # Return type
//!
//! Since a remote function call can fail due to connection problems, the return type
//! of the wrapped function must always be of the [Result] type.
//! Thus your function should return a [Result] type with an error type that can
//! convert from [CallError] and thus absorb the remote calling error.
//! If you return a different type the `call` method will not be available on the wrapper,
//! but you can still use the `try_call` method, which wraps the result into a [Result] type.
//!
//! # Cancellation
//!
//! If the caller drops the future while it is executing or the connection is interrupted
//! the remote function is automatically cancelled at the next `await` point.
//!
//! # Providers
//!
//! Optionally you can use the `provided` method of each wrapper to obtain a
//! provider for each remote function wrapper.
//! This allows you to drop the wrapped function without relying upon the
//! remote endpoint for that.
//! This is especially useful when you connect to untrusted remote endpoints
//! that could try to obtain and keep a large number of remote function wrappers to
//! perform a denial of service attack by exhausting your memory.
//!
//! # Alternatives
//!
//! If you need to expose several functions remotely that operate on the same object
//! consider [remote trait calling](crate::rtc) instead.
//!
use serde::{Deserialize, Serialize};
use std::{error::Error, fmt};
use crate::{
chmux,
rch::{base, oneshot},
};
/// An error occurred during calling a remote function.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum CallError {
/// Provider was dropped or function panicked.
Dropped,
/// Receiving from a remote endpoint failed.
RemoteReceive(base::RecvError),
/// Connecting a sent channel failed.
RemoteConnect(chmux::ConnectError),
/// Listening for a connection from a received channel failed.
RemoteListen(chmux::ListenerError),
}
impl fmt::Display for CallError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Dropped => write!(f, "provider dropped or function panicked"),
Self::RemoteReceive(err) => write!(f, "receive error: {err}"),
Self::RemoteConnect(err) => write!(f, "connect error: {err}"),
Self::RemoteListen(err) => write!(f, "listen error: {err}"),
}
}
}
impl From<oneshot::RecvError> for CallError {
fn from(err: oneshot::RecvError) -> Self {
match err {
oneshot::RecvError::Closed => Self::Dropped,
oneshot::RecvError::RemoteReceive(err) => Self::RemoteReceive(err),
oneshot::RecvError::RemoteConnect(err) => Self::RemoteConnect(err),
oneshot::RecvError::RemoteListen(err) => Self::RemoteListen(err),
}
}
}
impl Error for CallError {}
/// Generate argument call stubs.
macro_rules! arg_stub {
($name:ident, $fn_type:ident, $provider_type:ident, $new:ident, $provided:ident, ( $( $self_prefix:tt )* ), $( $arg:ident : $arg_type:ident ),*) => {
impl < $( $arg_type , )* R, Codec> $name < ($($arg_type ,)*), R, Codec>
where
$( $arg_type : RemoteSend ,)*
R: RemoteSend,
Codec: codec::Codec,
{
/// Create a new remote function.
#[allow(unused_mut)]
pub fn $new <F, Fut>(mut fun: F) -> Self
where
F: $fn_type ($($arg_type),*) -> Fut + Send + Sync + 'static,
Fut: Future<Output = R> + Send,
{
Self::new_int(move |( $($arg ,)* )| fun($($arg),*))
}
/// Create a new remote function and return it with its provider.
///
/// See the [module-level documentation](super) for details.
#[allow(unused_mut)]
pub fn $provided <F, Fut>(mut fun: F) -> (Self, $provider_type)
where
F: $fn_type ($($arg_type),*) -> Fut + Send + Sync + 'static,
Fut: Future<Output = R> + Send,
{
Self::provided_int(move |( $($arg ,)* )| fun($($arg),*))
}
/// Try to call the remote function.
#[allow(clippy::too_many_arguments)]
#[inline]
pub async fn try_call( $( $self_prefix )* self, $( $arg : $arg_type ),* ) -> Result<R, CallError> {
self.try_call_int(( $($arg ,)* )).await
}
}
impl < $($arg_type ,)* RT, RE, Codec> $name < ($($arg_type ,)* ), Result<RT, RE>, Codec>
where
$( $arg_type : RemoteSend ,)*
RT: RemoteSend,
RE: RemoteSend + From<CallError>,
Codec: codec::Codec,
{
/// Call the remote function.
///
/// The [CallError] type must be convertible to the functions error type.
#[allow(clippy::too_many_arguments)]
#[inline]
pub async fn call($( $self_prefix )* self, $( $arg : $arg_type ),*) -> Result<RT, RE> {
self.call_int(( $($arg ,)* )).await
}
}
};
}
mod msg;
mod rfn_const;
mod rfn_mut;
mod rfn_once;
pub use rfn_const::{RFn, RFnProvider};
pub use rfn_mut::{RFnMut, RFnMutProvider};
pub use rfn_once::{RFnOnce, RFnOnceProvider};