/*-------------------------------------------------------------------------
 *
 * socket.c
 *	   Win32 socket wrappers to handle APC/socket bug
 *
 *
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
 *
 * $PostgreSQL $
 *
 *-------------------------------------------------------------------------
 */

#ifdef WIN32

#include "c.h"

#undef recv
#undef send
#undef select

int pg_recv(int s, void *buf, size_t len, int flags)
{
	int n = recv(s,buf,len,flags);

	/* Interrupted by socket/APC interaction? */
	if (n < 0 && GetLastError() == ERROR_IO_PENDING)
		errno = EINTR;
	return n;
}


int pg_send(int s, const void *msg, size_t len, int flags)
{
	int n = send(s,msg,len,flags);

	/* Interrupted by socket/APC interaction? */
	if (n < 0 && GetLastError() == ERROR_IO_PENDING)
		errno = EINTR;
	return n;
}


/*
 * Unlike the above calls, select() gives no indication that it has been interrupted
 * via either return value, errno or WSA/GetLastError. However, we know that all FD_SETs
 * return untouched. We take advantage of this behaviour by adding an additional socket
 * to the read_mask, which we know should never be "readable". If the select returns,
 * with no error, and with this FD marked, then we know we've been bitten by the APC/socket
 * bug, and can act accordingly.
 */
int pg_select(int n, fd_set* read_mask, fd_set* write_mask, fd_set* except_mask, const struct timeval* timeout)
{
	static int fd = INVALID_SOCKET;
	int err;
	fd_set alt_read_mask;
	fd_set *p_read_mask = read_mask ? read_mask : &alt_read_mask;

	if (fd == INVALID_SOCKET)
		fd = socket(AF_INET, SOCK_STREAM, 0);

	FD_ZERO(&alt_read_mask);
	FD_SET(fd,p_read_mask);

	/* first parameter is actually ignored; only for completeness */
	err = select(max(n,fd+1),p_read_mask,write_mask,except_mask,timeout);

	/* if we returned without error, but fd is set, we know we've exercised the APC/socket bug */
	if (err >= 0 && FD_ISSET(fd,p_read_mask))
	{
		if (read_mask)		FD_ZERO(read_mask);
		if (write_mask)		FD_ZERO(write_mask);
		if (except_mask)	FD_ZERO(except_mask);
		errno = EINTR;  /* Overkill? SetLastError(???); WSASetLastError(WSAEINTR); */
		return SOCKET_ERROR;
	}
	return err;
}

#endif
