feat: tcp transport
- add tcp transport - refactor examples - improve proc macro - add benchmark examples
This commit is contained in:
parent
b619f79095
commit
bc23b1a9db
13 changed files with 363 additions and 110 deletions
14
Cargo.toml
14
Cargo.toml
|
|
@ -30,6 +30,8 @@ serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
|
# [profile.release]
|
||||||
|
# debug = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "channel_sync"
|
name = "channel_sync"
|
||||||
|
|
@ -39,6 +41,18 @@ required-features = ["channel"]
|
||||||
name = "channel_async"
|
name = "channel_async"
|
||||||
required-features = ["channel"]
|
required-features = ["channel"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "channel_sync_benchmark"
|
||||||
|
required-features = ["channel"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "channel_async_benchmark"
|
||||||
|
required-features = ["channel"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "tcp"
|
name = "tcp"
|
||||||
required-features = ["serde"]
|
required-features = ["serde"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "tcp_benchmark"
|
||||||
|
required-features = ["serde"]
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ The Server implements read-write locking, there can be many reads at once, but o
|
||||||
|
|
||||||
The Client object cannot be cloned. Instead all function calls are immutable, so a reference can be shared to all.
|
The Client object cannot be cloned. Instead all function calls are immutable, so a reference can be shared to all.
|
||||||
|
|
||||||
Currently only implements local thread messaging. Serialized TCP transport is unfinished.
|
Current status:
|
||||||
|
- channel_sync : Implementation OK
|
||||||
|
- channel_async : Implementation OK, but beware of performance bottlenecks. If you are making lots of small calls, channel_sync is more fitting
|
||||||
|
- tcp : Functional, but server close handling uninmplemented yet
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```rs
|
```rs
|
||||||
|
|
|
||||||
|
|
@ -16,23 +16,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let client = TestDataClient::new(c);
|
let client = TestDataClient::new(c);
|
||||||
let clientref = &client;
|
|
||||||
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
let job1 = async {
|
let job1 = async {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(DATASIZE, client.len().await.unwrap());
|
assert_eq!(267914296, client.fib(42).await.unwrap());
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
|
||||||
assert_eq!(267914296, clientref.fib(42).await.unwrap());
|
|
||||||
println!("fib1: {}", dur_to_str(now.elapsed()));
|
println!("fib1: {}", dur_to_str(now.elapsed()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let job2 = async {
|
let job2 = async {
|
||||||
let now = std::time::Instant::now();
|
|
||||||
assert_eq!(DATASIZE, client.len().await.unwrap());
|
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(4, client.calc().await.unwrap().unwrap_or(0));
|
assert_eq!(4, client.calc().await.unwrap().unwrap_or(0));
|
||||||
println!("calc: {}", dur_to_str(now.elapsed()));
|
println!("calc: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
@ -47,7 +40,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("fib2: {}", dur_to_str(now.elapsed()));
|
println!("fib2: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
clientref.push((false, TestEnum::NoValue)).await.unwrap();
|
assert_eq!(DATASIZE, client.len().await.unwrap());
|
||||||
|
println!("len: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
client.push((false, TestEnum::NoValue)).await.unwrap();
|
||||||
println!("push: {}", dur_to_str(now.elapsed()));
|
println!("push: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
|
|
@ -56,6 +53,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
join!(job1, job2);
|
join!(job1, job2);
|
||||||
|
|
||||||
|
println!("total: {}", dur_to_str(start_time.elapsed()));
|
||||||
|
|
||||||
client.stop().await.unwrap();
|
client.stop().await.unwrap();
|
||||||
srv_thread.await.unwrap().unwrap();
|
srv_thread.await.unwrap().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
39
examples/channel_async_benchmark.rs
Normal file
39
examples/channel_async_benchmark.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
|
use rspc_dev_utilities::test_data::{
|
||||||
|
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, CALLS_PER_THREAD,
|
||||||
|
DATASIZE, THREADS,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rspc::transport;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let (c, s) = transport::channel::new_async();
|
||||||
|
let data: TestData = make_test_data(DATASIZE);
|
||||||
|
|
||||||
|
let srv_thread = tokio::spawn(async move {
|
||||||
|
let mut server = TestDataServer::from(data);
|
||||||
|
server.listen(s).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let client = TestDataClient::new(c);
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
{
|
||||||
|
let set = FuturesUnordered::new();
|
||||||
|
for _ in 0..THREADS {
|
||||||
|
set.push(async {
|
||||||
|
for _ in 0..CALLS_PER_THREAD {
|
||||||
|
client.heavy_calc().await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let _: Vec<_> = set.collect().await;
|
||||||
|
}
|
||||||
|
println!("time: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
client.stop().await.unwrap();
|
||||||
|
srv_thread.await.unwrap().unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
use rspc_dev_utilities::test_data::{
|
use rspc_dev_utilities::test_data::{
|
||||||
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, TestEnum, DATASIZE,
|
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, TestEnum, DATASIZE,
|
||||||
};
|
};
|
||||||
use tokio::join;
|
|
||||||
|
|
||||||
use rspc::transport;
|
use rspc::transport;
|
||||||
|
|
||||||
|
|
@ -16,46 +16,54 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let client = TestDataClient::new(c);
|
let client = TestDataClient::new(c);
|
||||||
let clientref = &client;
|
|
||||||
|
|
||||||
let job1 = async {
|
let start_time = std::time::Instant::now();
|
||||||
let now = std::time::Instant::now();
|
|
||||||
assert_eq!(DATASIZE, client.len().await.unwrap());
|
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(267914296, clientref.fib(42).await.unwrap());
|
assert_eq!(DATASIZE, client.len().await.unwrap());
|
||||||
println!("fib1: {}", dur_to_str(now.elapsed()));
|
println!("len: {}", dur_to_str(now.elapsed()));
|
||||||
};
|
|
||||||
|
|
||||||
let job2 = async {
|
let now = std::time::Instant::now();
|
||||||
let now = std::time::Instant::now();
|
client.push((false, TestEnum::NoValue)).await.unwrap();
|
||||||
assert_eq!(DATASIZE, client.len().await.unwrap());
|
println!("push: {}", dur_to_str(now.elapsed()));
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(4, client.calc().await.unwrap().unwrap_or(0));
|
assert_eq!(DATASIZE + 1, client.len().await.unwrap());
|
||||||
println!("calc: {}", dur_to_str(now.elapsed()));
|
println!("len: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let cdat = make_test_data(DATASIZE);
|
let now = std::time::Instant::now();
|
||||||
let now = std::time::Instant::now();
|
assert_eq!(267914296, client.fib(42).await.unwrap());
|
||||||
assert_eq!(8, client.calc_add(cdat).await.unwrap().unwrap_or(0));
|
println!("fib1: {}", dur_to_str(now.elapsed()));
|
||||||
println!("calc_add: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(267914296, client.fib(42).await.unwrap());
|
assert_eq!(267914296, client.fib(42).await.unwrap());
|
||||||
println!("fib2: {}", dur_to_str(now.elapsed()));
|
println!("fib2: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
clientref.push((false, TestEnum::NoValue)).await.unwrap();
|
assert_eq!(4, client.calc().await.unwrap().unwrap_or(0));
|
||||||
println!("push: {}", dur_to_str(now.elapsed()));
|
println!("calc: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let cdat = make_test_data(DATASIZE);
|
||||||
assert_eq!(DATASIZE + 1, client.len().await.unwrap());
|
let now = std::time::Instant::now();
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
assert_eq!(8, client.calc_add(cdat).await.unwrap().unwrap_or(0));
|
||||||
};
|
println!("calc_add: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
{
|
||||||
|
let set = FuturesUnordered::new();
|
||||||
|
for _ in 0..128 {
|
||||||
|
set.push(async {
|
||||||
|
for _ in 0..100000 {
|
||||||
|
client.calc().await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let _: Vec<_> = set.collect().await;
|
||||||
|
}
|
||||||
|
println!("benchmark: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
println!("total: {}", dur_to_str(start_time.elapsed()));
|
||||||
|
|
||||||
join!(job1, job2);
|
|
||||||
client.stop().await.unwrap();
|
client.stop().await.unwrap();
|
||||||
srv_thread.await.unwrap().unwrap();
|
srv_thread.await.unwrap().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
39
examples/channel_sync_benchmark.rs
Normal file
39
examples/channel_sync_benchmark.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
|
use rspc_dev_utilities::test_data::{
|
||||||
|
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, CALLS_PER_THREAD,
|
||||||
|
DATASIZE, THREADS,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rspc::transport;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let (c, s) = transport::channel::new_sync();
|
||||||
|
let data: TestData = make_test_data(DATASIZE);
|
||||||
|
|
||||||
|
let srv_thread = tokio::spawn(async move {
|
||||||
|
let mut server = TestDataServer::from(data);
|
||||||
|
server.listen(s).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let client = TestDataClient::new(c);
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
{
|
||||||
|
let set = FuturesUnordered::new();
|
||||||
|
for _ in 0..THREADS {
|
||||||
|
set.push(async {
|
||||||
|
for _ in 0..CALLS_PER_THREAD {
|
||||||
|
client.heavy_calc().await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let _: Vec<_> = set.collect().await;
|
||||||
|
}
|
||||||
|
println!("time: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
client.stop().await.unwrap();
|
||||||
|
srv_thread.await.unwrap().unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -1,38 +1,33 @@
|
||||||
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
use rspc_dev_utilities::test_data::{
|
use rspc_dev_utilities::test_data::{
|
||||||
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, TestEnum, DATASIZE,
|
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, TestEnum, DATASIZE,
|
||||||
};
|
};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
|
|
||||||
use rspc::transport::serde::TcpClient;
|
use rspc::transport::serde::{TcpClient, TcpServer};
|
||||||
|
|
||||||
use rspc::transport;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let data: TestData = make_test_data(DATASIZE);
|
||||||
|
let mut server = TestDataServer::from(data);
|
||||||
|
let t = TcpServer::new(&"127.0.0.1:6543").await.unwrap();
|
||||||
|
let srv_thread = tokio::spawn(async move { server.listen(t).await });
|
||||||
|
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||||
|
|
||||||
let t = TcpClient::connect("127.0.0.1:6543").await.unwrap();
|
let t = TcpClient::connect("127.0.0.1:6543").await.unwrap();
|
||||||
let client = t.spawn().await;
|
let client = t.spawn().await;
|
||||||
|
|
||||||
// todo : server
|
|
||||||
|
|
||||||
let client = TestDataClient::new(client);
|
let client = TestDataClient::new(client);
|
||||||
|
|
||||||
let clientref = &client;
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
let job1 = async {
|
let job1 = async {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(DATASIZE, client.len().await.unwrap());
|
assert_eq!(267914296, client.fib(42).await.unwrap());
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
|
||||||
assert_eq!(267914296, clientref.fib(42).await.unwrap());
|
|
||||||
println!("fib1: {}", dur_to_str(now.elapsed()));
|
println!("fib1: {}", dur_to_str(now.elapsed()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let job2 = async {
|
let job2 = async {
|
||||||
let now = std::time::Instant::now();
|
|
||||||
assert_eq!(DATASIZE, client.len().await.unwrap());
|
|
||||||
println!("len: {}", dur_to_str(now.elapsed()));
|
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
assert_eq!(4, client.calc().await.unwrap().unwrap_or(0));
|
assert_eq!(4, client.calc().await.unwrap().unwrap_or(0));
|
||||||
println!("calc: {}", dur_to_str(now.elapsed()));
|
println!("calc: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
@ -47,7 +42,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("fib2: {}", dur_to_str(now.elapsed()));
|
println!("fib2: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
clientref.push((false, TestEnum::NoValue)).await.unwrap();
|
assert_eq!(DATASIZE, client.len().await.unwrap());
|
||||||
|
println!("len: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
client.push((false, TestEnum::NoValue)).await.unwrap();
|
||||||
println!("push: {}", dur_to_str(now.elapsed()));
|
println!("push: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
|
|
@ -56,7 +55,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
join!(job1, job2);
|
join!(job1, job2);
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
{
|
||||||
|
let set = FuturesUnordered::new();
|
||||||
|
for _ in 0..128 {
|
||||||
|
set.push(async {
|
||||||
|
for _ in 0..100000 {
|
||||||
|
client.calc().await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let _: Vec<_> = set.collect().await;
|
||||||
|
}
|
||||||
|
println!("benchmark: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
println!("total: {}", dur_to_str(start_time.elapsed()));
|
||||||
|
|
||||||
client.stop().await.unwrap();
|
client.stop().await.unwrap();
|
||||||
|
|
||||||
|
// todo : server stop
|
||||||
|
srv_thread.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
examples/tcp_benchmark.rs
Normal file
40
examples/tcp_benchmark.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
|
use rspc_dev_utilities::test_data::{
|
||||||
|
dur_to_str, make_test_data, TestData, TestDataClient, TestDataServer, CALLS_PER_THREAD,
|
||||||
|
DATASIZE, THREADS,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rspc::transport::serde::{TcpClient, TcpServer};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let data: TestData = make_test_data(DATASIZE);
|
||||||
|
let mut server = TestDataServer::from(data);
|
||||||
|
let t = TcpServer::new(&"127.0.0.1:6543").await.unwrap();
|
||||||
|
let srv_thread = tokio::spawn(async move { server.listen(t).await });
|
||||||
|
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||||
|
|
||||||
|
let t = TcpClient::connect("127.0.0.1:6543").await.unwrap();
|
||||||
|
let client = t.spawn().await;
|
||||||
|
let client = TestDataClient::new(client);
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
{
|
||||||
|
let set = FuturesUnordered::new();
|
||||||
|
for _ in 0..THREADS {
|
||||||
|
set.push(async {
|
||||||
|
for _ in 0..CALLS_PER_THREAD {
|
||||||
|
client.heavy_calc().await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let _: Vec<_> = set.collect().await;
|
||||||
|
}
|
||||||
|
println!("time: {}", dur_to_str(now.elapsed()));
|
||||||
|
|
||||||
|
client.stop().await.unwrap();
|
||||||
|
srv_thread.await.unwrap().unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -316,7 +316,7 @@ pub fn service(_attr: TokenStream, mut input: TokenStream) -> TokenStream {
|
||||||
impl #server {
|
impl #server {
|
||||||
pub async fn listen<Tr>(&mut self, mut transport: Tr) -> Result<(), Tr::Error>
|
pub async fn listen<Tr>(&mut self, mut transport: Tr) -> Result<(), Tr::Error>
|
||||||
where
|
where
|
||||||
Tr: ServerTransporter<#transport_request,#transport_response> + Send
|
Tr: ::rspc::transport::ServerTransporter<#transport_request,#transport_response> + Send
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
transport.listen( |v,obj| {
|
transport.listen( |v,obj| {
|
||||||
|
|
@ -339,14 +339,14 @@ pub fn service(_attr: TokenStream, mut input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
pub struct #client<Tr>
|
pub struct #client<Tr>
|
||||||
where
|
where
|
||||||
Tr: ClientTransporter<#transport_request,#transport_response>,
|
Tr: ::rspc::transport::ClientTransporter<#transport_request,#transport_response>,
|
||||||
{
|
{
|
||||||
transporter: Tr,
|
transporter: Tr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tr> #client<Tr>
|
impl<Tr> #client<Tr>
|
||||||
where
|
where
|
||||||
Tr: ClientTransporter<#transport_request,#transport_response>
|
Tr: ::rspc::transport::ClientTransporter<#transport_request,#transport_response>
|
||||||
{
|
{
|
||||||
pub fn new(transporter: Tr) -> Self {
|
pub fn new(transporter: Tr) -> Self {
|
||||||
#client {
|
#client {
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,14 @@ pub trait ServerTransporter<T, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn async_listener<T, R, C, L, LF, S, SF, F, FR, D, E>(
|
pub async fn async_listener<T, R, C, L, LF, S, SF, F, FR, D, E>(
|
||||||
listener: &mut L,
|
mut listener: L,
|
||||||
listener_recv: LF,
|
listener_recv: LF,
|
||||||
sender: &mut S,
|
mut sender: S,
|
||||||
sender_send: SF,
|
sender_send: SF,
|
||||||
handler: F,
|
handler: &F,
|
||||||
stop_response: Option<R>,
|
stop_response: &Option<R>,
|
||||||
userdata: &D,
|
userdata: &D,
|
||||||
) -> Result<(), E>
|
) -> Result<bool, E>
|
||||||
where
|
where
|
||||||
T: Send + Sync,
|
T: Send + Sync,
|
||||||
R: Send + Sync + 'static,
|
R: Send + Sync + 'static,
|
||||||
|
|
@ -55,10 +55,11 @@ where
|
||||||
SF: Fn(&mut S, (C, R)) -> BoxFuture<Result<(), E>> + Send + Sync + 'static,
|
SF: Fn(&mut S, (C, R)) -> BoxFuture<Result<(), E>> + Send + Sync + 'static,
|
||||||
LF: Fn(&mut L) -> BoxFuture<Result<Option<(C, T)>, E>> + 'static,
|
LF: Fn(&mut L) -> BoxFuture<Result<Option<(C, T)>, E>> + 'static,
|
||||||
{
|
{
|
||||||
|
let mut stop = false;
|
||||||
let mut pending = FuturesUnordered::new();
|
let mut pending = FuturesUnordered::new();
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
rcv = listener_recv(listener) => {
|
rcv = listener_recv(&mut listener) => {
|
||||||
match rcv? {
|
match rcv? {
|
||||||
Some((id, data)) => {
|
Some((id, data)) => {
|
||||||
pending.push(
|
pending.push(
|
||||||
|
|
@ -76,12 +77,14 @@ where
|
||||||
Some(r) = pending.next() => {
|
Some(r) = pending.next() => {
|
||||||
match r {
|
match r {
|
||||||
(id,Some(r)) => {
|
(id,Some(r)) => {
|
||||||
sender_send(sender, (id,r)).await?;
|
sender_send(&mut sender, (id,r)).await?;
|
||||||
},
|
},
|
||||||
(id,None) => {
|
(id,None) => {
|
||||||
if let Some(v) = stop_response {
|
if let Some(v) = stop_response {
|
||||||
sender_send(sender, (id,v)).await?;
|
todo!() // clone issue on R
|
||||||
|
// sender_send(&mut sender, (id,v)).await?;
|
||||||
}
|
}
|
||||||
|
stop = true;
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -94,11 +97,11 @@ where
|
||||||
for it in results {
|
for it in results {
|
||||||
match it {
|
match it {
|
||||||
(id, Some(r)) => {
|
(id, Some(r)) => {
|
||||||
sender_send(sender, (id, r)).await?;
|
sender_send(&mut sender, (id, r)).await?;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(stop)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
|
use futures::stream::FuturesUnordered;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
|
@ -115,6 +115,8 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turn the TCP Client into a background process and a channel client multiplexer
|
||||||
|
#[cfg(feature = "channel")]
|
||||||
pub async fn multiplex(
|
pub async fn multiplex(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
|
|
@ -133,6 +135,8 @@ where
|
||||||
(client, Box::pin(fut))
|
(client, Box::pin(fut))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawn a background client multiplexer and give a channel client
|
||||||
|
#[cfg(feature = "channel")]
|
||||||
pub async fn spawn(self) -> ChannelClient<T, R> {
|
pub async fn spawn(self) -> ChannelClient<T, R> {
|
||||||
let (client, job) = self.multiplex().await;
|
let (client, job) = self.multiplex().await;
|
||||||
tokio::spawn(job);
|
tokio::spawn(job);
|
||||||
|
|
@ -140,14 +144,28 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, R> ClientTransporter<T, R> for TcpClient<T, R>
|
||||||
|
where
|
||||||
|
T: for<'a> Deserialize<'a> + Serialize + Send + Sync + Unpin + 'static,
|
||||||
|
R: for<'a> Deserialize<'a> + Serialize + Send + Sync + Unpin + 'static,
|
||||||
|
{
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
async fn request(&self, data: T) -> Result<R, Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, R> TcpServer<T, R>
|
impl<T, R> TcpServer<T, R>
|
||||||
where
|
where
|
||||||
T: for<'a> Deserialize<'a> + Serialize,
|
T: for<'a> Deserialize<'a> + Serialize,
|
||||||
R: for<'a> Deserialize<'a> + Serialize,
|
R: for<'a> Deserialize<'a> + Serialize,
|
||||||
{
|
{
|
||||||
pub async fn new(address: &Ipv4Addr, port: u16) -> Result<TcpServer<T, R>, Error> {
|
pub async fn new<A>(address: &A) -> Result<TcpServer<T, R>, Error>
|
||||||
let address = format!("{}:{}", address, port);
|
where
|
||||||
let listener = TcpListener::bind(&address).await.map_err(Error::IO)?;
|
A: tokio::net::ToSocketAddrs + std::fmt::Display,
|
||||||
|
{
|
||||||
|
let listener = TcpListener::bind(address).await.map_err(Error::IO)?;
|
||||||
Ok(TcpServer {
|
Ok(TcpServer {
|
||||||
listener,
|
listener,
|
||||||
ghost: Default::default(),
|
ghost: Default::default(),
|
||||||
|
|
@ -156,15 +174,14 @@ where
|
||||||
|
|
||||||
async fn accept(&mut self) -> Result<TcpStream, Error> {
|
async fn accept(&mut self) -> Result<TcpStream, Error> {
|
||||||
let (socket, address) = self.listener.accept().await.map_err(Error::IO)?;
|
let (socket, address) = self.listener.accept().await.map_err(Error::IO)?;
|
||||||
println!("connection accepted: {:?}", address);
|
|
||||||
Ok(socket)
|
Ok(socket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, R> ServerTransporter<T, R> for TcpServer<T, R>
|
impl<T, R> ServerTransporter<T, R> for TcpServer<T, R>
|
||||||
where
|
where
|
||||||
T: for<'a> Deserialize<'a> + Serialize + Send + Sync + Unpin,
|
T: for<'a> Deserialize<'a> + Serialize + Send + Sync + Unpin + 'static,
|
||||||
R: for<'a> Deserialize<'a> + Serialize + Send + Sync + Unpin,
|
R: for<'a> Deserialize<'a> + Serialize + Send + Sync + Unpin + 'static,
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
|
@ -176,34 +193,45 @@ where
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where
|
where
|
||||||
FR: Future<Output = Option<R>> + Send + 'static,
|
FR: Future<Output = Option<R>> + Send + 'static,
|
||||||
F: Fn(T, &D) -> FR + Send + Sync,
|
F: Fn(T, &D) -> FR + Send + Sync + 'static,
|
||||||
D: Send + Sync,
|
D: Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let (client, fut) = channel::new_multiplexer::<R, T>();
|
let mut connections = FuturesUnordered::new();
|
||||||
|
loop {
|
||||||
// super::async_listener(
|
tokio::select! {
|
||||||
// &mut receiver, |_self| { Box::pin(async {
|
connect = self.accept() => {
|
||||||
// _self.recv().await
|
match connect {
|
||||||
// }) },
|
Ok(stream) => {
|
||||||
// &mut sender, |_self, data| { Box::pin(async {
|
let (sender, receiver) = split::<R, T>(stream).await;
|
||||||
// _self.send(data).await
|
let handler = &handler;
|
||||||
// }) },
|
let stop_response = &stop_response;
|
||||||
// handler, stop_response, &userdata);
|
let userdata = &userdata;
|
||||||
|
connections.push(async move {
|
||||||
while let Ok(mut stream) = self.accept().await {
|
super::async_listener(
|
||||||
let (sender, receiver) = split::<R, T>(stream).await;
|
receiver,
|
||||||
// tokio::spawn(async move {
|
|_self| Box::pin(async { _self.recv().await }),
|
||||||
// super::async_listener(
|
sender,
|
||||||
// &mut receiver, |_self| { Box::pin(async {
|
|_self, data| Box::pin(async { _self.send(data).await }),
|
||||||
// _self.recv().await
|
handler,
|
||||||
// }) },
|
stop_response,
|
||||||
// &mut sender, |_self, data| { Box::pin(async {
|
userdata,
|
||||||
// _self.send(data).await
|
).await
|
||||||
// }) },
|
});
|
||||||
// handler, stop_response, &userdata)
|
},
|
||||||
// });
|
Err(e) => {
|
||||||
|
println!("WARN: connection accept error: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(s) = connections.next() => {
|
||||||
|
if let Ok(true) = s {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// poll connected threads to get them running
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
46
tests/tcp.rs
Normal file
46
tests/tcp.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
mod tests {
|
||||||
|
// use rspc::transport::{channel, ClientTransporter, ServerTransporter};
|
||||||
|
|
||||||
|
pub struct MyStruct {
|
||||||
|
my_vec: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rspc::service]
|
||||||
|
impl MyStruct {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.my_vec.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, val: String) {
|
||||||
|
self.my_vec.push(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<String> {
|
||||||
|
self.my_vec.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test() {
|
||||||
|
// let my_data = MyStruct { my_vec: Vec::new() };
|
||||||
|
// let (c, s) = channel::new_sync();
|
||||||
|
|
||||||
|
// let srv_thread = tokio::spawn(async move {
|
||||||
|
// let mut server = MyStructServer::from(my_data);
|
||||||
|
// server.listen(s).await
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let client = MyStructClient::new(c);
|
||||||
|
// assert_eq!(client.len().await.unwrap(), 0);
|
||||||
|
// client.push("Hello world!".to_string()).await.unwrap();
|
||||||
|
// assert_eq!(client.len().await.unwrap(), 1);
|
||||||
|
// assert_eq!(
|
||||||
|
// client.pop().await.unwrap(),
|
||||||
|
// Some("Hello world!".to_string())
|
||||||
|
// );
|
||||||
|
// client.stop().await.unwrap();
|
||||||
|
// srv_thread.await.unwrap().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use rspc::transport::{ClientTransporter, ServerTransporter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn dur_to_num(dur: Duration) -> (u128, &'static str) {
|
pub fn dur_to_num(dur: Duration) -> (u128, &'static str) {
|
||||||
|
|
@ -21,7 +19,9 @@ pub fn dur_to_str(dur: Duration) -> String {
|
||||||
n.to_string() + " " + s
|
n.to_string() + " " + s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DATASIZE: usize = 1000000;
|
pub const DATASIZE: usize = 10000;
|
||||||
|
pub const THREADS: usize = 8;
|
||||||
|
pub const CALLS_PER_THREAD: usize = 1000;
|
||||||
|
|
||||||
const TEST_STRINGS: [&str; 4] = ["toto", "tata", "titi", "tutu"];
|
const TEST_STRINGS: [&str; 4] = ["toto", "tata", "titi", "tutu"];
|
||||||
|
|
||||||
|
|
@ -39,6 +39,10 @@ pub struct TestData {
|
||||||
|
|
||||||
#[rspc::service]
|
#[rspc::service]
|
||||||
impl TestData {
|
impl TestData {
|
||||||
|
pub fn mem_size(&self) -> usize {
|
||||||
|
std::mem::size_of_val(&self.vec)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.vec.len()
|
self.vec.len()
|
||||||
}
|
}
|
||||||
|
|
@ -52,6 +56,16 @@ impl TestData {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn heavy_calc(&self) -> usize {
|
||||||
|
let mut res = 0;
|
||||||
|
for v in &self.vec {
|
||||||
|
if let (true, TestEnum::Num(n)) = v {
|
||||||
|
res += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn slow_fct(&self) -> Option<usize> {
|
pub async fn slow_fct(&self) -> Option<usize> {
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
self.calc()
|
self.calc()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue