core: make sure win32 sockets remain blocking

Commit 7e344b5 hade the side effect of forcing every socket to
be non-blocking on Windows. This is because of a (documented)
side effect of WSAEventSelect(). So we need to make sure to restore
blocking behaviour afterwards for relevant sockets.
This commit is contained in:
Pierre Ossman 2013-09-20 10:10:50 +02:00 committed by Tanu Kaskinen
parent abfca5cb58
commit 0138a51a2c
3 changed files with 69 additions and 9 deletions

View file

@ -148,6 +148,10 @@
static pa_strlist *recorded_env = NULL; static pa_strlist *recorded_env = NULL;
#ifdef OS_IS_WIN32
static fd_set nonblocking_fds;
#endif
#ifdef OS_IS_WIN32 #ifdef OS_IS_WIN32
#include "poll.h" #include "poll.h"
@ -179,8 +183,59 @@ char *pa_win32_get_toplevel(HANDLE handle) {
#endif #endif
static void set_nonblock(int fd, bool nonblock) {
#ifdef O_NONBLOCK
int v, nv;
pa_assert(fd >= 0);
pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
if (nonblock)
nv = v | O_NONBLOCK;
else
nv = v & ~O_NONBLOCK;
if (v != nv)
pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
#elif defined(OS_IS_WIN32)
u_long arg;
if (nonblock)
arg = 1;
else
arg = 0;
if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
pa_log_warn("Only sockets can be made non-blocking!");
return;
}
/* There is no method to query status, so we remember all fds */
if (nonblock)
FD_SET(fd, &nonblocking_fds);
else
FD_CLR(fd, &nonblocking_fds);
#else
pa_log_warn("Non-blocking I/O not supported.!");
#endif
}
/** Make a file descriptor nonblock. Doesn't do any error checking */ /** Make a file descriptor nonblock. Doesn't do any error checking */
void pa_make_fd_nonblock(int fd) { void pa_make_fd_nonblock(int fd) {
set_nonblock(fd, true);
}
/** Make a file descriptor blocking. Doesn't do any error checking */
void pa_make_fd_block(int fd) {
set_nonblock(fd, false);
}
/** Query if a file descriptor is non-blocking */
bool pa_is_fd_nonblock(int fd) {
#ifdef O_NONBLOCK #ifdef O_NONBLOCK
int v; int v;
@ -188,17 +243,12 @@ void pa_make_fd_nonblock(int fd) {
pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0); pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
if (!(v & O_NONBLOCK)) return !!(v & O_NONBLOCK);
pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
#elif defined(OS_IS_WIN32) #elif defined(OS_IS_WIN32)
u_long arg = 1; return !!FD_ISSET(fd, &nonblocking_fds);
if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
pa_log_warn("Only sockets can be made non-blocking!");
}
#else #else
pa_log_warn("Non-blocking I/O not supported.!"); return false;
#endif #endif
} }
@ -531,13 +581,15 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
return ret; return ret;
} }
/** Platform independent read function. Necessary since not all /** Platform independent close function. Necessary since not all
* systems treat all file descriptors equal. */ * systems treat all file descriptors equal. */
int pa_close(int fd) { int pa_close(int fd) {
#ifdef OS_IS_WIN32 #ifdef OS_IS_WIN32
int ret; int ret;
FD_CLR(fd, &nonblocking_fds);
if ((ret = closesocket(fd)) == 0) if ((ret = closesocket(fd)) == 0)
return 0; return 0;

View file

@ -57,6 +57,9 @@ struct timeval;
#endif #endif
void pa_make_fd_nonblock(int fd); void pa_make_fd_nonblock(int fd);
void pa_make_fd_block(int fd);
bool pa_is_fd_nonblock(int fd);
void pa_make_fd_cloexec(int fd); void pa_make_fd_cloexec(int fd);
int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid, bool update_perms); int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid, bool update_perms);

View file

@ -65,6 +65,8 @@ typedef unsigned long nfds_t;
#include <time.h> #include <time.h>
#include <pulsecore/core-util.h>
#ifndef INFTIM #ifndef INFTIM
# define INFTIM (-1) # define INFTIM (-1)
#endif #endif
@ -602,6 +604,9 @@ restart:
/* It's a socket. */ /* It's a socket. */
WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
WSAEventSelect ((SOCKET) h, 0, 0); WSAEventSelect ((SOCKET) h, 0, 0);
/* Have to restore blocking as WSAEventSelect() clears it */
if (!pa_is_fd_nonblock(pfd[i].fd))
pa_make_fd_block(pfd[i].fd);
/* If we're lucky, WSAEnumNetworkEvents already provided a way /* If we're lucky, WSAEnumNetworkEvents already provided a way
to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */ to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */