AFX - Asynchronous Function Execution (Part 2)


This blog post is a continuation of my previous blog post.



Implementation so far


Afx is able to schedule cpu-bound functions. There are some major bugs, but who cares. It does not support any I/O call, which is sad. This blog is about the Implementation of I/O in afx.



Why I/O does not work currently


I/O calls are generally blocking and make the thread sleep. When a the executor thread is sleeping and it receives a signal, it wakes up without caring whether the I/O call is finished or not. Same with sleep() as well.



Implementation


We can create a new thread, lets call it poller. It will use the epoll syscall and will wait for events to occur. We need to make custom wrappers around already existing I/O calls. In those wrappers we will first make the file-descriptor non-blocking(consider this in terms of sockets), so that they can be attached to epoll. Then we can have a state for every async function. It can be either RUNNABLE, BLOCKED_ON_IO or BLOCKED_ON_TIMER. We will use the timerfd_create syscall to use timers with epoll.


In our custom wrappers, we can mark the current async function as either BLOCKED_ON_IO or BLOCKED_ON_TIMER and yield to monitor to schedule the next RUNNABLE function. When epoll receive any event, it will mark the respective async function as RUNNABLE.


When a blocked function becomes RUNNABLE again, we will first block SIGURG signal on that thread, then call the underlying syscall, so that it can execute without any disturbance.


Problem: When all the functions are blocked, where should the executor go. It can go to its pause state. This was causing some error. So to work around, a background async function will be running always which has just one function, to be RUNNABLE all the time and to just pause for its entire time of execution, until some other function become RUNNABLE again.


Source code and references: https://github.com/vanshjangir/afx