wepoll/test/test-connect-success-events.c

133 lines
3.8 KiB
C

/* In this test we check that stream sockets connected with `connect()` do not
* continuously report EPOLLOUT, but only when they're actually writable.
* It is a regression test for a bug that was caused by incorrectly mapping the
`AFD_POLL_CONNECT`
* event to `EPOLLOUT`.
*/
#include <errno.h>
#include <stdlib.h>
#include "test-util.h"
#include "util.h"
#include "wepoll.h"
#include "win.h"
#include "ws.h"
#define PORT 12345
#define ROUNDS 5
#define SEND_LENGTH (1 << 20)
int main(void) {
unsigned long one = 1;
SOCKET listen_sock, server_sock, client_sock;
struct sockaddr_in addr;
HANDLE ephnd;
struct epoll_event ev;
int round;
int r;
/* Initialize winsock. */
r = ws_global_init();
check(r == 0);
/* Set up address that the server listens on and the client connects to. */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(PORT);
/* Create the server socket and start listening for incoming connections. */
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
check(listen_sock != INVALID_SOCKET);
r = bind(listen_sock, (struct sockaddr*) &addr, sizeof addr);
check(r == 0);
r = listen(listen_sock, SOMAXCONN);
check(r == 0);
/* Create the client-end socket, switch it to non-blocking mode. */
client_sock = socket(AF_INET, SOCK_STREAM, 0);
check(client_sock != INVALID_SOCKET);
r = ioctlsocket(client_sock, (long) FIONBIO, &one);
check(r == 0);
/* Create an epoll port for the client end of the connection. */
ephnd = epoll_create1(0);
check(ephnd != NULL);
/* Associate the client socket with the epoll port. */
ev.events = EPOLLOUT;
ev.data.sock = client_sock;
r = epoll_ctl(ephnd, EPOLL_CTL_ADD, client_sock, &ev);
check(r == 0);
/* Begin connecting the client socket to the server. */
r = connect(client_sock, (struct sockaddr*) &addr, sizeof addr);
check(r == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK);
/* Accept the server end of the connection. */
server_sock = accept(listen_sock, NULL, NULL);
check(server_sock != INVALID_SOCKET);
for (round = 0; round < ROUNDS; round++) {
static char buf[SEND_LENGTH];
struct epoll_event evs[8];
int bytes_received, bytes_sent;
/* Wait for client end to become writable. */
r = epoll_wait(ephnd, evs, array_count(evs), -1);
/* Try to send data until the kernel buffer is full. */
memset(buf, round, sizeof buf);
bytes_sent = 0;
do {
/* We shouldn't get here unless epoll_wait() reported that the client
* socket is writable. */
check(r == 1);
check(evs[0].data.sock == client_sock);
check(evs[0].events == EPOLLOUT);
/* The actual send() call should never fail, because epoll_wait() just
* reported the socket as writable. */
r = send(client_sock, buf, sizeof buf, 0);
check(r > 0);
bytes_sent += r;
/* Call epoll_wait() without blocking to see if there's more space in the
* kernel write buffer. */
r = epoll_wait(ephnd, evs, array_count(evs), 0);
} while (r > 0);
/* Verify that epoll_wait() reported no events, but did not fail. */
check(r == 0);
/* Read all data incoming on the server end of the connection. */
bytes_received = 0;
do {
r = recv(server_sock, buf, sizeof buf, 0);
check(r > 0);
check(buf[0] == round);
bytes_received += r;
} while (bytes_received < bytes_sent);
check(bytes_received == bytes_sent);
}
/* Disassociate the client socket from epoll port. */
r = epoll_ctl(ephnd, EPOLL_CTL_DEL, client_sock, NULL);
check(r == 0);
/* Close the epoll port. */
r = epoll_close(ephnd);
check(r == 0);
/* Close all sockets. */
r = closesocket(listen_sock);
check(r == 0);
r = closesocket(server_sock);
check(r == 0);
r = closesocket(client_sock);
check(r == 0);
return 0;
}