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