use std::sync::Arc;
use rlmesh_grpc::env::Environment;
use rlmesh_grpc::lifecycle::{await_close_with_timeout, start_idle_shutdown};
use tokio::sync::Mutex;
use super::wire::{ScalarEnvAdapter, WireEnvAdapter};
use super::{Env, VectorEnv};
use crate::bound::BoundListener;
use crate::{BindAddress, EnvironmentError, Error, Result, ServeOptions};
pub struct EnvServer<E: Env> {
env: E,
}
impl<E: Env> EnvServer<E> {
pub fn new(env: E) -> Self {
Self { env }
}
}
impl<E: Env + 'static> EnvServer<E> {
pub async fn bind(self, addr: BindAddress) -> Result<BoundEnvServer> {
self.bind_with_options(addr, ServeOptions::default()).await
}
pub async fn bind_with_options(
self,
addr: BindAddress,
options: ServeOptions,
) -> Result<BoundEnvServer> {
let shutdown = rlmesh_grpc::lifecycle::ShutdownTrigger::new();
let activity_tx = start_idle_shutdown(options.idle_timeout, shutdown.clone());
let drain_timeout = options.drain_timeout;
let close_timeout = options.close_timeout;
let grpc_options = rlmesh_grpc::ServeOptions::from(options);
let listener = BoundListener::bind(addr).await?;
let local_addr = listener.local_addr()?;
let env = Arc::new(Mutex::new(WireEnvAdapter::new(ScalarEnvAdapter::new(
self.env,
))));
let service = rlmesh_grpc::env::env_service_from_shared(
Arc::clone(&env),
shutdown.clone(),
grpc_options,
activity_tx,
);
let (_health_reporter, health_service) =
rlmesh_grpc::health::serving_health_service().await;
let router = tonic::transport::Server::builder()
.add_service(health_service)
.add_service(service);
let env: Arc<Mutex<dyn Environment + Send + Sync>> = env;
Ok(BoundEnvServer {
listener,
router,
shutdown,
env,
local_addr,
drain_timeout,
close_timeout,
})
}
pub async fn serve(self, addr: BindAddress) -> Result<()> {
self.serve_with_options(addr, ServeOptions::default()).await
}
pub async fn serve_with_options(self, addr: BindAddress, options: ServeOptions) -> Result<()> {
self.bind_with_options(addr, options).await?.serve().await
}
}
pub struct VectorEnvServer<E: VectorEnv> {
env: E,
}
impl<E: VectorEnv> VectorEnvServer<E> {
pub fn new(env: E) -> Self {
Self { env }
}
}
impl<E: VectorEnv + 'static> VectorEnvServer<E> {
pub async fn bind(self, addr: BindAddress) -> Result<BoundEnvServer> {
self.bind_with_options(addr, ServeOptions::default()).await
}
pub async fn bind_with_options(
self,
addr: BindAddress,
options: ServeOptions,
) -> Result<BoundEnvServer> {
let shutdown = rlmesh_grpc::lifecycle::ShutdownTrigger::new();
let activity_tx = start_idle_shutdown(options.idle_timeout, shutdown.clone());
let drain_timeout = options.drain_timeout;
let close_timeout = options.close_timeout;
let grpc_options = rlmesh_grpc::ServeOptions::from(options);
let listener = BoundListener::bind(addr).await?;
let local_addr = listener.local_addr()?;
let env = Arc::new(Mutex::new(WireEnvAdapter::new(self.env)));
let service = rlmesh_grpc::env::env_service_from_shared(
Arc::clone(&env),
shutdown.clone(),
grpc_options,
activity_tx,
);
let (_health_reporter, health_service) =
rlmesh_grpc::health::serving_health_service().await;
let router = tonic::transport::Server::builder()
.add_service(health_service)
.add_service(service);
let env: Arc<Mutex<dyn Environment + Send + Sync>> = env;
Ok(BoundEnvServer {
listener,
router,
shutdown,
env,
local_addr,
drain_timeout,
close_timeout,
})
}
pub async fn serve(self, addr: BindAddress) -> Result<()> {
self.serve_with_options(addr, ServeOptions::default()).await
}
pub async fn serve_with_options(self, addr: BindAddress, options: ServeOptions) -> Result<()> {
self.bind_with_options(addr, options).await?.serve().await
}
}
pub struct BoundEnvServer {
listener: BoundListener,
router: tonic::transport::server::Router,
shutdown: rlmesh_grpc::lifecycle::ShutdownTrigger,
env: Arc<Mutex<dyn Environment + Send + Sync>>,
local_addr: BindAddress,
drain_timeout: Option<std::time::Duration>,
close_timeout: Option<std::time::Duration>,
}
impl BoundEnvServer {
pub fn local_addr(&self) -> &BindAddress {
&self.local_addr
}
pub async fn serve(self) -> Result<()> {
let serve_result = self
.listener
.serve(self.router, self.shutdown, self.drain_timeout)
.await;
let close_result = close_env(self.env, self.close_timeout).await;
crate::error::join_results(serve_result, close_result, "environment server failed")
}
}
async fn close_env(
env: Arc<Mutex<dyn Environment + Send + Sync>>,
close_timeout: Option<std::time::Duration>,
) -> Result<()> {
let close = async {
env.lock()
.await
.close()
.await
.map(|_| ())
.map_err(|err| Error::Environment(EnvironmentError::from(err)))
};
await_close_with_timeout(close, close_timeout)
.await
.map_err(Error::Timeout)?
}