test: add test for EPOLLONESHOT, EPOLLRDHUP and EPOLLHUP
This commit is contained in:
parent
e55a7612b7
commit
26ef0feed5
243
test/test-oneshot-and-hangup.c
Normal file
243
test/test-oneshot-and-hangup.c
Normal file
@ -0,0 +1,243 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "wepoll.h"
|
||||
#include "win.h"
|
||||
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
int tcp_socketpair(SOCKET socks[2]) {
|
||||
SOCKET listen_sock;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof addr;
|
||||
|
||||
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (listen_sock == INVALID_SOCKET)
|
||||
return SOCKET_ERROR;
|
||||
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
if (bind(listen_sock, (struct sockaddr*) &addr, sizeof addr) == SOCKET_ERROR)
|
||||
goto err1;
|
||||
|
||||
if (getsockname(listen_sock, (struct sockaddr*) &addr, &addrlen) ==
|
||||
SOCKET_ERROR)
|
||||
goto err1;
|
||||
|
||||
if (listen(listen_sock, 1) == SOCKET_ERROR)
|
||||
goto err1;
|
||||
|
||||
socks[0] = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socks[0] == INVALID_SOCKET)
|
||||
goto err1;
|
||||
|
||||
if (connect(socks[0], (struct sockaddr*) &addr, sizeof addr) == SOCKET_ERROR)
|
||||
goto err2;
|
||||
|
||||
socks[1] = accept(listen_sock, NULL, NULL);
|
||||
if (socks[1] == INVALID_SOCKET)
|
||||
goto err2;
|
||||
|
||||
closesocket(listen_sock);
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
closesocket(socks[0]);
|
||||
err1:
|
||||
closesocket(listen_sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sock_set_nonblock(SOCKET sock, bool enable) {
|
||||
u_long arg = enable;
|
||||
return ioctlsocket(sock, FIONBIO, &arg);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
static const char HELLO[] = "hello, world!";
|
||||
HANDLE epoll_port;
|
||||
SOCKET send_sock, recv_sock;
|
||||
|
||||
{
|
||||
/* Create an epoll instance. */
|
||||
epoll_port = epoll_create(1);
|
||||
assert(epoll_port > 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Create a TCP socket pair. */
|
||||
SOCKET socks[2];
|
||||
int r = tcp_socketpair(socks);
|
||||
assert(r == 0);
|
||||
|
||||
send_sock = socks[0];
|
||||
recv_sock = socks[1];
|
||||
}
|
||||
|
||||
{
|
||||
/* Enable non-blocking mode on the receiving end. */
|
||||
int r = sock_set_nonblock(recv_sock, true);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Send some data in order to trigger an event on the receiving socket. */
|
||||
int r = send(send_sock, HELLO, sizeof HELLO, 0);
|
||||
assert(r == sizeof HELLO);
|
||||
}
|
||||
|
||||
{
|
||||
/* Add the receiving socket to the epoll set. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
ev.events = (uint32_t) EPOLLIN | EPOLLONESHOT;
|
||||
ev.data.sock = recv_sock;
|
||||
|
||||
r = epoll_ctl(epoll_port, EPOLL_CTL_ADD, recv_sock, &ev);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Receive the EPOLLIN event for recv_sock. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
memset(&ev, 0, sizeof ev);
|
||||
|
||||
r = epoll_wait(epoll_port, &ev, 1, -1);
|
||||
assert(r == 1);
|
||||
assert(ev.events == EPOLLIN);
|
||||
assert(ev.data.sock == recv_sock);
|
||||
}
|
||||
|
||||
{
|
||||
/* Read the data from the socket. */
|
||||
char buffer[16];
|
||||
int r = recv(recv_sock, buffer, sizeof buffer, 0);
|
||||
assert(r > 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Since the last epoll_ctl() call specified the EPOLLONESOT flag,
|
||||
* no events should be reported here -- neither EPOLLIN nor EPOLLHUP. */
|
||||
static const int timeout = 250; /* Quarter second. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
memset(&ev, 0, sizeof ev);
|
||||
|
||||
r = epoll_wait(epoll_port, &ev, 1, timeout);
|
||||
assert(r == 0); /* Time-out. */
|
||||
}
|
||||
|
||||
{
|
||||
/* Attempt to EPOLL_CTL_ADD the socket to the port. This should fail,
|
||||
* because EPOLLONESHOT causes events to be disabled after one is reported,
|
||||
* but the socket should not be dropped from the epoll set. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
ev.events = (uint32_t) EPOLLIN | EPOLLONESHOT;
|
||||
ev.data.sock = recv_sock;
|
||||
|
||||
r = epoll_ctl(epoll_port, EPOLL_CTL_ADD, recv_sock, &ev);
|
||||
|
||||
assert(r == -1);
|
||||
assert(errno == EEXIST);
|
||||
assert(GetLastError() == ERROR_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
{
|
||||
/* Modify the read socket event mask to EPOLLRDHUP. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
ev.events = (uint32_t) EPOLLRDHUP | EPOLLONESHOT;
|
||||
ev.data.sock = recv_sock;
|
||||
|
||||
r = epoll_ctl(epoll_port, EPOLL_CTL_MOD, recv_sock, &ev);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Send some data, which will never be read by the receiving end, otherwise
|
||||
* Windows won't detect that the connection is closed. */
|
||||
int r = send(send_sock, HELLO, sizeof HELLO, 0);
|
||||
assert(r == sizeof HELLO);
|
||||
}
|
||||
|
||||
{
|
||||
/* Send FIN packet. */
|
||||
int r = shutdown(send_sock, SD_SEND);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Receive the EPOLLRDHUP event for recv_sock. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
memset(&ev, 0, sizeof ev);
|
||||
|
||||
r = epoll_wait(epoll_port, &ev, 1, -1);
|
||||
assert(r == 1);
|
||||
assert(ev.events == EPOLLRDHUP);
|
||||
assert(ev.data.sock == recv_sock);
|
||||
}
|
||||
|
||||
{
|
||||
/* Close to receiving socket, so the sending socket should detect a
|
||||
* connection hang-up */
|
||||
int r = closesocket(recv_sock);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Add the *write* socket to the epoll set. The event mask is empty,
|
||||
* but since EPOLLHUP and EPOLLERR are always reportable, the next call to
|
||||
* epoll_wait() should be able to detect that the connection is now closed.
|
||||
*/
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
ev.events = 0;
|
||||
ev.data.sock = send_sock;
|
||||
|
||||
r = epoll_ctl(epoll_port, EPOLL_CTL_ADD, send_sock, &ev);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Receive the EPOLLHUP event for write end of the connection. */
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
memset(&ev, 0, sizeof ev);
|
||||
|
||||
r = epoll_wait(epoll_port, &ev, 1, -1);
|
||||
assert(r == 1);
|
||||
assert(ev.events == EPOLLHUP);
|
||||
assert(ev.data.sock == send_sock);
|
||||
}
|
||||
|
||||
{
|
||||
/* Close the send socket. */
|
||||
int r = closesocket(send_sock);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Close the epoll port. */
|
||||
int r = epoll_close(epoll_port);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user