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;
100pub mod object_pool;
101pub mod poll;
102pub mod reactor;
103pub mod thread_pool;
104pub use handler::EventHandler;
105pub use mio::event::Event;
106pub use object_pool::{ObjectPool, PooledObject};
107
108use crate::{
109    error::Result,
110    reactor::{DEFAULT_EVENTS_CAPACITY, DEFAULT_POLL_TIMEOUT_MS},
111    thread_pool::DEFAULT_POOL_CAPACITY,
112};
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, 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    /// - 4 worker threads ([`DEFAULT_POOL_CAPACITY`])
190    /// - 1024 events capacity ([`DEFAULT_EVENTS_CAPACITY`])
191    /// - 100ms poll timeout ([`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::new(
198            DEFAULT_POOL_CAPACITY,
199            DEFAULT_EVENTS_CAPACITY,
200            DEFAULT_POLL_TIMEOUT_MS,
201        )
202        .unwrap();
203        Self { reactor }
204    }
205}
206
207impl EventLoop {
208    /// Creates a new `EventLoop` with custom configuration.
209    ///
210    /// ## Arguments
211    /// * `workers` - Number of worker threads in the thread pool (recommended: num_cpus)
212    /// * `events_capacity` - Maximum number of events to poll per iteration (typical: 512-4096)
213    /// * `poll_timeout_ms` - Poll timeout in milliseconds (balance between latency and CPU usage)
214    ///
215    /// ## Errors
216    ///
217    /// Returns an error if:
218    /// - The reactor cannot be initialized
219    /// - The thread pool cannot be created
220    /// - The polling mechanism fails to initialize
221    ///
222    /// ## Example
223    ///
224    /// ```rust,no_run
225    /// use mill_io::EventLoop;
226    ///
227    /// // High-throughput configuration
228    /// let event_loop = EventLoop::new(8, 2048, 50)?;
229    ///
230    /// // Low-latency configuration
231    /// let event_loop = EventLoop::new(2, 256, 10)?;
232    /// # Ok::<(), Box<dyn std::error::Error>>(())
233    /// ```
234    pub fn new(workers: usize, events_capacity: usize, poll_timeout_ms: u64) -> Result<Self> {
235        let reactor = reactor::Reactor::new(workers, events_capacity, poll_timeout_ms)?;
236        Ok(Self { reactor })
237    }
238
239    /// Registers an I/O source with the event loop and associates it with a handler.
240    ///
241    /// This method registers an I/O source (such as a TCP listener or socket) with the event loop.
242    /// When events occur on the source, the provided handler will be invoked on a worker thread.
243    ///
244    ///
245    /// ## Arguments
246    /// * `source` - The I/O source to register (e.g., [`mio::net::TcpListener`])
247    /// * `token` - Unique token for identifying events from this source
248    /// * `interests` - I/O events to listen for ([`mio::Interest::READABLE`], [`mio::Interest::WRITABLE`])
249    /// * `handler` - Event handler that will process events from this source
250    ///
251    /// ## Errors
252    ///
253    /// Returns an error if:
254    /// - The token is already in use
255    /// - The source cannot be registered with the underlying poll mechanism
256    /// - The handler registry is full
257    ///
258    /// ## Example
259    ///
260    /// ```rust,no_run
261    /// use mill_io::{EventLoop, EventHandler};
262    /// use mio::{net::TcpListener, Interest, Token, event::Event};
263    /// use std::net::SocketAddr;
264    ///
265    /// struct ConnectionHandler;
266    /// impl EventHandler for ConnectionHandler {
267    ///     fn handle_event(&self, event: &Event) {
268    ///         // Handle new connections
269    ///     }
270    /// }
271    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
272    ///     let event_loop = EventLoop::default();
273    ///     let addr: SocketAddr = "0.0.0.0:8080".parse()?;
274    ///     let mut listener = TcpListener::bind(addr)?;
275    ///
276    ///     event_loop.register(
277    ///         &mut listener,
278    ///         Token(0),
279    ///         Interest::READABLE,
280    ///         ConnectionHandler
281    ///     )?;
282    ///    Ok(())
283    /// }
284    /// ```
285    pub fn register<H, S>(
286        &self,
287        source: &mut S,
288        token: Token,
289        interests: Interest,
290        handler: H,
291    ) -> Result<()>
292    where
293        H: EventHandler + Send + Sync + 'static,
294        S: mio::event::Source + ?Sized,
295    {
296        self.reactor
297            .poll_handle
298            .register(source, token, interests, handler)
299    }
300
301    /// Deregisters an I/O source from the event loop.
302    ///
303    /// Removes the source from the polling mechanism and clears its associated handler.
304    /// After deregistration, no more events will be delivered for this source.
305    ///
306    /// ## #Arguments
307    /// * `source` - The I/O source to deregister
308    /// * `token` - Token associated with the source during registration
309    ///
310    /// ## Error
311    ///
312    /// Returns an error if:
313    /// - The source is not currently registered
314    /// - The deregistration fails at the OS level
315    /// - The token is invalid
316    ///
317    /// ## Example
318    ///
319    /// ```rust,no_run
320    /// use mill_io::{EventLoop, EventHandler};
321    /// use mio::{net::TcpListener, Interest, Token, event::Event};
322    /// use std::net::SocketAddr;
323    ///
324    /// struct Handler;
325    /// impl EventHandler for Handler {
326    ///     fn handle_event(&self, _: &Event) {}
327    /// }
328    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
329    ///     
330    ///     let event_loop = EventLoop::default();
331    ///     let addr: SocketAddr = "127.0.0.1:0".parse()?;
332    ///     let mut listener = TcpListener::bind(addr)?;
333    ///     let token = Token(0);
334    ///
335    ///     // Register
336    ///     event_loop.register(&mut listener, token, Interest::READABLE, Handler)?;
337    ///
338    ///     // Later, deregister
339    ///     event_loop.deregister(&mut listener, token)?;
340    ///     Ok(())
341    /// }
342    /// ```
343    pub fn deregister<S>(&self, source: &mut S, token: Token) -> Result<()>
344    where
345        S: mio::event::Source + ?Sized,
346    {
347        self.reactor.poll_handle.deregister(source, token)
348    }
349
350    /// Runs the event loop, blocking the current thread and dispatching events.
351    ///
352    /// This method starts the reactor's main loop, which will:
353    /// 1. Poll for I/O events using the configured timeout
354    /// 2. Dispatch events to registered handlers via the thread pool
355    /// 3. Continue until [`stop()`](Self::stop) is called or an error occurs
356    ///
357    /// The method blocks the calling thread and will only return when the event loop
358    /// is stopped or encounters a fatal error.
359    ///
360    /// ## Errors
361    ///
362    /// Returns an error if:
363    /// - The polling mechanism fails
364    /// - The thread pool encounters a fatal error
365    /// - System resources are exhausted
366    ///
367    /// ## Example
368    ///
369    /// ```rust,no_run
370    /// use mill_io::EventLoop;
371    ///
372    /// let event_loop = EventLoop::default();
373    /// // Register some handlers first...
374    /// event_loop.run()
375    /// # ; Ok::<(), Box<dyn std::error::Error>>(())
376    /// ```
377    pub fn run(&self) -> Result<()> {
378        self.reactor.run()
379    }
380
381    /// Signals the event loop to stop gracefully.
382    ///
383    /// This method initiates a graceful shutdown of the event loop. It sends a shutdown
384    /// signal to the reactor, which will cause the main loop to exit after finishing
385    /// the current polling cycle.
386    ///
387    /// This method is non-blocking and returns immediately. The actual shutdown happens
388    /// asynchronously, and [`run()`](Self::run) will return once the shutdown is complete.
389    ///
390    /// # Thread Safety
391    ///
392    /// This method is thread-safe and can be called from any thread, making it suitable
393    /// for use in signal handlers or from other threads.
394    ///
395    /// ## Example
396    ///
397    /// ```rust,no_run
398    /// use mill_io::EventLoop;
399    /// use std::thread;
400    /// use std::sync::Arc;
401    ///
402    /// let event_loop = Arc::new(EventLoop::default());
403    /// let event_loop_clone = Arc::clone(&event_loop);
404    ///
405    /// // Start event loop in background thread
406    /// let handle = thread::spawn(move || {
407    ///     // In a real application, you would handle the result properly
408    ///     let _ = event_loop_clone.run();
409    /// });
410    ///
411    /// // Stop after some time
412    /// thread::sleep(std::time::Duration::from_secs(1));
413    /// event_loop.stop();
414    ///
415    /// // Wait for shutdown
416    /// let _ = handle.join();
417    /// ```
418    pub fn stop(&self) {
419        let shutdown_handler = self.reactor.get_shutdown_handle();
420        shutdown_handler.shutdown();
421    }
422}