feat: enable dispatch event using single thread (#3828)

* refactor: lib dispatch

* chore: type def

* chore: type def

* fix: local set spawn

* chore: replace tokio spawn

* chore: update log

* chore: boxed event

* chore: tauri lock
This commit is contained in:
Nathan.fooo
2023-10-30 12:35:06 +08:00
committed by GitHub
parent 7f4e7e6aa0
commit e08a1a6974
67 changed files with 822 additions and 554 deletions

View File

@ -22,17 +22,18 @@ serde_json = {version = "1.0", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_repr = { version = "0.1", optional = true }
validator = "0.16.1"
tracing = { version = "0.1"}
#optional crate
bincode = { version = "1.3", optional = true}
protobuf = {version = "2.28.0", optional = true}
tracing = { version = "0.1"}
[dev-dependencies]
tokio = { version = "1.26", features = ["full"] }
futures-util = "0.3.26"
[features]
default = ["use_protobuf"]
default = ["use_protobuf", ]
use_serde = ["bincode", "serde_json", "serde", "serde_repr"]
use_protobuf= ["protobuf"]
single_thread = []

View File

@ -1,6 +1,7 @@
use crate::errors::{DispatchError, InternalError};
use bytes::Bytes;
use crate::errors::{DispatchError, InternalError};
// To bytes
pub trait ToBytes {
fn into_bytes(self) -> Result<Bytes, DispatchError>;
@ -26,21 +27,6 @@ where
}
}
// #[cfg(feature = "use_serde")]
// impl<T> ToBytes for T
// where
// T: serde::Serialize,
// {
// fn into_bytes(self) -> Result<Bytes, DispatchError> {
// match serde_json::to_string(&self.0) {
// Ok(s) => Ok(Bytes::from(s)),
// Err(e) => Err(InternalError::SerializeToBytes(format!("{:?}", e)).into()),
// }
// }
// }
// From bytes
pub trait AFPluginFromBytes: Sized {
fn parse_from_bytes(bytes: Bytes) -> Result<Self, DispatchError>;
}

View File

@ -1,3 +1,13 @@
use std::any::Any;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{future::Future, sync::Arc};
use derivative::*;
use pin_project::pin_project;
use tracing::event;
use crate::module::AFPluginStateMap;
use crate::runtime::AFPluginRuntime;
use crate::{
errors::{DispatchError, Error, InternalError},
@ -5,20 +15,76 @@ use crate::{
response::AFPluginEventResponse,
service::{AFPluginServiceFactory, Service},
};
use derivative::*;
use futures_core::future::BoxFuture;
use futures_util::task::Context;
use pin_project::pin_project;
use std::{future::Future, sync::Arc};
use tokio::macros::support::{Pin, Poll};
#[cfg(feature = "single_thread")]
pub trait AFConcurrent {}
#[cfg(feature = "single_thread")]
impl<T> AFConcurrent for T where T: ?Sized {}
#[cfg(not(feature = "single_thread"))]
pub trait AFConcurrent: Send + Sync {}
#[cfg(not(feature = "single_thread"))]
impl<T> AFConcurrent for T where T: Send + Sync {}
#[cfg(feature = "single_thread")]
pub type AFBoxFuture<'a, T> = futures_core::future::LocalBoxFuture<'a, T>;
#[cfg(not(feature = "single_thread"))]
pub type AFBoxFuture<'a, T> = futures_core::future::BoxFuture<'a, T>;
pub type AFStateMap = std::sync::Arc<AFPluginStateMap>;
#[cfg(feature = "single_thread")]
pub(crate) fn downcast_owned<T: 'static>(boxed: AFBox) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed)
}
#[cfg(not(feature = "single_thread"))]
pub(crate) fn downcast_owned<T: 'static + Send + Sync>(boxed: AFBox) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed)
}
#[cfg(feature = "single_thread")]
pub(crate) type AFBox = Box<dyn Any>;
#[cfg(not(feature = "single_thread"))]
pub(crate) type AFBox = Box<dyn Any + Send + Sync>;
#[cfg(feature = "single_thread")]
pub type BoxFutureCallback =
Box<dyn FnOnce(AFPluginEventResponse) -> AFBoxFuture<'static, ()> + 'static>;
#[cfg(not(feature = "single_thread"))]
pub type BoxFutureCallback =
Box<dyn FnOnce(AFPluginEventResponse) -> AFBoxFuture<'static, ()> + Send + Sync + 'static>;
#[cfg(feature = "single_thread")]
pub fn af_spawn<T>(future: T) -> tokio::task::JoinHandle<T::Output>
where
T: Future + Send + 'static,
T::Output: Send + 'static,
{
tokio::spawn(future)
}
#[cfg(not(feature = "single_thread"))]
pub fn af_spawn<T>(future: T) -> tokio::task::JoinHandle<T::Output>
where
T: Future + Send + 'static,
T::Output: Send + 'static,
{
tokio::spawn(future)
}
pub struct AFPluginDispatcher {
plugins: AFPluginMap,
runtime: AFPluginRuntime,
runtime: Arc<AFPluginRuntime>,
}
impl AFPluginDispatcher {
pub fn construct<F>(runtime: AFPluginRuntime, module_factory: F) -> AFPluginDispatcher
pub fn construct<F>(runtime: Arc<AFPluginRuntime>, module_factory: F) -> AFPluginDispatcher
where
F: FnOnce() -> Vec<AFPlugin>,
{
@ -30,24 +96,24 @@ impl AFPluginDispatcher {
}
}
pub fn async_send<Req>(
pub async fn async_send<Req>(
dispatch: Arc<AFPluginDispatcher>,
request: Req,
) -> DispatchFuture<AFPluginEventResponse>
) -> AFPluginEventResponse
where
Req: std::convert::Into<AFPluginRequest>,
Req: Into<AFPluginRequest>,
{
AFPluginDispatcher::async_send_with_callback(dispatch, request, |_| Box::pin(async {}))
AFPluginDispatcher::async_send_with_callback(dispatch, request, |_| Box::pin(async {})).await
}
pub fn async_send_with_callback<Req, Callback>(
pub async fn async_send_with_callback<Req, Callback>(
dispatch: Arc<AFPluginDispatcher>,
request: Req,
callback: Callback,
) -> DispatchFuture<AFPluginEventResponse>
) -> AFPluginEventResponse
where
Req: std::convert::Into<AFPluginRequest>,
Callback: FnOnce(AFPluginEventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync,
Req: Into<AFPluginRequest>,
Callback: FnOnce(AFPluginEventResponse) -> AFBoxFuture<'static, ()> + AFConcurrent + 'static,
{
let request: AFPluginRequest = request.into();
let plugins = dispatch.plugins.clone();
@ -57,7 +123,70 @@ impl AFPluginDispatcher {
request,
callback: Some(Box::new(callback)),
};
let join_handle = dispatch.runtime.spawn(async move {
// Spawns a future onto the runtime.
//
// This spawns the given future onto the runtime's executor, usually a
// thread pool. The thread pool is then responsible for polling the future
// until it completes.
//
// The provided future will start running in the background immediately
// when `spawn` is called, even if you don't await the returned
// `JoinHandle`.
let handle = dispatch.runtime.spawn(async move {
service.call(service_ctx).await.unwrap_or_else(|e| {
tracing::error!("Dispatch runtime error: {:?}", e);
InternalError::Other(format!("{:?}", e)).as_response()
})
});
let result = dispatch.runtime.run_until(handle).await;
result.unwrap_or_else(|e| {
let msg = format!("EVENT_DISPATCH join error: {:?}", e);
tracing::error!("{}", msg);
let error = InternalError::JoinError(msg);
error.as_response()
})
}
pub fn box_async_send<Req>(
dispatch: Arc<AFPluginDispatcher>,
request: Req,
) -> DispatchFuture<AFPluginEventResponse>
where
Req: Into<AFPluginRequest> + 'static,
{
AFPluginDispatcher::boxed_async_send_with_callback(dispatch, request, |_| Box::pin(async {}))
}
pub fn boxed_async_send_with_callback<Req, Callback>(
dispatch: Arc<AFPluginDispatcher>,
request: Req,
callback: Callback,
) -> DispatchFuture<AFPluginEventResponse>
where
Req: Into<AFPluginRequest> + 'static,
Callback: FnOnce(AFPluginEventResponse) -> AFBoxFuture<'static, ()> + AFConcurrent + 'static,
{
let request: AFPluginRequest = request.into();
let plugins = dispatch.plugins.clone();
let service = Box::new(DispatchService { plugins });
tracing::trace!("Async event: {:?}", &request.event);
let service_ctx = DispatchContext {
request,
callback: Some(Box::new(callback)),
};
// Spawns a future onto the runtime.
//
// This spawns the given future onto the runtime's executor, usually a
// thread pool. The thread pool is then responsible for polling the future
// until it completes.
//
// The provided future will start running in the background immediately
// when `spawn` is called, even if you don't await the returned
// `JoinHandle`.
let handle = dispatch.runtime.spawn(async move {
service.call(service_ctx).await.unwrap_or_else(|e| {
tracing::error!("Dispatch runtime error: {:?}", e);
InternalError::Other(format!("{:?}", e)).as_response()
@ -66,7 +195,8 @@ impl AFPluginDispatcher {
DispatchFuture {
fut: Box::pin(async move {
join_handle.await.unwrap_or_else(|e| {
let result = dispatch.runtime.run_until(handle).await;
result.unwrap_or_else(|e| {
let msg = format!("EVENT_DISPATCH join error: {:?}", e);
tracing::error!("{}", msg);
let error = InternalError::JoinError(msg);
@ -76,44 +206,56 @@ impl AFPluginDispatcher {
}
}
#[cfg(not(feature = "single_thread"))]
pub fn sync_send(
dispatch: Arc<AFPluginDispatcher>,
request: AFPluginRequest,
) -> AFPluginEventResponse {
futures::executor::block_on(async {
AFPluginDispatcher::async_send_with_callback(dispatch, request, |_| Box::pin(async {})).await
})
futures::executor::block_on(AFPluginDispatcher::async_send_with_callback(
dispatch,
request,
|_| Box::pin(async {}),
))
}
pub fn spawn<F>(&self, f: F)
#[cfg(feature = "single_thread")]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> tokio::task::JoinHandle<F::Output>
where
F: Future<Output = ()> + Send + 'static,
F: Future + 'static,
{
self.runtime.spawn(f);
self.runtime.spawn(future)
}
#[cfg(not(feature = "single_thread"))]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> tokio::task::JoinHandle<F::Output>
where
F: Future + Send + 'static,
<F as Future>::Output: Send + 'static,
{
self.runtime.spawn(future)
}
#[cfg(feature = "single_thread")]
pub async fn run_until<F>(&self, future: F) -> F::Output
where
F: Future + 'static,
{
let handle = self.runtime.spawn(future);
self.runtime.run_until(handle).await.unwrap()
}
#[cfg(not(feature = "single_thread"))]
pub async fn run_until<'a, F>(&self, future: F) -> F::Output
where
F: Future + Send + 'a,
<F as Future>::Output: Send + 'a,
{
self.runtime.run_until(future).await
}
}
#[pin_project]
pub struct DispatchFuture<T: Send + Sync> {
#[pin]
pub fut: Pin<Box<dyn Future<Output = T> + Sync + Send>>,
}
impl<T> Future for DispatchFuture<T>
where
T: Send + Sync,
{
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
Poll::Ready(futures_core::ready!(this.fut.poll(cx)))
}
}
pub type BoxFutureCallback =
Box<dyn FnOnce(AFPluginEventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync>;
#[derive(Derivative)]
#[derivative(Debug)]
pub struct DispatchContext {
@ -136,36 +278,37 @@ pub(crate) struct DispatchService {
impl Service<DispatchContext> for DispatchService {
type Response = AFPluginEventResponse;
type Error = DispatchError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type Future = AFBoxFuture<'static, Result<Self::Response, Self::Error>>;
#[cfg_attr(
feature = "use_tracing",
tracing::instrument(name = "DispatchService", level = "debug", skip(self, ctx))
)]
#[tracing::instrument(name = "DispatchService", level = "debug", skip(self, ctx))]
fn call(&self, ctx: DispatchContext) -> Self::Future {
let module_map = self.plugins.clone();
let (request, callback) = ctx.into_parts();
Box::pin(async move {
let result = {
// print_module_map_info(&module_map);
match module_map.get(&request.event) {
Some(module) => {
tracing::trace!("Handle event: {:?} by {:?}", &request.event, module.name);
event!(
tracing::Level::TRACE,
"Handle event: {:?} by {:?}",
&request.event,
module.name
);
let fut = module.new_service(());
let service_fut = fut.await?.call(request);
service_fut.await
},
None => {
let msg = format!("Can not find the event handler. {:?}", request);
tracing::error!("{}", msg);
event!(tracing::Level::ERROR, "{}", msg);
Err(InternalError::HandleNotFound(msg).into())
},
}
};
let response = result.unwrap_or_else(|e| e.into());
tracing::trace!("Dispatch result: {:?}", response);
event!(tracing::Level::TRACE, "Dispatch result: {:?}", response);
if let Some(callback) = callback {
callback(response.clone()).await;
}
@ -190,3 +333,21 @@ fn print_plugins(plugins: &AFPluginMap) {
tracing::info!("Event: {:?} plugin : {:?}", k, v.name);
})
}
#[pin_project]
pub struct DispatchFuture<T: AFConcurrent> {
#[pin]
pub fut: Pin<Box<dyn Future<Output = T> + 'static>>,
}
impl<T> Future for DispatchFuture<T>
where
T: AFConcurrent + 'static,
{
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
Poll::Ready(futures_core::ready!(this.fut.poll(cx)))
}
}

View File

@ -1,15 +1,17 @@
use std::fmt;
use bytes::Bytes;
use dyn_clone::DynClone;
use tokio::{sync::mpsc::error::SendError, task::JoinError};
use crate::prelude::AFConcurrent;
use crate::{
byte_trait::AFPluginFromBytes,
request::AFPluginEventRequest,
response::{AFPluginEventResponse, ResponseBuilder},
};
use bytes::Bytes;
use dyn_clone::DynClone;
use std::fmt;
use tokio::{sync::mpsc::error::SendError, task::JoinError};
pub trait Error: fmt::Debug + DynClone + Send + Sync {
pub trait Error: fmt::Debug + DynClone + AFConcurrent {
fn as_response(&self) -> AFPluginEventResponse;
}

View File

@ -1,10 +1,9 @@
use std::{
any::{Any, TypeId},
collections::HashMap,
};
use std::{any::TypeId, collections::HashMap};
use crate::prelude::{downcast_owned, AFBox, AFConcurrent};
#[derive(Default, Debug)]
pub struct AFPluginStateMap(HashMap<TypeId, Box<dyn Any + Sync + Send>>);
pub struct AFPluginStateMap(HashMap<TypeId, AFBox>);
impl AFPluginStateMap {
#[inline]
@ -14,7 +13,7 @@ impl AFPluginStateMap {
pub fn insert<T>(&mut self, val: T) -> Option<T>
where
T: 'static + Send + Sync,
T: 'static + AFConcurrent,
{
self
.0
@ -24,14 +23,14 @@ impl AFPluginStateMap {
pub fn remove<T>(&mut self) -> Option<T>
where
T: 'static + Send + Sync,
T: 'static + AFConcurrent,
{
self.0.remove(&TypeId::of::<T>()).and_then(downcast_owned)
}
pub fn get<T>(&self) -> Option<&T>
where
T: 'static + Send + Sync,
T: 'static,
{
self
.0
@ -41,7 +40,7 @@ impl AFPluginStateMap {
pub fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static + Send + Sync,
T: 'static + AFConcurrent,
{
self
.0
@ -51,7 +50,7 @@ impl AFPluginStateMap {
pub fn contains<T>(&self) -> bool
where
T: 'static + Send + Sync,
T: 'static + AFConcurrent,
{
self.0.contains_key(&TypeId::of::<T>())
}
@ -60,7 +59,3 @@ impl AFPluginStateMap {
self.0.extend(other.0);
}
}
fn downcast_owned<T: 'static + Send + Sync>(boxed: Box<dyn Any + Send + Sync>) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed)
}

View File

@ -1,15 +1,17 @@
use std::{any::type_name, ops::Deref, sync::Arc};
use crate::prelude::AFConcurrent;
use crate::{
errors::{DispatchError, InternalError},
request::{payload::Payload, AFPluginEventRequest, FromAFPluginRequest},
util::ready::{ready, Ready},
};
use std::{any::type_name, ops::Deref, sync::Arc};
pub struct AFPluginState<T: ?Sized + Send + Sync>(Arc<T>);
pub struct AFPluginState<T: ?Sized + AFConcurrent>(Arc<T>);
impl<T> AFPluginState<T>
where
T: Send + Sync,
T: AFConcurrent,
{
pub fn new(data: T) -> Self {
AFPluginState(Arc::new(data))
@ -22,7 +24,7 @@ where
impl<T> Deref for AFPluginState<T>
where
T: ?Sized + Send + Sync,
T: ?Sized + AFConcurrent,
{
type Target = Arc<T>;
@ -33,7 +35,7 @@ where
impl<T> Clone for AFPluginState<T>
where
T: ?Sized + Send + Sync,
T: ?Sized + AFConcurrent,
{
fn clone(&self) -> AFPluginState<T> {
AFPluginState(self.0.clone())
@ -42,7 +44,7 @@ where
impl<T> From<Arc<T>> for AFPluginState<T>
where
T: ?Sized + Send + Sync,
T: ?Sized + AFConcurrent,
{
fn from(arc: Arc<T>) -> Self {
AFPluginState(arc)
@ -51,7 +53,7 @@ where
impl<T> FromAFPluginRequest for AFPluginState<T>
where
T: ?Sized + Send + Sync + 'static,
T: ?Sized + AFConcurrent + 'static,
{
type Error = DispatchError;
type Future = Ready<Result<Self, DispatchError>>;
@ -59,7 +61,7 @@ where
#[inline]
fn from_request(req: &AFPluginEventRequest, _: &mut Payload) -> Self::Future {
if let Some(state) = req.get_state::<AFPluginState<T>>() {
ready(Ok(state.clone()))
ready(Ok(state))
} else {
let msg = format!(
"Failed to get the plugin state of type: {}",

View File

@ -1,4 +1,5 @@
#![allow(clippy::module_inception)]
pub use container::*;
pub use data::*;
pub use module::*;

View File

@ -9,15 +9,15 @@ use std::{
task::{Context, Poll},
};
use futures_core::future::BoxFuture;
use futures_core::ready;
use nanoid::nanoid;
use pin_project::pin_project;
use crate::dispatcher::AFConcurrent;
use crate::prelude::{AFBoxFuture, AFStateMap};
use crate::service::AFPluginHandler;
use crate::{
errors::{DispatchError, InternalError},
module::{container::AFPluginStateMap, AFPluginState},
request::{payload::Payload, AFPluginEventRequest, FromAFPluginRequest},
response::{AFPluginEventResponse, AFPluginResponder},
service::{
@ -58,7 +58,7 @@ pub struct AFPlugin {
pub name: String,
/// a list of `AFPluginState` that the plugin registers. The state can be read by the plugin's handler.
states: Arc<AFPluginStateMap>,
states: AFStateMap,
/// Contains a list of factories that are used to generate the services used to handle the passed-in
/// `ServiceRequest`.
@ -72,7 +72,7 @@ impl std::default::Default for AFPlugin {
fn default() -> Self {
Self {
name: "".to_owned(),
states: Arc::new(AFPluginStateMap::new()),
states: Default::default(),
event_service_factory: Arc::new(HashMap::new()),
}
}
@ -88,11 +88,10 @@ impl AFPlugin {
self
}
pub fn state<D: 'static + Send + Sync>(mut self, data: D) -> Self {
pub fn state<D: AFConcurrent + 'static>(mut self, data: D) -> Self {
Arc::get_mut(&mut self.states)
.unwrap()
.insert(AFPluginState::new(data));
.insert(crate::module::AFPluginState::new(data));
self
}
@ -100,9 +99,9 @@ impl AFPlugin {
pub fn event<E, H, T, R>(mut self, event: E, handler: H) -> Self
where
H: AFPluginHandler<T, R>,
T: FromAFPluginRequest + 'static + Send + Sync,
<T as FromAFPluginRequest>::Future: Sync + Send,
R: Future + 'static + Send + Sync,
T: FromAFPluginRequest + 'static + AFConcurrent,
<T as FromAFPluginRequest>::Future: AFConcurrent,
R: Future + AFConcurrent + 'static,
R::Output: AFPluginResponder + 'static,
E: Eq + Hash + Debug + Clone + Display,
{
@ -169,7 +168,7 @@ impl AFPluginServiceFactory<AFPluginRequest> for AFPlugin {
type Error = DispatchError;
type Service = BoxService<AFPluginRequest, Self::Response, Self::Error>;
type Context = ();
type Future = BoxFuture<'static, Result<Self::Service, Self::Error>>;
type Future = AFBoxFuture<'static, Result<Self::Service, Self::Error>>;
fn new_service(&self, _cfg: Self::Context) -> Self::Future {
let services = self.event_service_factory.clone();
@ -185,13 +184,14 @@ pub struct AFPluginService {
services: Arc<
HashMap<AFPluginEvent, BoxServiceFactory<(), ServiceRequest, ServiceResponse, DispatchError>>,
>,
states: Arc<AFPluginStateMap>,
states: AFStateMap,
}
impl Service<AFPluginRequest> for AFPluginService {
type Response = AFPluginEventResponse;
type Error = DispatchError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type Future = AFBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn call(&self, request: AFPluginRequest) -> Self::Future {
let AFPluginRequest { id, event, payload } = request;
@ -224,7 +224,7 @@ impl Service<AFPluginRequest> for AFPluginService {
#[pin_project]
pub struct AFPluginServiceFuture {
#[pin]
fut: BoxFuture<'static, Result<ServiceResponse, DispatchError>>,
fut: AFBoxFuture<'static, Result<ServiceResponse, DispatchError>>,
}
impl Future for AFPluginServiceFuture {

View File

@ -1,9 +1,7 @@
use bytes::Bytes;
use std::{fmt, fmt::Formatter};
pub enum PayloadError {}
use bytes::Bytes;
// TODO: support stream data
#[derive(Clone)]
#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
pub enum Payload {

View File

@ -1,47 +1,48 @@
use std::future::Future;
use crate::{
errors::{DispatchError, InternalError},
module::{AFPluginEvent, AFPluginStateMap},
request::payload::Payload,
util::ready::{ready, Ready},
};
use derivative::*;
use futures_core::ready;
use std::{
fmt::Debug,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use derivative::*;
use futures_core::ready;
use crate::prelude::{AFConcurrent, AFStateMap};
use crate::{
errors::{DispatchError, InternalError},
module::AFPluginEvent,
request::payload::Payload,
util::ready::{ready, Ready},
};
#[derive(Clone, Debug, Derivative)]
pub struct AFPluginEventRequest {
#[allow(dead_code)]
pub(crate) id: String,
pub(crate) event: AFPluginEvent,
#[derivative(Debug = "ignore")]
pub(crate) states: Arc<AFPluginStateMap>,
pub(crate) states: AFStateMap,
}
impl AFPluginEventRequest {
pub fn new<E>(id: String, event: E, module_data: Arc<AFPluginStateMap>) -> AFPluginEventRequest
pub fn new<E>(id: String, event: E, states: AFStateMap) -> AFPluginEventRequest
where
E: Into<AFPluginEvent>,
{
Self {
id,
event: event.into(),
states: module_data,
states,
}
}
pub fn get_state<T: 'static>(&self) -> Option<&T>
pub fn get_state<T>(&self) -> Option<T>
where
T: Send + Sync,
T: AFConcurrent + 'static + Clone,
{
if let Some(data) = self.states.get::<T>() {
return Some(data);
return Some(data.clone());
}
None

View File

@ -1,24 +1,117 @@
use std::{io, thread};
use std::fmt::{Display, Formatter};
use std::future::Future;
use std::io;
use tokio::runtime;
use tokio::runtime::Runtime;
use tokio::task::JoinHandle;
pub type AFPluginRuntime = tokio::runtime::Runtime;
pub struct AFPluginRuntime {
inner: Runtime,
#[cfg(feature = "single_thread")]
local: tokio::task::LocalSet,
}
pub fn tokio_default_runtime() -> io::Result<AFPluginRuntime> {
impl Display for AFPluginRuntime {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if cfg!(feature = "single_thread") {
write!(f, "Runtime(single_thread)")
} else {
write!(f, "Runtime(multi_thread)")
}
}
}
impl AFPluginRuntime {
pub fn new() -> io::Result<Self> {
let inner = default_tokio_runtime()?;
Ok(Self {
inner,
#[cfg(feature = "single_thread")]
local: tokio::task::LocalSet::new(),
})
}
#[cfg(feature = "single_thread")]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
{
self.local.spawn_local(future)
}
#[cfg(not(feature = "single_thread"))]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
<F as Future>::Output: Send + 'static,
{
self.inner.spawn(future)
}
#[cfg(feature = "single_thread")]
pub async fn run_until<F>(&self, future: F) -> F::Output
where
F: Future,
{
self.local.run_until(future).await
}
#[cfg(not(feature = "single_thread"))]
pub async fn run_until<F>(&self, future: F) -> F::Output
where
F: Future,
{
future.await
}
#[cfg(feature = "single_thread")]
#[track_caller]
pub fn block_on<F>(&self, f: F) -> F::Output
where
F: Future,
{
self.local.block_on(&self.inner, f)
}
#[cfg(not(feature = "single_thread"))]
#[track_caller]
pub fn block_on<F>(&self, f: F) -> F::Output
where
F: Future,
{
self.inner.block_on(f)
}
}
#[cfg(feature = "single_thread")]
pub fn default_tokio_runtime() -> io::Result<Runtime> {
runtime::Builder::new_current_thread()
.thread_name("dispatch-rt-st")
.enable_io()
.enable_time()
.build()
}
#[cfg(not(feature = "single_thread"))]
pub fn default_tokio_runtime() -> io::Result<Runtime> {
runtime::Builder::new_multi_thread()
.thread_name("dispatch-rt")
.thread_name("dispatch-rt-mt")
.enable_io()
.enable_time()
.on_thread_start(move || {
tracing::trace!(
"{:?} thread started: thread_id= {}",
thread::current(),
std::thread::current(),
thread_id::get()
);
})
.on_thread_stop(move || {
tracing::trace!(
"{:?} thread stopping: thread_id= {}",
thread::current(),
std::thread::current(),
thread_id::get(),
);
})

View File

@ -1,21 +1,33 @@
use crate::prelude::{AFBoxFuture, AFConcurrent};
use crate::service::{AFPluginServiceFactory, Service};
use futures_core::future::BoxFuture;
pub fn factory<SF, Req>(factory: SF) -> BoxServiceFactory<SF::Context, Req, SF::Response, SF::Error>
where
SF: AFPluginServiceFactory<Req> + 'static + Sync + Send,
SF: AFPluginServiceFactory<Req> + 'static + AFConcurrent,
Req: 'static,
SF::Response: 'static,
SF::Service: 'static,
SF::Future: 'static,
SF::Error: 'static + Send + Sync,
<SF as AFPluginServiceFactory<Req>>::Service: Sync + Send,
<<SF as AFPluginServiceFactory<Req>>::Service as Service<Req>>::Future: Send + Sync,
<SF as AFPluginServiceFactory<Req>>::Future: Send + Sync,
SF::Error: 'static,
<SF as AFPluginServiceFactory<Req>>::Service: AFConcurrent,
<<SF as AFPluginServiceFactory<Req>>::Service as Service<Req>>::Future: AFConcurrent,
<SF as AFPluginServiceFactory<Req>>::Future: AFConcurrent,
{
BoxServiceFactory(Box::new(FactoryWrapper(factory)))
}
#[cfg(feature = "single_thread")]
type Inner<Cfg, Req, Res, Err> = Box<
dyn AFPluginServiceFactory<
Req,
Context = Cfg,
Response = Res,
Error = Err,
Service = BoxService<Req, Res, Err>,
Future = AFBoxFuture<'static, Result<BoxService<Req, Res, Err>, Err>>,
>,
>;
#[cfg(not(feature = "single_thread"))]
type Inner<Cfg, Req, Res, Err> = Box<
dyn AFPluginServiceFactory<
Req,
@ -23,9 +35,9 @@ type Inner<Cfg, Req, Res, Err> = Box<
Response = Res,
Error = Err,
Service = BoxService<Req, Res, Err>,
Future = BoxFuture<'static, Result<BoxService<Req, Res, Err>, Err>>,
> + Sync
+ Send,
Future = AFBoxFuture<'static, Result<BoxService<Req, Res, Err>, Err>>,
> + Send
+ Sync,
>;
pub struct BoxServiceFactory<Cfg, Req, Res, Err>(Inner<Cfg, Req, Res, Err>);
@ -39,15 +51,21 @@ where
type Error = Err;
type Service = BoxService<Req, Res, Err>;
type Context = Cfg;
type Future = BoxFuture<'static, Result<Self::Service, Self::Error>>;
type Future = AFBoxFuture<'static, Result<Self::Service, Self::Error>>;
fn new_service(&self, cfg: Cfg) -> Self::Future {
self.0.new_service(cfg)
}
}
#[cfg(feature = "single_thread")]
pub type BoxService<Req, Res, Err> = Box<
dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<'static, Result<Res, Err>>>
dyn Service<Req, Response = Res, Error = Err, Future = AFBoxFuture<'static, Result<Res, Err>>>,
>;
#[cfg(not(feature = "single_thread"))]
pub type BoxService<Req, Res, Err> = Box<
dyn Service<Req, Response = Res, Error = Err, Future = AFBoxFuture<'static, Result<Res, Err>>>
+ Sync
+ Send,
>;
@ -88,11 +106,11 @@ impl<S> ServiceWrapper<S> {
impl<S, Req, Res, Err> Service<Req> for ServiceWrapper<S>
where
S: Service<Req, Response = Res, Error = Err>,
S::Future: 'static + Send + Sync,
S::Future: 'static + AFConcurrent,
{
type Response = Res;
type Error = Err;
type Future = BoxFuture<'static, Result<Res, Err>>;
type Future = AFBoxFuture<'static, Result<Res, Err>>;
fn call(&self, req: Req) -> Self::Future {
Box::pin(self.inner.call(req))
@ -108,15 +126,15 @@ where
Err: 'static,
SF: AFPluginServiceFactory<Req, Context = Cfg, Response = Res, Error = Err>,
SF::Future: 'static,
SF::Service: 'static + Send + Sync,
<<SF as AFPluginServiceFactory<Req>>::Service as Service<Req>>::Future: Send + Sync + 'static,
<SF as AFPluginServiceFactory<Req>>::Future: Send + Sync,
SF::Service: 'static + AFConcurrent,
<<SF as AFPluginServiceFactory<Req>>::Service as Service<Req>>::Future: AFConcurrent + 'static,
<SF as AFPluginServiceFactory<Req>>::Future: AFConcurrent,
{
type Response = Res;
type Error = Err;
type Service = BoxService<Req, Res, Err>;
type Context = Cfg;
type Future = BoxFuture<'static, Result<Self::Service, Self::Error>>;
type Future = AFBoxFuture<'static, Result<Self::Service, Self::Error>>;
fn new_service(&self, cfg: Cfg) -> Self::Future {
let f = self.0.new_service(cfg);

View File

@ -8,18 +8,19 @@ use std::{
use futures_core::ready;
use pin_project::pin_project;
use crate::dispatcher::AFConcurrent;
use crate::{
errors::DispatchError,
request::{payload::Payload, AFPluginEventRequest, FromAFPluginRequest},
request::{AFPluginEventRequest, FromAFPluginRequest},
response::{AFPluginEventResponse, AFPluginResponder},
service::{AFPluginServiceFactory, Service, ServiceRequest, ServiceResponse},
util::ready::*,
};
/// A closure that is run every time for the specified plugin event
pub trait AFPluginHandler<T, R>: Clone + 'static + Sync + Send
pub trait AFPluginHandler<T, R>: Clone + AFConcurrent + 'static
where
R: Future + Send + Sync,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
fn call(&self, param: T) -> R;
@ -29,7 +30,7 @@ pub struct AFPluginHandlerService<H, T, R>
where
H: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Sync + Send,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
handler: H,
@ -40,7 +41,7 @@ impl<H, T, R> AFPluginHandlerService<H, T, R>
where
H: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Sync + Send,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
pub fn new(handler: H) -> Self {
@ -55,7 +56,7 @@ impl<H, T, R> Clone for AFPluginHandlerService<H, T, R>
where
H: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Sync + Send,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
fn clone(&self) -> Self {
@ -70,7 +71,7 @@ impl<F, T, R> AFPluginServiceFactory<ServiceRequest> for AFPluginHandlerService<
where
F: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Send + Sync,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
type Response = ServiceResponse;
@ -88,7 +89,7 @@ impl<H, T, R> Service<ServiceRequest> for AFPluginHandlerService<H, T, R>
where
H: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Sync + Send,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
type Response = ServiceResponse;
@ -107,7 +108,7 @@ pub enum HandlerServiceFuture<H, T, R>
where
H: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Sync + Send,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
Extract(#[pin] T::Future, Option<AFPluginEventRequest>, H),
@ -118,7 +119,7 @@ impl<F, T, R> Future for HandlerServiceFuture<F, T, R>
where
F: AFPluginHandler<T, R>,
T: FromAFPluginRequest,
R: Future + Sync + Send,
R: Future + AFConcurrent,
R::Output: AFPluginResponder,
{
type Output = Result<ServiceResponse, DispatchError>;
@ -154,8 +155,8 @@ where
macro_rules! factory_tuple ({ $($param:ident)* } => {
impl<Func, $($param,)* Res> AFPluginHandler<($($param,)*), Res> for Func
where Func: Fn($($param),*) -> Res + Clone + 'static + Sync + Send,
Res: Future + Sync + Send,
where Func: Fn($($param),*) -> Res + Clone + 'static + AFConcurrent,
Res: Future + AFConcurrent,
Res::Output: AFPluginResponder,
{
#[allow(non_snake_case)]
@ -181,7 +182,7 @@ macro_rules! tuple_from_req ({$tuple_type:ident, $(($n:tt, $T:ident)),+} => {
type Error = DispatchError;
type Future = $tuple_type<$($T),+>;
fn from_request(req: &AFPluginEventRequest, payload: &mut Payload) -> Self::Future {
fn from_request(req: &AFPluginEventRequest, payload: &mut crate::prelude::Payload) -> Self::Future {
$tuple_type {
items: <($(Option<$T>,)+)>::default(),
futs: FromRequestFutures($($T::from_request(req, payload),)+),

View File

@ -1,7 +1,8 @@
use lib_dispatch::prelude::*;
use lib_dispatch::runtime::tokio_default_runtime;
use std::sync::Arc;
use lib_dispatch::prelude::*;
use lib_dispatch::runtime::AFPluginRuntime;
pub async fn hello() -> String {
"say hello".to_string()
}
@ -9,7 +10,7 @@ pub async fn hello() -> String {
#[tokio::test]
async fn test() {
let event = "1";
let runtime = tokio_default_runtime().unwrap();
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let dispatch = Arc::new(AFPluginDispatcher::construct(runtime, || {
vec![AFPlugin::new().event(event, hello)]
}));