use std::fs;
use std::io;
use std::path::Path;
use crate::error::{Result, SaferRingError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Backend {
IoUring,
Epoll,
Stub,
}
impl Backend {
pub fn description(&self) -> &'static str {
match self {
Backend::IoUring => "High-performance io_uring backend",
Backend::Epoll => "Traditional epoll-based backend",
Backend::Stub => "Stub implementation for non-Linux platforms",
}
}
pub fn supports_advanced_features(&self) -> bool {
matches!(self, Backend::IoUring)
}
pub fn performance_multiplier(&self) -> f32 {
match self {
Backend::IoUring => 3.0, Backend::Epoll => 1.0, Backend::Stub => 0.1, }
}
}
#[derive(Debug)]
pub struct Runtime {
backend: Backend,
environment: EnvironmentInfo,
}
impl Runtime {
pub fn auto_detect() -> Result<Self> {
let environment = EnvironmentInfo::detect();
let backend = Self::select_best_backend(&environment)?;
if environment.is_cloud_environment() && backend != Backend::IoUring {
eprintln!("WARNING: io_uring not available in cloud environment");
eprintln!("Performance may be degraded. See documentation for configuration tips:");
eprintln!("- Docker: Add --cap-add SYS_ADMIN or use --privileged");
eprintln!("- Kubernetes: Set privileged: true or configure seccomp/apparmor");
eprintln!("- Cloud Run/Lambda: Consider using Cloud Functions with custom runtimes");
}
Ok(Self {
backend,
environment,
})
}
pub fn with_backend(backend: Backend) -> Result<Self> {
let environment = EnvironmentInfo::detect();
match backend {
Backend::IoUring => {
if !Self::is_io_uring_available() {
return Err(SaferRingError::Io(io::Error::new(
io::ErrorKind::Unsupported,
"io_uring backend requested but not available on this system",
)));
}
}
Backend::Epoll => {
#[cfg(not(target_os = "linux"))]
{
return Err(SaferRingError::Io(io::Error::new(
io::ErrorKind::Unsupported,
"epoll backend only available on Linux",
)));
}
}
Backend::Stub => {
}
}
Ok(Self {
backend,
environment,
})
}
pub fn backend(&self) -> Backend {
self.backend
}
pub fn environment(&self) -> &EnvironmentInfo {
&self.environment
}
pub fn is_cloud_environment(&self) -> bool {
self.environment.is_cloud_environment()
}
pub fn performance_guidance(&self) -> Vec<&'static str> {
let mut guidance = Vec::new();
match self.backend {
Backend::IoUring => {
guidance.push("✓ Using high-performance io_uring backend");
if self.environment.container_runtime.is_some() {
guidance
.push("Consider tuning container security settings for better performance");
}
}
Backend::Epoll => {
guidance.push("âš Using epoll fallback - performance may be limited");
if self.is_cloud_environment() {
guidance
.push("Check cloud platform documentation for enabling io_uring support");
}
}
Backend::Stub => {
guidance.push("âš Using stub implementation - limited functionality");
guidance.push("Consider running on Linux for better performance");
}
}
if let Some(container) = &self.environment.container_runtime {
match container.as_str() {
"docker" => {
guidance.push("Docker detected: Add --cap-add SYS_ADMIN for io_uring support");
}
"containerd" | "cri-o" => {
guidance.push(
"Kubernetes detected: Configure privileged pods or custom seccomp profiles",
);
}
_ => {}
}
}
guidance
}
fn select_best_backend(_environment: &EnvironmentInfo) -> Result<Backend> {
#[cfg(not(target_os = "linux"))]
{
Ok(Backend::Stub)
}
#[cfg(target_os = "linux")]
{
if Self::is_io_uring_available() {
if _environment.is_cloud_environment() {
if let Some(restriction) = Self::check_io_uring_restrictions() {
eprintln!("io_uring restricted: {restriction}");
return Ok(Backend::Epoll);
}
}
return Ok(Backend::IoUring);
}
Ok(Backend::Epoll)
}
}
#[cfg(target_os = "linux")]
fn is_io_uring_available() -> bool {
io_uring::IoUring::new(1).is_ok()
}
#[cfg(not(target_os = "linux"))]
fn is_io_uring_available() -> bool {
false
}
#[cfg(target_os = "linux")]
#[allow(dead_code)]
fn check_io_uring_restrictions() -> Option<String> {
if let Ok(status) = fs::read_to_string("/proc/self/status") {
if status.contains("Seccomp:") && !status.contains("Seccomp:\t0") {
return Some("seccomp profile may restrict io_uring system calls".to_string());
}
}
if Path::new("/proc/self/attr/current").exists() {
if let Ok(apparmor) = fs::read_to_string("/proc/self/attr/current") {
if !apparmor.trim().is_empty() && apparmor.trim() != "unconfined" {
return Some("AppArmor profile may restrict io_uring".to_string());
}
}
}
if Path::new("/.dockerenv").exists() {
return Some("Docker environment detected - io_uring may be restricted".to_string());
}
None
}
#[cfg(not(target_os = "linux"))]
#[allow(dead_code)]
fn check_io_uring_restrictions() -> Option<String> {
None
}
}
#[derive(Debug)]
pub struct EnvironmentInfo {
pub container_runtime: Option<String>,
pub kubernetes: bool,
pub serverless: bool,
pub kernel_version: Option<String>,
pub cpu_count: usize,
}
impl EnvironmentInfo {
pub fn detect() -> Self {
Self {
container_runtime: Self::detect_container_runtime(),
kubernetes: Self::detect_kubernetes(),
serverless: Self::detect_serverless(),
kernel_version: Self::detect_kernel_version(),
cpu_count: Self::detect_cpu_count(),
}
}
pub fn is_cloud_environment(&self) -> bool {
self.container_runtime.is_some() || self.kubernetes || self.serverless
}
fn detect_container_runtime() -> Option<String> {
if Path::new("/.dockerenv").exists() {
return Some("docker".to_string());
}
if let Ok(cgroup) = fs::read_to_string("/proc/1/cgroup") {
if cgroup.contains("docker") {
return Some("docker".to_string());
}
if cgroup.contains("containerd") {
return Some("containerd".to_string());
}
if cgroup.contains("cri-o") {
return Some("cri-o".to_string());
}
}
None
}
fn detect_kubernetes() -> bool {
std::env::var("KUBERNETES_SERVICE_HOST").is_ok()
|| Path::new("/var/run/secrets/kubernetes.io").exists()
}
fn detect_serverless() -> bool {
std::env::var("AWS_LAMBDA_FUNCTION_NAME").is_ok() ||
std::env::var("FUNCTION_NAME").is_ok() ||
std::env::var("AZURE_FUNCTIONS_ENVIRONMENT").is_ok() ||
std::env::var("K_SERVICE").is_ok()
}
#[cfg(target_os = "linux")]
fn detect_kernel_version() -> Option<String> {
fs::read_to_string("/proc/version")
.ok()
.and_then(|v| v.split_whitespace().nth(2).map(|s| s.to_string()))
}
#[cfg(not(target_os = "linux"))]
fn detect_kernel_version() -> Option<String> {
None
}
fn detect_cpu_count() -> usize {
std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(1)
}
}
pub fn is_io_uring_available() -> bool {
Runtime::is_io_uring_available()
}
pub fn get_environment_info() -> EnvironmentInfo {
EnvironmentInfo::detect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backend_properties() {
assert_eq!(
Backend::IoUring.description(),
"High-performance io_uring backend"
);
assert_eq!(
Backend::Epoll.description(),
"Traditional epoll-based backend"
);
assert_eq!(
Backend::Stub.description(),
"Stub implementation for non-Linux platforms"
);
assert!(Backend::IoUring.supports_advanced_features());
assert!(!Backend::Epoll.supports_advanced_features());
assert!(!Backend::Stub.supports_advanced_features());
assert_eq!(Backend::IoUring.performance_multiplier(), 3.0);
assert_eq!(Backend::Epoll.performance_multiplier(), 1.0);
assert_eq!(Backend::Stub.performance_multiplier(), 0.1);
}
#[test]
fn test_runtime_auto_detect() {
let runtime = Runtime::auto_detect().unwrap();
match runtime.backend() {
Backend::IoUring | Backend::Epoll | Backend::Stub => {}
}
let env = runtime.environment();
assert!(env.cpu_count > 0);
}
#[test]
fn test_environment_detection() {
let env = EnvironmentInfo::detect();
assert!(env.cpu_count > 0);
let _ = env.is_cloud_environment();
}
#[test]
fn test_performance_guidance() {
let runtime = Runtime::auto_detect().unwrap();
let guidance = runtime.performance_guidance();
assert!(!guidance.is_empty());
for guide in guidance {
assert!(!guide.is_empty());
}
}
#[cfg(target_os = "linux")]
#[test]
fn test_stub_backend_on_non_linux_request() {
let runtime = Runtime::with_backend(Backend::Stub).unwrap();
assert_eq!(runtime.backend(), Backend::Stub);
}
#[cfg(not(target_os = "linux"))]
#[test]
fn test_epoll_backend_fails_on_non_linux() {
let result = Runtime::with_backend(Backend::Epoll);
assert!(result.is_err());
}
#[test]
fn test_is_io_uring_available() {
let _ = is_io_uring_available();
}
#[test]
fn test_get_environment_info() {
let env = get_environment_info();
assert!(env.cpu_count > 0);
}
}