domfarolino / browser

Simple browser components
4 stars 1 forks source link

Introduce TaskLoopForIO (macOS for now) #25

Closed domfarolino closed 3 years ago

domfarolino commented 3 years ago

Closes #24. First stab at TaskLoopForIO (macOS only for now), mostly based on the Chromium's base::MessagePumpKqueue, which is Chrome's IO thread implementation in macOS (I think; there also exists a message pump implementation that uses libevent though...). It uses the kqueue to listen to operating system events.

TaskLoopForIO supports user-posted tasks just like TaskLoopForWorker, however it implements this a little differently, since we don't relying on base::ConditionVariable to wake the sleeping TaskLoopForIO up. Because we're using kqueue for now on macOS, when other threads post a task to TaskLoopForIO, we need to wake the TaskLoopForIO up with a kernel event. We do this by having TaskLoopForIO always listen to a given mach port named wakeup_, and after PostTask() pushes a task to the queue_, it writes an empty message to wakeup_ to wake up the sleeping thread on which TaskLoop is "bound". When the sleeping thread wakes up, TaskLoopForIO::Run will be resumed (just after the kevent64() system call), and we take a look at the event that we've been woken up for, and take one of two paths:

In addition to supporting user-posted tasks like TaskLoopForWorker (described above), TaskLoopForIO exposes the ability to listen to a file descriptor via the base::TaskLoopForIO::SocketReader interface. A SocketReader registers itself with the TaskLoopForIO and associates itself with a given file descriptor it cares about. The SocketReader is inserted into the loop's async_socket_readers_ map. When a file descriptor that the TaskLoop knows about is written to, the TaskLoopForIO wakes up, finds the SocketReader associated with the file descriptor (via async_socket_readers_), and notifies the reader so it can read from the descriptor.


The kernel queue (kqueue) is interesting because if, while a TaskLoopForIO is busy executing a big task for example, multiple events with different filters (types, more-or-less) get pushed to the kqueue, once the TaskLoopForIO queries kevent64 again next, we can read them all of the events in the kqueue that have been posted since our last query. The return value of kevent64 tells us how many events are ready to be read/processed, and TaskLoopForIO supports this with an inner for loop that iterates over the return value of kevent64, and processes all given events. This is also why we resize the events_ vector upon every event loop iteration.


~Before this PR is finished, TaskLoopIO needs to support watching for file descriptor writes. That's the only way we'll be able to support mage.~

domfarolino commented 3 years ago

This is getting close. There is some cleaning up I'd like to do, but @musgravejw can you give it a quick once-over sanity check to see if this all makes sense to you?

domfarolino commented 3 years ago

I think this is all set to land! I've added some TODOs to follow up on, but I'd like to not block this any longer on those. Feel free to take a look but I'll probably aim to land this soon.

Again this is macOS only, so the next priority will be getting cross-platform build flag support so we can make more platform-specific TaskLoop bits.

domfarolino commented 3 years ago

Outstanding concerns: