#include "event.h"
#include "net.h"
#include "utils.h"
+#include "xalloc.h"
struct timeval now;
+#ifndef HAVE_MINGW
static fd_set readfds;
static fd_set writefds;
-static volatile bool running;
+#else
+static const long READ_EVENTS = FD_READ | FD_ACCEPT | FD_CLOSE;
+static const long WRITE_EVENTS = FD_WRITE | FD_CONNECT;
+static DWORD event_count = 0;
+#endif
+static bool running;
static int io_compare(const io_t *a, const io_t *b) {
+#ifndef HAVE_MINGW
return a->fd - b->fd;
+#else
+ return a->event - b->event;
+#endif
}
static int timeout_compare(const timeout_t *a, const timeout_t *b) {
return;
io->fd = fd;
+#ifdef HAVE_MINGW
+ if (io->fd != -1) {
+ io->event = WSACreateEvent();
+ if (io->event == WSA_INVALID_EVENT)
+ abort();
+ }
+ event_count++;
+#endif
io->cb = cb;
io->data = data;
io->node.data = io;
abort();
}
+#ifdef HAVE_MINGW
+void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
+ io_add(io, cb, data, -1, 0);
+ io->event = event;
+}
+#endif
+
void io_set(io_t *io, int flags) {
+ if (flags == io->flags)
+ return;
io->flags = flags;
+ if (io->fd == -1)
+ return;
+#ifndef HAVE_MINGW
if(flags & IO_READ)
FD_SET(io->fd, &readfds);
else
FD_SET(io->fd, &writefds);
else
FD_CLR(io->fd, &writefds);
+#else
+ long events = 0;
+ if (flags & IO_WRITE)
+ events |= WRITE_EVENTS;
+ if (flags & IO_READ)
+ events |= READ_EVENTS;
+ if (WSAEventSelect(io->fd, io->event, events) != 0)
+ abort();
+#endif
}
void io_del(io_t *io) {
return;
io_set(io, 0);
+#ifdef HAVE_MINGW
+ if (io->fd != -1 && WSACloseEvent(io->event) == FALSE)
+ abort();
+ event_count--;
+#endif
splay_unlink_node(&io_tree, &io->node);
io->cb = NULL;
}
#endif
+static struct timeval * get_time_remaining(struct timeval *diff) {
+ gettimeofday(&now, NULL);
+ struct timeval *tv = NULL;
+
+ while(timeout_tree.head) {
+ timeout_t *timeout = timeout_tree.head->data;
+ timersub(&timeout->tv, &now, diff);
+
+ if(diff->tv_sec < 0) {
+ timeout->cb(timeout->data);
+ if(timercmp(&timeout->tv, &now, <))
+ timeout_del(timeout);
+ } else {
+ tv = diff;
+ break;
+ }
+ }
+
+ return tv;
+}
+
bool event_loop(void) {
running = true;
+#ifndef HAVE_MINGW
fd_set readable;
fd_set writable;
while(running) {
- gettimeofday(&now, NULL);
- struct timeval diff, *tv = NULL;
-
- while(timeout_tree.head) {
- timeout_t *timeout = timeout_tree.head->data;
- timersub(&timeout->tv, &now, &diff);
-
- if(diff.tv_sec < 0) {
- timeout->cb(timeout->data);
- if(timercmp(&timeout->tv, &now, <))
- timeout_del(timeout);
- } else {
- tv = &diff;
- break;
- }
- }
-
memcpy(&readable, &readfds, sizeof readable);
memcpy(&writable, &writefds, sizeof writable);
+ struct timeval diff;
+ struct timeval *tv = get_time_remaining(&diff);
int fds = 0;
fds = last->fd + 1;
}
-#ifdef HAVE_MINGW
- LeaveCriticalSection(&mutex);
-#endif
int n = select(fds, &readable, &writable, NULL, tv);
-#ifdef HAVE_MINGW
- EnterCriticalSection(&mutex);
-#endif
if(n < 0) {
if(sockwouldblock(sockerrno))
io->cb(io->data, IO_READ);
}
}
+#else
+ while (running) {
+ struct timeval diff;
+ struct timeval *tv = get_time_remaining(&diff);
+ DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
+
+ if (!event_count) {
+ Sleep(timeout_ms);
+ continue;
+ }
- return true;
-}
+ /*
+ For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
+ which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
+ it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
+ is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
+ to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
+
+ Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
+ this event being fired again if ignored.
+ */
+ io_t* writeable_io = NULL;
+ for splay_each(io_t, io, &io_tree)
+ if (io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
+ writeable_io = io;
+ break;
+ }
+ if (writeable_io) {
+ writeable_io->cb(writeable_io->data, IO_WRITE);
+ continue;
+ }
-void event_flush_output(void) {
- for splay_each(io_t, io, &io_tree)
- if(FD_ISSET(io->fd, &writefds))
- io->cb(io->data, IO_WRITE);
+ WSAEVENT* events = xmalloc(event_count * sizeof(*events));
+ DWORD event_index = 0;
+ for splay_each(io_t, io, &io_tree) {
+ events[event_index] = io->event;
+ event_index++;
+ }
+
+ DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
+
+ WSAEVENT event;
+ if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count)
+ event = events[result - WSA_WAIT_EVENT_0];
+ free(events);
+ if (result == WSA_WAIT_TIMEOUT)
+ continue;
+ if (result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count)
+ return false;
+
+ io_t *io = splay_search(&io_tree, &((io_t){.event = event}));
+ if (!io)
+ abort();
+
+ if (io->fd == -1) {
+ io->cb(io->data, 0);
+ } else {
+ WSANETWORKEVENTS network_events;
+ if (WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0)
+ return false;
+ if (network_events.lNetworkEvents & WRITE_EVENTS)
+ io->cb(io->data, IO_WRITE);
+ if (network_events.lNetworkEvents & READ_EVENTS)
+ io->cb(io->data, IO_READ);
+ }
+ }
+#endif
+
+ return true;
}
void event_exit(void) {
typedef struct io_t {
int fd;
int flags;
+#ifdef HAVE_MINGW
+ WSAEVENT event;
+#endif
io_cb_t cb;
void *data;
splay_node_t node;
extern struct timeval now;
extern void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags);
+#ifdef HAVE_MINGW
+extern void io_add_event(io_t *io, io_cb_t cb, void* data, WSAEVENT event);
+#endif
extern void io_del(io_t *io);
extern void io_set(io_t *io, int flags);
extern void signal_del(signal_t *sig);
extern bool event_loop(void);
-extern void event_flush_output(void);
extern void event_exit(void);
#endif
int device_fd = -1;
static HANDLE device_handle = INVALID_HANDLE_VALUE;
+static io_t device_read_io;
+static OVERLAPPED device_read_overlapped;
+static vpn_packet_t device_read_packet;
char *device = NULL;
char *iface = NULL;
static char *device_info = NULL;
extern char *myport;
-static DWORD WINAPI tapreader(void *bla) {
- int status;
- DWORD len;
- OVERLAPPED overlapped;
- vpn_packet_t packet;
-
- logger(DEBUG_ALWAYS, LOG_DEBUG, "Tap reader running");
-
- /* Read from tap device and send to parent */
+static void device_issue_read() {
+ device_read_overlapped.Offset = 0;
+ device_read_overlapped.OffsetHigh = 0;
- overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ int status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, NULL, &device_read_overlapped);
- for(;;) {
- overlapped.Offset = 0;
- overlapped.OffsetHigh = 0;
- ResetEvent(overlapped.hEvent);
-
- status = ReadFile(device_handle, (void *)packet.data, MTU, &len, &overlapped);
+ if(!status && GetLastError() != ERROR_IO_PENDING) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
+ device, strerror(errno));
+ }
+}
- if(!status) {
- if(GetLastError() == ERROR_IO_PENDING) {
- WaitForSingleObject(overlapped.hEvent, INFINITE);
- if(!GetOverlappedResult(device_handle, &overlapped, &len, FALSE))
- continue;
- } else {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
- device, strerror(errno));
- return -1;
- }
- }
+static void device_handle_read(void *data) {
+ ResetEvent(device_read_overlapped.hEvent);
- EnterCriticalSection(&mutex);
- packet.len = len;
- packet.priority = 0;
- route(myself, &packet);
- event_flush_output();
- LeaveCriticalSection(&mutex);
+ DWORD len;
+ if (!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
+ device, strerror(errno));
+ return;
}
+
+ device_read_packet.len = len;
+ device_read_packet.priority = 0;
+ route(myself, &device_read_packet);
+ device_issue_read();
}
static bool setup_device(void) {
/* Start the tap reader */
- thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
-
- if(!thread) {
- logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "CreateThread", winerror(GetLastError()));
- return false;
- }
+ io_add_event(&device_read_io, device_handle_read, NULL, CreateEvent(NULL, TRUE, FALSE, NULL));
+ device_read_overlapped.hEvent = device_read_io.event;
+ device_issue_read();
device_info = "Windows tap device";
}
static void close_device(void) {
+ io_del(&device_read_io);
+ CancelIo(device_handle);
+ CloseHandle(device_read_overlapped.hEvent);
CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
free(device); device = NULL;
#ifndef HAVE_MINGW
#define closesocket(s) close(s)
-#else
-extern CRITICAL_SECTION mutex;
#endif
#endif /* __TINC_NET_H__ */
return true;
}
+static io_t stop_io;
+
DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
switch(request) {
case SERVICE_CONTROL_INTERROGATE:
return ERROR_CALL_NOT_IMPLEMENTED;
}
- event_exit();
- status.dwWaitHint = 30000;
+ status.dwWaitHint = 1000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);
+ if (WSASetEvent(stop_io.event) == FALSE)
+ abort();
return NO_ERROR;
}
+static void stop_handler(void *data, int flags) {
+ event_exit();
+}
+
VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
extern int main2(int argc, char **argv);
+ io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
+ if (stop_io.event == FALSE)
+ abort();
+
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 0;
SetServiceStatus(statushandle, &status);
}
+ if (WSACloseEvent(stop_io.event) == FALSE)
+ abort();
+ io_del(&stop_io);
return;
}
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
-CRITICAL_SECTION mutex;
int main2(int argc, char **argv);
#endif
}
int main2(int argc, char **argv) {
- InitializeCriticalSection(&mutex);
- EnterCriticalSection(&mutex);
#endif
char *priority = NULL;