[threadpool-ms-io] Fix epoll socket close/remove race condition
authorLudovic Henry <ludovic.henry@xamarin.com>
Fri, 10 Jul 2015 21:51:51 +0000 (18:51 -0300)
committerLudovic Henry <ludovic.henry@xamarin.com>
Tue, 14 Jul 2015 17:56:06 +0000 (14:56 -0300)
commit312ad9deef85fd2c43c6b0c2b8169370f2dab7e6
treeff56ef2d34f40ffb4c4f57856bd757d6c5ca0bf8
parenta004e31dc7c05ee2f3ca2cd9822c3bc46391f805
[threadpool-ms-io] Fix epoll socket close/remove race condition

With the previous commit, it fixes a range of bug that happens only with epoll. This is because, contrary to poll and kqueue, epoll keeps an internal state that has to be kept in sync with the threadpool_io->states hash table.

This threadpool_io->states hash table is keeping a list of file descriptor to watch, with the corresponding SocketAsyncResult to callback in case of event on the fd. But epoll also keeps its internal state that needs to me modified with EPOLL_CTL_ADD, EPOLL_CTL_MOD and EPOLL_CTL_DEL.
Contrary to kqueue, EPOLLONESHOT does not force epoll to remove the socket from its internal state once an event have been triggered; but it will just not fire this event again, until this event is reset. Also, epoll will remove a fd from its internal state when this fd is closed.

This will lead to a bunch of out-of-sync states like the following :
 - the user start reading asynchronously the STDOUT of a child process, the child process quit (closing the STDOUT fd), this fd is reused by the OS for something else (like a socket), the IO threadpool call epoll_ctl with EPOLL_CTL_MOD (because the fd is still in the states hashtable), but the OS returns an error because the underlying file is different, even if the fd is the same.
 - the user opens a socket, does some asynchronous operation on it, and close it; removing it from the table but not from epoll (as it does not call epoll_ctl with EPOLL_CTL_DEL). For another reason, the fd has not been closed in the OS (and thus in epoll), because it might, for example, still be open by another process. After that, we receive an event for this fd (as it is still in epoll), and between the time of the epoll_wait and the epoll_ctl with EPOLL_CTL_DEL, the fd is actually closed in the OS, leading to a EBADF error.

The g_warning have also been replaced by g_error, so we do not silently discard this kind of error in testing.
mono/metadata/threadpool-ms-io-epoll.c
mono/metadata/threadpool-ms-io.c