watcher: Add Windows support

Instead of a pipe we use a TCP socketpair (can't select() a _pipe()), and
Windsock2 send/recv functions instead of read/write.

Currently supported (and required) are file descriptors provided by Winsock
only; we might use a separate mechanism for traditional file handles if
required (or switch to Windows events and WaitForMultipleObjects) for a future
version.
This commit is contained in:
Martin Willi 2013-10-17 11:56:15 +02:00
parent df0769299a
commit 93f78d8225
1 changed files with 80 additions and 16 deletions

View File

@ -24,7 +24,9 @@
#include <unistd.h>
#include <errno.h>
#ifndef WIN32
#include <sys/select.h>
#endif
#include <fcntl.h>
typedef struct private_watcher_t private_watcher_t;
@ -119,7 +121,14 @@ static void update(private_watcher_t *this)
this->pending = TRUE;
if (this->notify[1] != -1)
{
ignore_result(write(this->notify[1], buf, sizeof(buf)));
#ifdef WIN32
if (send(this->notify[1], buf, sizeof(buf), 0) == -1)
#else
if (write(this->notify[1], buf, sizeof(buf)) == -1)
#endif
{
DBG1(DBG_JOB, "notifying watcher failed: %s", strerror(errno));
}
}
}
@ -293,21 +302,40 @@ static job_requeue_t watch(private_watcher_t *this)
{
char buf[1];
bool old;
ssize_t len;
job_t *job;
DBG2(DBG_JOB, "watcher going to select()");
thread_cleanup_push((void*)activate_all, this);
old = thread_cancelability(TRUE);
res = select(maxfd + 1, &rd, &wr, &ex, NULL);
thread_cancelability(old);
thread_cleanup_pop(FALSE);
if (res > 0)
{
if (this->notify[0] != -1 && FD_ISSET(this->notify[0], &rd))
{
DBG2(DBG_JOB, "watcher got notification, rebuilding");
while (read(this->notify[0], buf, sizeof(buf)) > 0);
while (TRUE)
{
#ifdef WIN32
len = recv(this->notify[0], buf, sizeof(buf), 0);
#else
len = read(this->notify[0], buf, sizeof(buf));
#endif
if (len == -1)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
DBG1(DBG_JOB, "reading watcher notify failed: %s",
strerror(errno));
}
break;
}
}
this->pending = FALSE;
DBG2(DBG_JOB, "watcher got notification, rebuilding");
return JOB_REQUEUE_DIRECT;
}
@ -446,13 +474,60 @@ METHOD(watcher_t, destroy, void,
free(this);
}
#ifdef WIN32
/**
* Create notify pipe with a TCP socketpair
*/
static bool create_notify(private_watcher_t *this)
{
u_long on = 1;
if (socketpair(AF_INET, SOCK_STREAM, 0, this->notify) == 0)
{
/* use non-blocking I/O on read-end of notify pipe */
if (ioctlsocket(this->notify[0], FIONBIO, &on) == 0)
{
return TRUE;
}
DBG1(DBG_LIB, "setting watcher notify pipe read-end non-blocking "
"failed: %s", strerror(errno));
}
return FALSE;
}
#else /* !WIN32 */
/**
* Create a notify pipe with a one-directional pipe
*/
static bool create_notify(private_watcher_t *this)
{
int flags;
if (pipe(this->notify) == 0)
{
/* use non-blocking I/O on read-end of notify pipe */
flags = fcntl(this->notify[0], F_GETFL);
if (flags != -1 &&
fcntl(this->notify[0], F_SETFL, flags | O_NONBLOCK) != -1)
{
return TRUE;
}
DBG1(DBG_LIB, "setting watcher notify pipe read-end non-blocking "
"failed: %s", strerror(errno));
}
return FALSE;
}
#endif /* !WIN32 */
/**
* See header
*/
watcher_t *watcher_create()
{
private_watcher_t *this;
int flags;
INIT(this,
.public = {
@ -467,18 +542,7 @@ watcher_t *watcher_create()
.notify = {-1, -1},
);
if (pipe(this->notify) == 0)
{
/* use non-blocking I/O on read-end of notify pipe */
flags = fcntl(this->notify[0], F_GETFL);
if (flags == -1 ||
fcntl(this->notify[0], F_SETFL, flags | O_NONBLOCK) == -1)
{
DBG1(DBG_LIB, "setting watcher notify pipe read-end non-blocking "
"failed: %s", strerror(errno));
}
}
else
if (!create_notify(this))
{
DBG1(DBG_LIB, "creating watcher notify pipe failed: %s",
strerror(errno));