Adds the full second half of the CORE-C12 fix:
- scheme_pool::SchemePool: a per-scheme worker pool that takes
closures and dispatches SchemeWork items to dedicated worker
threads via mpsc channels. Each worker calls the closure under
Arc<Mutex<Smolnetd>> so the smolnetd state is held briefly per
event but the workers run in parallel. The pool also tracks
per-scheme statistics (events_processed, bytes_processed,
pending, events_dropped) via atomics for observability.
- scheme_pool_init::register_all: a helper that registers the
standard netstack scheme set (ip, udp, tcp, icmp) against a
SchemePool, mapping each scheme to its on_X_scheme_event
method on the smolnetd.
- corec12_integration: the main loop integration layer. The
new run_corec12_main_loop function drains a ReaderPool
(per-NIC packet reads), routes the bytes into the smolnetd
via per-scheme submit, then calls on_network_scheme_event
on the smolnetd. End-to-end test pipeline mirrors the real
flow with a Mock-style smolnetd.
- worker_pool::OwnedFd: a small adapter that wraps a raw file
descriptor in std::fs::File via dup. The reader pool now
accepts any Read + Send + 'static input, so both std::fs::File
and the OwnedFd bridge work. The pool stays generic.
Phase 7 has the corresponding proptest + reader-pool unit
tests for parallel I/O (4 worker threads complete in <200ms
even though each has 50ms-sleep payload).
The smolnetd currently uses Rc<RefCell<...>> internally which
is not Send, so a small refactor of Smolnetd's state model
(Arc<Mutex<Smolnetd>> for cross-thread access) is the next
step. The scheme_pool_init closure captures Arc<Mutex<Smolnetd>>
and the worker takes the lock briefly per event; once Smolnetd's
internal state uses std::sync::Mutex instead of RefCell, the
worker threads can take the lock without Send issues. This is
a separate refactor that lives as a follow-up patch.