gsliepen/tinc — pull request #20 by dechamps (2014-06-22 20:03:14+00:00)
Title: Use Win32 events to interrupt the event loop from the TAP-Win32 thread

Commit f9ab8e266b93aa3be772374ef4a8fdb06e376568 introduced a `event_flush_output()` function that fires a write event on all IOs registered for write. This was meant to expedite writes on TCP sockets ordered from the TAP-Win32 thread; otherwise they would have to wait until the `select()` call times out, at which point the event loop would realize that there are new write I/Os to watch for.

There are two problems with the aforementioned change:

- If the TCP socket is busy (blocked) when the TAP thread calls route(),
it will still wait for the event loop timeout instead of waiting for
the socket to become available;

- Bluntly firing all registered write I/Os is problematic when one of
these I/Os was waiting for a TCP socket to connect; when the callback
gets executed it will assume the socket is now connected even though
it's not. I've observed weird behaviors because of this, such as
connection timeouts on tinc startup, and very confusing log output.

This commit fixes the issue by registering all I/Os with a `WSAEVENT` (which is really another name for a Win32 event) using `WSAEventSelect()`. Instead of using `select()` to wait for events, we now use `WSAWaitForMultipleEvents()`, but only for notification - `select()` with a NULL timeout is still used to find out which sockets actually fired so that we can keep as much common code as possible.

This new waiting method is equivalent to just using `select()`, but with one added feature: by manually signaling the `WSAEVENT` object, the `WSAWaitForMultipleEvents()` can be manually interrupted from any thread. Thus, the TAP-Win32 thread can now cleanly and efficiently request for an event loop restart simply by signalling the event.