mill_io/lib.rs
1//! # Mill-IO
2//! A lightweight, production-ready event loop library for Rust that provides efficient non-blocking I/O management
3//! without relying on heavyweight async runtimes like Tokio.
4//! Mill-IO is a modular, reactor-based event loop built on top of [`mio`], offering cross-platform polling,
5//! configurable thread pool integration, and object pooling for high-performance applications that need
6//! fine-grained control over their I/O operations.
7//! ## Core Philosophy
8//! Mill-IO was designed for applications that require:
9//! - **Predictable performance** with minimal runtime overhead
10//! - **Runtime-agnostic architecture** that doesn't force async/await patterns
11//! - **Direct control** over concurrency and resource management
12//! - **Minimal dependencies** for reduced attack surface and faster builds
13//! ## Features
14//! - **Runtime-agnostic**: No dependency on Tokio or other async runtimes
15//! - **Cross-platform**: Leverages mio's polling abstraction (epoll, kqueue, IOCP)
16//! - **Thread pool integration**: Configurable worker threads for handling I/O events
17//! - **Object pooling**: Reduces allocation overhead for frequent operations
18//! - **Clean API**: Simple registration and handler interface
19//! - **Thread-safe**: Lock-free operations in hot paths
20//! ## Architecture Overview
21//! ```text
22//! ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
23//! │ EventLoop │───▶│ Reactor │───▶│ PollHandle │
24//! └─────────────┘ └──────────────┘ └─────────────┘
25//! │
26//! ▼
27//! ┌──────────────┐ ┌─────────────┐
28//! │ ThreadPool │───▶│ Workers │
29//! └──────────────┘ └─────────────┘
30//! ```
31//! ## Quick Start
32//!
33//! ```rust,no_run
34//! use mill_io::{EventLoop, EventHandler};
35//! use mio::{net::TcpListener, Interest, Token, event::Event};
36//! use std::net::SocketAddr;
37//!
38//! struct EchoHandler;
39//!
40//! impl EventHandler for EchoHandler {
41//! fn handle_event(&self, event: &Event) {
42//! println!("Received event: {:?}", event);
43//! // Handle incoming connections and data
44//! }
45//! }
46//!
47//! fn main() -> Result<(), Box<dyn std::error::Error>> {
48//! // Create event loop with default configuration
49//! let event_loop = EventLoop::default();
50//!
51//! // Bind to localhost
52//! let addr: SocketAddr = "127.0.0.1:8080".parse()?;
53//! let mut listener = TcpListener::bind(addr)?;
54//!
55//! // Register the listener with a handler
56//! event_loop.register(
57//! &mut listener,
58//! Token(1),
59//! Interest::READABLE,
60//! EchoHandler
61//! )?;
62//!
63//! println!("Server listening on 127.0.0.1:8080");
64//!
65//! // Start the event loop (blocks until stopped)
66//! event_loop.run()?;
67//!
68//! Ok(())
69//! }
70//! ```
71//!
72//! ```rust,no_run
73//! use mill_io::EventLoop;
74//!
75//! fn main() -> Result<(), Box<dyn std::error::Error>> {
76//! let event_loop = EventLoop::new(
77//! 8, // 8 worker threads
78//! 1024, // Handle up to 1024 events per poll
79//! 100 // 100ms poll timeout
80//! )?;
81//! Ok(())
82//! }
83//! ```
84//!
85//! - [`EventLoop`]: Main entry point for registering I/O sources and running the event loop
86//! - [`EventHandler`]: Trait for implementing custom event handling logic
87//! - [`reactor`]: Core reactor implementation managing the event loop lifecycle
88//! - [`thread_pool`]: Configurable thread pool for distributing work
89//! - [`poll`]: Cross-platform polling abstraction and handler registry
90//! - [`error`]: Error types and result handling
91//!
92//! For comprehensive examples and architectural details, see the [README](../README.md)
93//! and [Architecture Guide](../docs/Arch.md).
94
95#![cfg_attr(feature = "unstable-mpmc", feature(mpmc_channel))]
96
97use mio::{Interest, Token};
98pub mod error;
99pub mod handler;
100#[cfg(feature = "net")]
101pub mod net;
102pub mod object_pool;
103pub mod poll;
104pub mod reactor;
105pub mod thread_pool;
106
107pub use handler::EventHandler;
108pub use mio::event::Event;
109pub use object_pool::{ObjectPool, PooledObject};
110pub use thread_pool::{ComputePoolMetrics, TaskPriority};
111
112use crate::error::Result;
113
114/// A convenient prelude module that re-exports commonly used types and traits.
115///
116/// This module provides a convenient way to import the most commonly used items from mill-io:
117///
118/// ```rust
119/// use mill_io::prelude::*;
120/// ```
121///
122/// This brings into scope:
123/// - [`EventHandler`] - Trait for implementing event handling logic
124/// - [`ObjectPool`] and [`PooledObject`] - Object pooling utilities
125/// - [`reactor::Reactor`] - Core reactor implementation (advanced usage)
126/// - [`thread_pool::ThreadPool`] - Thread pool implementation (advanced usage)
127pub mod prelude {
128 pub use crate::handler::EventHandler;
129 pub use crate::object_pool::{ObjectPool, PooledObject};
130 pub use crate::reactor::{self, Reactor};
131 pub use crate::thread_pool::{self, ComputePoolMetrics, TaskPriority, ThreadPool};
132}
133
134/// The main event loop structure for registering I/O sources and handling events.
135///
136/// `EventLoop` is the primary interface for Mill-IO, providing a simple API for:
137/// - Registering I/O sources (sockets, files, etc.) with event handlers
138/// - Starting and stopping the event loop
139/// - Managing the underlying reactor and thread pool
140///
141/// The event loop uses a reactor pattern internally, where I/O events are detected
142/// by the polling mechanism and dispatched to registered handlers via a thread pool.
143///
144/// ## Example
145///
146/// Basic usage with default configuration:
147///
148/// ```rust,no_run
149/// use mill_io::{EventLoop, EventHandler};
150/// use mio::{net::TcpListener, Interest, Token, event::Event};
151/// use std::net::SocketAddr;
152///
153/// struct MyHandler;
154/// impl EventHandler for MyHandler {
155/// fn handle_event(&self, event: &Event) {
156/// println!("Event received: {:?}", event);
157/// }
158/// }
159///
160/// let event_loop = EventLoop::default();
161/// let addr: SocketAddr = "127.0.0.1:0".parse()?;
162/// let mut listener = TcpListener::bind(addr)?;
163///
164/// event_loop.register(&mut listener, Token(0), Interest::READABLE, MyHandler)?;
165/// event_loop.run()?; // Blocks until stopped
166/// # Ok::<(), Box<dyn std::error::Error>>(())
167/// ```
168///
169/// Custom configuration:
170///
171/// ```rust,no_run
172/// use mill_io::EventLoop;
173///
174/// let event_loop = EventLoop::new(
175/// 4, // 4 worker threads
176/// 512, // Buffer for 512 events per poll
177/// 50 // 50ms poll timeout
178/// )?;
179/// # Ok::<(), Box<dyn std::error::Error>>(())
180/// ```
181pub struct EventLoop {
182 reactor: reactor::Reactor,
183}
184
185impl Default for EventLoop {
186 /// Creates a new `EventLoop` with default configuration.
187 ///
188 /// The default configuration uses:
189 /// - Number of worker threads equal to available CPU cores, falling back to 4 threads if CPU detection fails ([`thread_pool::DEFAULT_POOL_CAPACITY`])
190 /// - 1024 events capacity ([`reactor::DEFAULT_EVENTS_CAPACITY`])
191 /// - 150ms poll timeout ([`reactor::DEFAULT_POLL_TIMEOUT_MS`])
192 ///
193 /// # Panics
194 ///
195 /// Panics if the reactor cannot be initialized with default settings.
196 fn default() -> Self {
197 let reactor = reactor::Reactor::default();
198 Self { reactor }
199 }
200}
201
202impl EventLoop {
203 /// Creates a new `EventLoop` with custom configuration.
204 ///
205 /// ## Arguments
206 /// * `workers` - Number of worker threads in the thread pool (recommended: num_cpus)
207 /// * `events_capacity` - Maximum number of events to poll per iteration (typical: 512-4096)
208 /// * `poll_timeout_ms` - Poll timeout in milliseconds (balance between latency and CPU usage)
209 ///
210 /// ## Errors
211 ///
212 /// Returns an error if:
213 /// - The reactor cannot be initialized
214 /// - The thread pool cannot be created
215 /// - The polling mechanism fails to initialize
216 ///
217 /// ## Example
218 ///
219 /// ```rust,no_run
220 /// use mill_io::EventLoop;
221 ///
222 /// // High-throughput configuration
223 /// let event_loop = EventLoop::new(8, 2048, 50)?;
224 ///
225 /// // Low-latency configuration
226 /// let event_loop = EventLoop::new(2, 256, 10)?;
227 /// # Ok::<(), Box<dyn std::error::Error>>(())
228 /// ```
229 pub fn new(workers: usize, events_capacity: usize, poll_timeout_ms: u64) -> Result<Self> {
230 let reactor = reactor::Reactor::new(workers, events_capacity, poll_timeout_ms)?;
231 Ok(Self { reactor })
232 }
233
234 /// Registers an I/O source with the event loop and associates it with a handler.
235 ///
236 /// This method registers an I/O source (such as a TCP listener or socket) with the event loop.
237 /// When events occur on the source, the provided handler will be invoked on a worker thread.
238 ///
239 ///
240 /// ## Arguments
241 /// * `source` - The I/O source to register (e.g., [`mio::net::TcpListener`])
242 /// * `token` - Unique token for identifying events from this source
243 /// * `interests` - I/O events to listen for ([`mio::Interest::READABLE`], [`mio::Interest::WRITABLE`])
244 /// * `handler` - Event handler that will process events from this source
245 ///
246 /// ## Errors
247 ///
248 /// Returns an error if:
249 /// - The token is already in use
250 /// - The source cannot be registered with the underlying poll mechanism
251 /// - The handler registry is full
252 ///
253 /// ## Example
254 ///
255 /// ```rust,no_run
256 /// use mill_io::{EventLoop, EventHandler};
257 /// use mio::{net::TcpListener, Interest, Token, event::Event};
258 /// use std::net::SocketAddr;
259 ///
260 /// struct ConnectionHandler;
261 /// impl EventHandler for ConnectionHandler {
262 /// fn handle_event(&self, event: &Event) {
263 /// // Handle new connections
264 /// }
265 /// }
266 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
267 /// let event_loop = EventLoop::default();
268 /// let addr: SocketAddr = "0.0.0.0:8080".parse()?;
269 /// let mut listener = TcpListener::bind(addr)?;
270 ///
271 /// event_loop.register(
272 /// &mut listener,
273 /// Token(0),
274 /// Interest::READABLE,
275 /// ConnectionHandler
276 /// )?;
277 /// Ok(())
278 /// }
279 /// ```
280 pub fn register<H, S>(
281 &self,
282 source: &mut S,
283 token: Token,
284 interests: Interest,
285 handler: H,
286 ) -> Result<()>
287 where
288 H: EventHandler + Send + Sync + 'static,
289 S: mio::event::Source + ?Sized,
290 {
291 self.reactor
292 .poll_handle
293 .register(source, token, interests, handler)
294 }
295
296 /// Deregisters an I/O source from the event loop.
297 ///
298 /// Removes the source from the polling mechanism and clears its associated handler.
299 /// After deregistration, no more events will be delivered for this source.
300 ///
301 /// ## #Arguments
302 /// * `source` - The I/O source to deregister
303 /// * `token` - Token associated with the source during registration
304 ///
305 /// ## Error
306 ///
307 /// Returns an error if:
308 /// - The source is not currently registered
309 /// - The deregistration fails at the OS level
310 /// - The token is invalid
311 ///
312 /// ## Example
313 ///
314 /// ```rust,no_run
315 /// use mill_io::{EventLoop, EventHandler};
316 /// use mio::{net::TcpListener, Interest, Token, event::Event};
317 /// use std::net::SocketAddr;
318 ///
319 /// struct Handler;
320 /// impl EventHandler for Handler {
321 /// fn handle_event(&self, _: &Event) {}
322 /// }
323 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
324 ///
325 /// let event_loop = EventLoop::default();
326 /// let addr: SocketAddr = "127.0.0.1:0".parse()?;
327 /// let mut listener = TcpListener::bind(addr)?;
328 /// let token = Token(0);
329 ///
330 /// // Register
331 /// event_loop.register(&mut listener, token, Interest::READABLE, Handler)?;
332 ///
333 /// // Later, deregister
334 /// event_loop.deregister(&mut listener, token)?;
335 /// Ok(())
336 /// }
337 /// ```
338 pub fn deregister<S>(&self, source: &mut S, token: Token) -> Result<()>
339 where
340 S: mio::event::Source + ?Sized,
341 {
342 self.reactor.poll_handle.deregister(source, token)
343 }
344
345 /// Runs the event loop, blocking the current thread and dispatching events.
346 ///
347 /// This method starts the reactor's main loop, which will:
348 /// 1. Poll for I/O events using the configured timeout
349 /// 2. Dispatch events to registered handlers via the thread pool
350 /// 3. Continue until [`stop()`](Self::stop) is called or an error occurs
351 ///
352 /// The method blocks the calling thread and will only return when the event loop
353 /// is stopped or encounters a fatal error.
354 ///
355 /// ## Errors
356 ///
357 /// Returns an error if:
358 /// - The polling mechanism fails
359 /// - The thread pool encounters a fatal error
360 /// - System resources are exhausted
361 ///
362 /// ## Example
363 ///
364 /// ```rust,no_run
365 /// use mill_io::EventLoop;
366 ///
367 /// let event_loop = EventLoop::default();
368 /// // Register some handlers first...
369 /// event_loop.run()
370 /// # ; Ok::<(), Box<dyn std::error::Error>>(())
371 /// ```
372 pub fn run(&self) -> Result<()> {
373 self.reactor.run()
374 }
375
376 /// Submits a CPU-intensive task to the compute thread pool with default (Normal) priority.
377 ///
378 /// This method allows offloading heavy computations (e.g., cryptography, image processing)
379 /// to a dedicated thread pool, preventing the I/O event loop from being blocked.
380 ///
381 /// ## Arguments
382 /// * `task` - The closure to execute
383 ///
384 /// ## Example
385 ///
386 /// ```rust,no_run
387 /// use mill_io::EventLoop;
388 ///
389 /// let event_loop = EventLoop::default();
390 ///
391 /// event_loop.spawn_compute(|| {
392 /// // Heavy computation here
393 /// let result = 2 + 2;
394 /// println!("Computed: {}", result);
395 /// });
396 /// ```
397 pub fn spawn_compute<F>(&self, task: F)
398 where
399 F: FnOnce() + Send + 'static,
400 {
401 self.reactor.spawn_compute(task, TaskPriority::Normal);
402 }
403
404 /// Submits a CPU-intensive task to the compute thread pool with a specific priority.
405 ///
406 /// ## Arguments
407 /// * `task` - The closure to execute
408 /// * `priority` - The priority of the task
409 ///
410 /// ## Example
411 ///
412 /// ```rust,no_run
413 /// use mill_io::{EventLoop, TaskPriority};
414 ///
415 /// let event_loop = EventLoop::default();
416 ///
417 /// event_loop.spawn_compute_with_priority(|| {
418 /// // Heavy computation here
419 /// }, TaskPriority::High);
420 /// ```
421 pub fn spawn_compute_with_priority<F>(&self, task: F, priority: TaskPriority)
422 where
423 F: FnOnce() + Send + 'static,
424 {
425 self.reactor.spawn_compute(task, priority);
426 }
427
428 /// Returns metrics for the compute-intensive thread pool.
429 pub fn get_compute_metrics(&self) -> std::sync::Arc<ComputePoolMetrics> {
430 self.reactor.get_compute_metrics()
431 }
432
433 /// Signals the event loop to stop gracefully.
434 ///
435 /// This method initiates a graceful shutdown of the event loop. It sends a shutdown
436 /// signal to the reactor, which will cause the main loop to exit after finishing
437 /// the current polling cycle.
438 ///
439 /// This method is non-blocking and returns immediately. The actual shutdown happens
440 /// asynchronously, and [`run()`](Self::run) will return once the shutdown is complete.
441 ///
442 /// # Thread Safety
443 ///
444 /// This method is thread-safe and can be called from any thread, making it suitable
445 /// for use in signal handlers or from other threads.
446 ///
447 /// ## Example
448 ///
449 /// ```rust,no_run
450 /// use mill_io::EventLoop;
451 /// use std::thread;
452 /// use std::sync::Arc;
453 ///
454 /// let event_loop = Arc::new(EventLoop::default());
455 /// let event_loop_clone = Arc::clone(&event_loop);
456 ///
457 /// // Start event loop in background thread
458 /// let handle = thread::spawn(move || {
459 /// // In a real application, you would handle the result properly
460 /// let _ = event_loop_clone.run();
461 /// });
462 ///
463 /// // Stop after some time
464 /// thread::sleep(std::time::Duration::from_secs(1));
465 /// event_loop.stop();
466 ///
467 /// // Wait for shutdown
468 /// let _ = handle.join();
469 /// ```
470 pub fn stop(&self) {
471 let shutdown_handler = self.reactor.get_shutdown_handle();
472 shutdown_handler.shutdown();
473 }
474}