mirror of https://github.com/stjepang/smol
wip
This commit is contained in:
parent
6ab678ccaa
commit
de27769b98
|
@ -157,14 +157,14 @@ impl Reactor {
|
||||||
///
|
///
|
||||||
/// This doesn't have strong guarantees. If there are ready events, they may or may not be
|
/// This doesn't have strong guarantees. If there are ready events, they may or may not be
|
||||||
/// processed depending on whether the reactor is locked.
|
/// processed depending on whether the reactor is locked.
|
||||||
pub fn poll(&self) -> io::Result<()> {
|
pub fn poll(&self) -> io::Result<Option<usize>> {
|
||||||
if let Some(events) = self.events.try_lock() {
|
if let Some(mut lock) = self.try_lock() {
|
||||||
let reactor = self;
|
|
||||||
let mut lock = ReactorLock { reactor, events };
|
|
||||||
// React to events without blocking.
|
// React to events without blocking.
|
||||||
lock.react(false)?;
|
let n = lock.react(false)?;
|
||||||
|
Ok(Some(n))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locks the reactor.
|
/// Locks the reactor.
|
||||||
|
@ -173,6 +173,14 @@ impl Reactor {
|
||||||
let events = self.events.lock().await;
|
let events = self.events.lock().await;
|
||||||
ReactorLock { reactor, events }
|
ReactorLock { reactor, events }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to lock the reactor.
|
||||||
|
pub fn try_lock(&self) -> Option<ReactorLock<'_>> {
|
||||||
|
self.events.try_lock().map(|events| {
|
||||||
|
let reactor = self;
|
||||||
|
ReactorLock { reactor, events }
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Polls the reactor for I/O events and wakes up tasks.
|
/// Polls the reactor for I/O events and wakes up tasks.
|
||||||
|
@ -183,12 +191,18 @@ pub(crate) struct ReactorLock<'a> {
|
||||||
|
|
||||||
impl ReactorLock<'_> {
|
impl ReactorLock<'_> {
|
||||||
/// Blocks until at least one event is processed.
|
/// Blocks until at least one event is processed.
|
||||||
pub fn wait(&mut self) -> io::Result<()> {
|
pub fn wait(&mut self) -> io::Result<usize> {
|
||||||
self.react(true)
|
self.react(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn poll(&mut self) -> io::Result<usize> {
|
||||||
|
self.react(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Processes new events, optionally blocking until the first event.
|
/// Processes new events, optionally blocking until the first event.
|
||||||
fn react(&mut self, block: bool) -> io::Result<()> {
|
fn react(&mut self, block: bool) -> io::Result<usize> {
|
||||||
|
let mut count = 0;
|
||||||
loop {
|
loop {
|
||||||
// Fire timers and compute the timeout until the next event.
|
// Fire timers and compute the timeout until the next event.
|
||||||
let timeout = self.fire_timers();
|
let timeout = self.fire_timers();
|
||||||
|
@ -207,7 +221,7 @@ impl ReactorLock<'_> {
|
||||||
// The timeout was hit, so check for timers again.
|
// The timeout was hit, so check for timers again.
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
self.fire_timers();
|
self.fire_timers();
|
||||||
return Ok(());
|
return Ok(0); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
// At least one I/O event occured.
|
// At least one I/O event occured.
|
||||||
|
@ -225,11 +239,12 @@ impl ReactorLock<'_> {
|
||||||
|
|
||||||
// Wake up tasks waiting on I/O.
|
// Wake up tasks waiting on I/O.
|
||||||
for w in wakers.drain(..) {
|
for w in wakers.drain(..) {
|
||||||
|
count += 1; // TODO: don't forget timers
|
||||||
w.wake();
|
w.wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The syscall was interrupted - recompute the timeout and restart.
|
// The syscall was interrupted - recompute the timeout and restart.
|
||||||
|
|
42
src/run.rs
42
src/run.rs
|
@ -128,6 +128,7 @@ pub fn run<T>(future: impl Future<Output = T>) -> T {
|
||||||
//
|
//
|
||||||
// This way we make sure that if any changes happen that might give us new work will
|
// This way we make sure that if any changes happen that might give us new work will
|
||||||
// unblock epoll/kevent/wepoll and let us continue the loop.
|
// unblock epoll/kevent/wepoll and let us continue the loop.
|
||||||
|
let mut step = 0;
|
||||||
loop {
|
loop {
|
||||||
// 1. Poll the main future.
|
// 1. Poll the main future.
|
||||||
if let Poll::Ready(val) = throttle::setup(|| future.as_mut().poll(cx)) {
|
if let Poll::Ready(val) = throttle::setup(|| future.as_mut().poll(cx)) {
|
||||||
|
@ -137,15 +138,51 @@ pub fn run<T>(future: impl Future<Output = T>) -> T {
|
||||||
let more_local = local.execute();
|
let more_local = local.execute();
|
||||||
// 3. Run a batch of tasks in the work-stealing executor.
|
// 3. Run a batch of tasks in the work-stealing executor.
|
||||||
let more_worker = worker.execute();
|
let more_worker = worker.execute();
|
||||||
|
// TODO: wake every time a worker is dropped?
|
||||||
|
|
||||||
// 4. Poll the reactor.
|
// 4. Poll the reactor.
|
||||||
reactor.poll().expect("failure while polling I/O");
|
if let Some(mut reactor_lock) = reactor.try_lock() {
|
||||||
|
step = 0;
|
||||||
|
|
||||||
|
// Clear the two I/O events.
|
||||||
|
let local_ev = local.event().clear();
|
||||||
|
let ws_ev = ws_executor.event().clear();
|
||||||
|
|
||||||
|
if reactor_lock.poll().expect("failure while polling I/O") > 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if more_local || more_worker {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any of the two I/O events has been triggered, continue the loop.
|
||||||
|
if local_ev || ws_ev {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until an I/O event occurs.
|
||||||
|
reactor_lock.wait().expect("failure while waiting on I/O");
|
||||||
|
|
||||||
|
local.event().clear();
|
||||||
|
ws_executor.event().clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If there is more work in the thread-local or the work-stealing executor, continue
|
// If there is more work in the thread-local or the work-stealing executor, continue
|
||||||
// the loop.
|
// the loop.
|
||||||
if more_local || more_worker {
|
if more_local || more_worker {
|
||||||
|
step = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
step += 1;
|
||||||
|
if step <= 2 {
|
||||||
|
std::thread::yield_now();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
step = 0;
|
||||||
|
|
||||||
// Prepare for blocking until the reactor is locked or `local.event()` is triggered.
|
// Prepare for blocking until the reactor is locked or `local.event()` is triggered.
|
||||||
//
|
//
|
||||||
// Note that there is no need to wait for `ws_executor.event()`. If the reactor is
|
// Note that there is no need to wait for `ws_executor.event()`. If the reactor is
|
||||||
|
@ -173,6 +210,9 @@ pub fn run<T>(future: impl Future<Output = T>) -> T {
|
||||||
|
|
||||||
// Block until an I/O event occurs.
|
// Block until an I/O event occurs.
|
||||||
reactor_lock.wait().expect("failure while waiting on I/O");
|
reactor_lock.wait().expect("failure while waiting on I/O");
|
||||||
|
|
||||||
|
local.event().clear();
|
||||||
|
ws_executor.event().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -115,8 +115,7 @@ impl ThreadLocalExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll the reactor and drain the injector queue. We do this occasionally to make
|
// Drain the injector queue occasionally to make execution more fair.
|
||||||
// execution more fair to all tasks involved.
|
|
||||||
self.fetch();
|
self.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,11 +135,8 @@ impl ThreadLocalExecutor {
|
||||||
self.queue.borrow_mut().pop_front()
|
self.queue.borrow_mut().pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Polls the reactor and moves all tasks from the injector queue into the main queue.
|
/// Moves all tasks from the injector queue into the main queue.
|
||||||
fn fetch(&self) {
|
fn fetch(&self) {
|
||||||
// The reactor might wake tasks belonging to this executor.
|
|
||||||
Reactor::get().poll().expect("failure while polling I/O");
|
|
||||||
|
|
||||||
// Move tasks from the injector queue into the main queue.
|
// Move tasks from the injector queue into the main queue.
|
||||||
let mut queue = self.queue.borrow_mut();
|
let mut queue = self.queue.borrow_mut();
|
||||||
while let Ok(r) = self.injector.pop() {
|
while let Ok(r) = self.injector.pop() {
|
||||||
|
|
|
@ -33,7 +33,6 @@ use scoped_tls_hkt::scoped_thread_local;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
||||||
use crate::io_event::IoEvent;
|
use crate::io_event::IoEvent;
|
||||||
use crate::reactor::Reactor;
|
|
||||||
use crate::task::{Runnable, Task};
|
use crate::task::{Runnable, Task};
|
||||||
use crate::throttle;
|
use crate::throttle;
|
||||||
|
|
||||||
|
@ -180,10 +179,11 @@ impl Worker<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush the slot, grab some tasks from the global queue, and poll the reactor. We do
|
|
||||||
// this occasionally to make execution more fair to all tasks involved.
|
|
||||||
self.push(None);
|
self.push(None);
|
||||||
self.fetch();
|
if let Some(r) = retry_steal(|| self.executor.injector.steal_batch_and_pop(&self.queue))
|
||||||
|
{
|
||||||
|
self.push(Some(r));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are likely more tasks to run.
|
// There are likely more tasks to run.
|
||||||
|
@ -199,9 +199,6 @@ impl Worker<'_> {
|
||||||
if let Some(r) = self.slot.replace(runnable.into()) {
|
if let Some(r) = self.slot.replace(runnable.into()) {
|
||||||
// If the slot had a task, push it into the queue.
|
// If the slot had a task, push it into the queue.
|
||||||
self.queue.push(r);
|
self.queue.push(r);
|
||||||
|
|
||||||
// Notify other workers that there are stealable tasks.
|
|
||||||
self.executor.event.notify();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,39 +209,14 @@ impl Worker<'_> {
|
||||||
return Some(r);
|
return Some(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not, fetch more tasks from the injector queue, the reactor, or other workers.
|
|
||||||
self.fetch();
|
|
||||||
|
|
||||||
// Check the slot and the queue again.
|
|
||||||
self.slot.take().or_else(|| self.queue.pop())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Steals from the injector and polls the reactor, or steals from other workers if that fails.
|
|
||||||
fn fetch(&self) {
|
|
||||||
// Try stealing from the global queue.
|
// Try stealing from the global queue.
|
||||||
if let Some(r) = retry_steal(|| self.executor.injector.steal_batch_and_pop(&self.queue)) {
|
if let Some(r) = retry_steal(|| self.executor.injector.steal_batch_and_pop(&self.queue)) {
|
||||||
// Push the task, but don't return -- let's not forget to poll the reactor.
|
return Some(r);
|
||||||
self.push(r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll the reactor.
|
// Try stealing from other workers.
|
||||||
Reactor::get().poll().expect("failure while polling I/O");
|
|
||||||
|
|
||||||
// If there is at least one task in the slot, return.
|
|
||||||
if let Some(r) = self.slot.take() {
|
|
||||||
self.slot.set(Some(r));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is at least one task in the queue, return.
|
|
||||||
if !self.queue.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still no tasks found - our last hope is to steal from other workers.
|
|
||||||
let stealers = self.executor.stealers.read().unwrap();
|
let stealers = self.executor.stealers.read().unwrap();
|
||||||
|
retry_steal(|| {
|
||||||
if let Some(r) = retry_steal(|| {
|
|
||||||
// Pick a random starting point in the iterator list and rotate the list.
|
// Pick a random starting point in the iterator list and rotate the list.
|
||||||
let n = stealers.len();
|
let n = stealers.len();
|
||||||
let start = fast_random(n);
|
let start = fast_random(n);
|
||||||
|
@ -258,10 +230,7 @@ impl Worker<'_> {
|
||||||
// that's the collected result and we'll retry from the beginning.
|
// that's the collected result and we'll retry from the beginning.
|
||||||
iter.map(|(_, s)| s.steal_batch_and_pop(&self.queue))
|
iter.map(|(_, s)| s.steal_batch_and_pop(&self.queue))
|
||||||
.collect()
|
.collect()
|
||||||
}) {
|
})
|
||||||
// Push the stolen task.
|
|
||||||
self.push(r);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue