From a2f6609d578cbc195b0d55e0744fe83996798d82 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 4 Dec 2017 23:21:39 +0100 Subject: [PATCH 01/25] api: report EINVAL if epoll_wait() is called with invalid `maxevents` --- src/api.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api.c b/src/api.c index dce2fe5..10438de 100644 --- a/src/api.c +++ b/src/api.c @@ -105,6 +105,9 @@ int epoll_wait(HANDLE ephnd, ep_port_t* port_info; int result; + if (maxevents <= 0) + return_error(-1, ERROR_INVALID_PARAMETER); + if (init() < 0) return -1; From af9a6eb40f109b839647edc0064c7686824db828 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 4 Dec 2017 23:22:03 +0100 Subject: [PATCH 02/25] test: add tests for epoll_wait() failure modes --- test/test-error.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/test/test-error.c b/test/test-error.c index 9096516..3bb5c4e 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -58,7 +58,49 @@ int main(void) { { /* Test epoll_wait() errors. */ - /* TODO. */ + HANDLE valid_ephnd; + struct epoll_event evs[1]; + int r; + + valid_ephnd = epoll_create1(0); + check(valid_ephnd != NULL); + + /* Invalid `ephnd` */ + r = epoll_wait(NULL, evs, 1, 0); + check_error(r == -1, EBADF, ERROR_INVALID_HANDLE); + r = epoll_wait(INVALID_HANDLE_VALUE, evs, 1, 0); + check_error(r == -1, EBADF, ERROR_INVALID_HANDLE); + r = epoll_wait(bad_value, evs, 1, 0); + check_error(r == -1, EBADF, ERROR_INVALID_HANDLE); + r = epoll_wait(bad_type, evs, 1, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + + /* Zero `maxevents` */ + r = epoll_wait(valid_ephnd, evs, 0, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(NULL, evs, 0, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(INVALID_HANDLE_VALUE, evs, 0, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(bad_value, evs, 0, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(bad_type, evs, 0, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + + /* Negative `maxevents` */ + r = epoll_wait(valid_ephnd, evs, -1, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(NULL, evs, -1, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(INVALID_HANDLE_VALUE, evs, -1, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(bad_value, evs, -1, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_wait(bad_type, evs, -1, 0); + check_error(r == -1, EINVAL, ERROR_INVALID_PARAMETER); + + r = epoll_close(valid_ephnd); + check(r == 0); } CloseHandle(bad_type); From 283aa5fe7679aa8b604b6d140da11e72cffab310 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 04:46:19 +0100 Subject: [PATCH 03/25] error: map ERROR_NOT_SUPPORTED to EPERM --- src/error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.c b/src/error.c index d05db90..3aec9bb 100644 --- a/src/error.c +++ b/src/error.c @@ -51,7 +51,7 @@ X(ERROR_NOT_LOCKED, EACCES) \ X(ERROR_NOT_READY, EACCES) \ X(ERROR_NOT_SAME_DEVICE, EXDEV) \ - X(ERROR_NOT_SUPPORTED, EOPNOTSUPP) \ + X(ERROR_NOT_SUPPORTED, EPERM) \ X(ERROR_NO_MORE_FILES, ENOENT) \ X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ X(ERROR_OPERATION_ABORTED, EINTR) \ From 4159a1436462c8aaea889a9f7c3664fd00686b43 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 04:47:59 +0100 Subject: [PATCH 04/25] afd: make afd_get_protocol() error reporting more accurate --- src/afd.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/afd.c b/src/afd.c index 2c071bd..79eb1a0 100644 --- a/src/afd.c +++ b/src/afd.c @@ -150,19 +150,18 @@ WEPOLL_INTERNAL ssize_t afd_get_protocol(SOCKET socket, if (id < 0) { /* If getting protocol information failed, it might be due to the socket * not being an AFD socket. If so, attempt to fetch the underlying base - * socket, then try again to obtain protocol information. If that also - * fails, return the *original* error. */ - DWORD original_error = GetLastError(); - if (original_error != ERROR_NOT_SUPPORTED) - return_error(-1); + * socket, then try again to obtain protocol information. */ + DWORD error = GetLastError(); + if (error != ERROR_NOT_SUPPORTED) + return -1; afd_socket = _afd_get_base_socket(socket); if (afd_socket == INVALID_SOCKET || afd_socket == socket) - return_error(-1, original_error); + return_error(-1, error); id = _afd_get_protocol_info(afd_socket, protocol_info); if (id < 0) - return_error(-1, original_error); + return -1; } *afd_socket_out = afd_socket; From e5a8d83c8b633832a4ce8a2978dc38c32952bf3e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 05:02:57 +0100 Subject: [PATCH 05/25] api: epoll_ctl() to return ENOTSOCK when `sock` argument is not a socket --- src/afd.c | 2 +- src/error.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/afd.c b/src/afd.c index 79eb1a0..48b0c4e 100644 --- a/src/afd.c +++ b/src/afd.c @@ -116,7 +116,7 @@ static ssize_t _afd_get_protocol_info(SOCKET socket, SO_PROTOCOL_INFOW, (char*) protocol_info, &opt_len) != 0) - return_error(-1); + return_handle_error(-1, socket); id = -1; for (size_t i = 0; i < array_count(AFD_PROVIDER_GUID_LIST); i++) { diff --git a/src/error.c b/src/error.c index 3aec9bb..753a8e1 100644 --- a/src/error.c +++ b/src/error.c @@ -95,7 +95,7 @@ X(WSAENETUNREACH, ENETUNREACH) \ X(WSAENOBUFS, ENOMEM) \ X(WSAENOTCONN, ENOTCONN) \ - X(WSAENOTSOCK, EBADF) \ + X(WSAENOTSOCK, ENOTSOCK) \ X(WSAEOPNOTSUPP, EOPNOTSUPP) \ X(WSAESHUTDOWN, EPIPE) \ X(WSAETIMEDOUT, ETIMEDOUT) \ From a11f4de81ce792ea04f23ab080bc8dc24a1ec274 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 05:11:32 +0100 Subject: [PATCH 06/25] test: add some epoll_ctl() test cases to test-error --- test/test-error.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/test/test-error.c b/test/test-error.c index 3bb5c4e..cb6cb43 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -52,8 +52,32 @@ int main(void) { } { - /* Test epoll_ctl() errors. */ - /* TODO. */ + /* Test epoll_ctl() errors. */ + /* TODO: incomplete. */ + HANDLE valid_ephnd; + SOCKET sock_bad = (SOCKET) 0xbadbad; + SOCKET sock_nonsock = (SOCKET) bad_type; + struct epoll_event ev; + int r; + + valid_ephnd = epoll_create1(0); + check(valid_ephnd != NULL); + + ev.data.u64 = 0; + ev.events = 0; + + /* Invalid `sock` */ + r = epoll_ctl(valid_ephnd, EPOLL_CTL_ADD, 0, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(valid_ephnd, EPOLL_CTL_ADD, INVALID_SOCKET, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(valid_ephnd, EPOLL_CTL_ADD, sock_bad, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(valid_ephnd, EPOLL_CTL_ADD, sock_nonsock, &ev); + check_error(r < 0, ENOTSOCK, WSAENOTSOCK); + + r = epoll_close(valid_ephnd); + check(r == 0); } { From 50f84cdc6b8bbee2c086c54239dec027788f96f9 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 19:12:52 +0100 Subject: [PATCH 07/25] port: refactor socket update queue --- src/port.c | 24 +++++++++--------------- src/port.h | 8 +++----- src/sock.c | 6 ++---- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/port.c b/src/port.c index 7e6bb06..a5005d3 100644 --- a/src/port.c +++ b/src/port.c @@ -50,7 +50,7 @@ ep_port_t* ep_port_new(HANDLE* iocp_out) { memset(port_info, 0, sizeof *port_info); port_info->iocp = iocp; - queue_init(&port_info->update_queue); + queue_init(&port_info->sock_update_queue); tree_init(&port_info->sock_tree); reflock_tree_node_init(&port_info->handle_tree_node); InitializeCriticalSection(&port_info->lock); @@ -113,12 +113,12 @@ int ep_port_delete(ep_port_t* port_info) { } static int _ep_port_update_events(ep_port_t* port_info) { - queue_t* update_queue = &port_info->update_queue; + queue_t* sock_update_queue = &port_info->sock_update_queue; /* Walk the queue, submitting new poll requests for every socket that needs * it. */ - while (!queue_empty(update_queue)) { - queue_node_t* queue_node = queue_first(update_queue); + while (!queue_empty(sock_update_queue)) { + queue_node_t* queue_node = queue_first(sock_update_queue); ep_sock_t* sock_info = container_of(queue_node, ep_sock_t, queue_node); if (ep_sock_update(port_info, sock_info) < 0) @@ -382,20 +382,14 @@ void ep_port_release_poll_group(poll_group_t* poll_group) { void ep_port_request_socket_update(ep_port_t* port_info, ep_sock_t* sock_info) { - if (ep_port_is_socket_update_pending(port_info, sock_info)) + if (queue_enqueued(&sock_info->queue_node)) return; - queue_append(&port_info->update_queue, &sock_info->queue_node); - assert(ep_port_is_socket_update_pending(port_info, sock_info)); + queue_append(&port_info->sock_update_queue, &sock_info->queue_node); } -void ep_port_clear_socket_update(ep_port_t* port_info, ep_sock_t* sock_info) { - if (!ep_port_is_socket_update_pending(port_info, sock_info)) +void ep_port_cancel_socket_update(ep_port_t* port_info, ep_sock_t* sock_info) { + unused(port_info); + if (!queue_enqueued(&sock_info->queue_node)) return; queue_remove(&sock_info->queue_node); } - -bool ep_port_is_socket_update_pending(ep_port_t* port_info, - ep_sock_t* sock_info) { - unused(port_info); - return queue_enqueued(&sock_info->queue_node); -} diff --git a/src/port.h b/src/port.h index 1c31f8f..beaf7ef 100644 --- a/src/port.h +++ b/src/port.h @@ -20,7 +20,7 @@ typedef struct ep_port { poll_group_allocator_t* poll_group_allocators[array_count(AFD_PROVIDER_GUID_LIST)]; tree_t sock_tree; - queue_t update_queue; + queue_t sock_update_queue; reflock_tree_node_t handle_tree_node; CRITICAL_SECTION lock; size_t active_poll_count; @@ -56,9 +56,7 @@ WEPOLL_INTERNAL ep_sock_t* ep_port_find_socket(ep_port_t* port_info, WEPOLL_INTERNAL void ep_port_request_socket_update(ep_port_t* port_info, ep_sock_t* sock_info); -WEPOLL_INTERNAL void ep_port_clear_socket_update(ep_port_t* port_info, - ep_sock_t* sock_info); -WEPOLL_INTERNAL bool ep_port_is_socket_update_pending(ep_port_t* port_info, - ep_sock_t* sock_info); +WEPOLL_INTERNAL void ep_port_cancel_socket_update(ep_port_t* port_info, + ep_sock_t* sock_info); #endif /* WEPOLL_PORT_H_ */ diff --git a/src/sock.c b/src/sock.c index 0fd0ed4..094f5d9 100644 --- a/src/sock.c +++ b/src/sock.c @@ -217,7 +217,7 @@ void ep_sock_delete(ep_port_t* port_info, ep_sock_t* sock_info) { } ep_port_del_socket(port_info, sock_info); - ep_port_clear_socket_update(port_info, sock_info); + ep_port_cancel_socket_update(port_info, sock_info); ep_port_release_poll_group(sock_private->poll_group); sock_private->poll_group = NULL; @@ -257,8 +257,6 @@ int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) { SOCKET driver_socket = poll_group_get_socket(sock_private->poll_group); bool socket_closed = false; - assert(ep_port_is_socket_update_pending(port_info, sock_info)); - if ((sock_private->poll_status == _POLL_PENDING) && (sock_private->user_events & _KNOWN_EPOLL_EVENTS & ~sock_private->pending_events) == 0) { @@ -303,7 +301,7 @@ int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) { assert(false); } - ep_port_clear_socket_update(port_info, sock_info); + ep_port_cancel_socket_update(port_info, sock_info); /* If we saw an ERROR_INVALID_HANDLE error, drop the socket. */ if (socket_closed) From eecfb0e5e3aa0a1f235ed47e6a47f19b4026fdef Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 19:37:23 +0100 Subject: [PATCH 08/25] port: make ep_port_release_poll_group() signature consistent --- src/port.c | 4 +++- src/port.h | 3 ++- src/sock.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/port.c b/src/port.c index a5005d3..77ec165 100644 --- a/src/port.c +++ b/src/port.c @@ -376,7 +376,9 @@ poll_group_t* ep_port_acquire_poll_group( return poll_group_acquire(pga); } -void ep_port_release_poll_group(poll_group_t* poll_group) { +void ep_port_release_poll_group(ep_port_t* port_info, + poll_group_t* poll_group) { + unused(port_info); poll_group_release(poll_group); } diff --git a/src/port.h b/src/port.h index beaf7ef..9ee58c0 100644 --- a/src/port.h +++ b/src/port.h @@ -44,7 +44,8 @@ WEPOLL_INTERNAL poll_group_t* ep_port_acquire_poll_group( ep_port_t* port_info, size_t protocol_id, const WSAPROTOCOL_INFOW* protocol_info); -WEPOLL_INTERNAL void ep_port_release_poll_group(poll_group_t* poll_group); +WEPOLL_INTERNAL void ep_port_release_poll_group(ep_port_t* port_info, + poll_group_t* poll_group); WEPOLL_INTERNAL int ep_port_add_socket(ep_port_t* port_info, ep_sock_t* sock_info, diff --git a/src/sock.c b/src/sock.c index 094f5d9..5419825 100644 --- a/src/sock.c +++ b/src/sock.c @@ -198,7 +198,7 @@ ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { err2: _ep_sock_free(sock_private); err1: - ep_port_release_poll_group(poll_group); + ep_port_release_poll_group(port_info, poll_group); return NULL; } @@ -218,7 +218,7 @@ void ep_sock_delete(ep_port_t* port_info, ep_sock_t* sock_info) { ep_port_del_socket(port_info, sock_info); ep_port_cancel_socket_update(port_info, sock_info); - ep_port_release_poll_group(sock_private->poll_group); + ep_port_release_poll_group(port_info, sock_private->poll_group); sock_private->poll_group = NULL; /* If the poll request still needs to complete, the ep_sock object can't From 05bad1e58b79c401e7fcc24b329780b2b6e7b5a3 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 19:29:02 +0100 Subject: [PATCH 09/25] sock: move poll cancellation to _ep_sock_cancel_poll() --- src/sock.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/sock.c b/src/sock.c index 5419825..77dc191 100644 --- a/src/sock.c +++ b/src/sock.c @@ -159,6 +159,19 @@ static inline void _ep_sock_free(_ep_sock_private_t* sock_private) { free(sock_private); } +static int _ep_sock_cancel_poll(_ep_sock_private_t* sock_private) { + assert(sock_private->poll_status == _POLL_PENDING); + + if (_poll_req_cancel(&sock_private->poll_req, + poll_group_get_socket(sock_private->poll_group)) < 0) + return -1; + + sock_private->poll_status = _POLL_CANCELLED; + sock_private->pending_events = 0; + + return 0; +} + ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { SOCKET afd_socket; ssize_t protocol_id; @@ -209,12 +222,8 @@ void ep_sock_delete(ep_port_t* port_info, ep_sock_t* sock_info) { assert(!sock_private->deleted); sock_private->deleted = true; - if (sock_private->poll_status == _POLL_PENDING) { - _poll_req_cancel(&sock_private->poll_req, - poll_group_get_socket(sock_private->poll_group)); - sock_private->poll_status = _POLL_CANCELLED; - sock_private->pending_events = 0; - } + if (sock_private->poll_status == _POLL_PENDING) + _ep_sock_cancel_poll(sock_private); ep_port_del_socket(port_info, sock_info); ep_port_cancel_socket_update(port_info, sock_info); @@ -254,7 +263,6 @@ int ep_sock_set_event(ep_port_t* port_info, int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) { _ep_sock_private_t* sock_private = _ep_sock_private(sock_info); - SOCKET driver_socket = poll_group_get_socket(sock_private->poll_group); bool socket_closed = false; if ((sock_private->poll_status == _POLL_PENDING) && @@ -270,16 +278,16 @@ int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) { * events that the user is interested in. Cancel the pending poll request; * when it completes it will be submitted again with the correct event * mask. */ - if (_poll_req_cancel(&sock_private->poll_req, driver_socket) < 0) + if (_ep_sock_cancel_poll(sock_private) < 0) return -1; - sock_private->poll_status = _POLL_CANCELLED; - sock_private->pending_events = 0; } else if (sock_private->poll_status == _POLL_CANCELLED) { /* The poll request has already been cancelled, we're still waiting for it * to return. For now, there's nothing that needs to be done. */ } else if (sock_private->poll_status == _POLL_IDLE) { + SOCKET driver_socket = poll_group_get_socket(sock_private->poll_group); + if (_poll_req_submit(&sock_private->poll_req, sock_private->user_events, sock_private->afd_socket, From f260365c457487db6e7c4d6b607b70e3db66466e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 20:03:53 +0100 Subject: [PATCH 10/25] port: plug memory leak where deleted socket would never be freed --- src/port.c | 21 +++++++++++++++++++++ src/port.h | 6 ++++++ src/sock.c | 51 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/port.c b/src/port.c index 77ec165..64dabc1 100644 --- a/src/port.c +++ b/src/port.c @@ -51,6 +51,7 @@ ep_port_t* ep_port_new(HANDLE* iocp_out) { port_info->iocp = iocp; queue_init(&port_info->sock_update_queue); + queue_init(&port_info->sock_deleted_queue); tree_init(&port_info->sock_tree); reflock_tree_node_init(&port_info->handle_tree_node); InitializeCriticalSection(&port_info->lock); @@ -86,6 +87,7 @@ int ep_port_close(ep_port_t* port_info) { int ep_port_delete(ep_port_t* port_info) { tree_node_t* tree_node; + queue_node_t* queue_node; EnterCriticalSection(&port_info->lock); @@ -97,6 +99,11 @@ int ep_port_delete(ep_port_t* port_info) { ep_sock_force_delete(port_info, sock_info); } + while ((queue_node = queue_first(&port_info->sock_deleted_queue)) != NULL) { + ep_sock_t* sock_info = container_of(queue_node, ep_sock_t, queue_node); + ep_sock_force_delete(port_info, sock_info); + } + for (size_t i = 0; i < array_count(port_info->poll_group_allocators); i++) { poll_group_allocator_t* pga = port_info->poll_group_allocators[i]; if (pga != NULL) @@ -395,3 +402,17 @@ void ep_port_cancel_socket_update(ep_port_t* port_info, ep_sock_t* sock_info) { return; queue_remove(&sock_info->queue_node); } + +void ep_port_add_deleted_socket(ep_port_t* port_info, ep_sock_t* sock_info) { + if (queue_enqueued(&sock_info->queue_node)) + return; + queue_append(&port_info->sock_deleted_queue, &sock_info->queue_node); +} + +void ep_port_remove_deleted_socket(ep_port_t* port_info, + ep_sock_t* sock_info) { + unused(port_info); + if (!queue_enqueued(&sock_info->queue_node)) + return; + queue_remove(&sock_info->queue_node); +} diff --git a/src/port.h b/src/port.h index 9ee58c0..07151e4 100644 --- a/src/port.h +++ b/src/port.h @@ -21,6 +21,7 @@ typedef struct ep_port { poll_group_allocators[array_count(AFD_PROVIDER_GUID_LIST)]; tree_t sock_tree; queue_t sock_update_queue; + queue_t sock_deleted_queue; reflock_tree_node_t handle_tree_node; CRITICAL_SECTION lock; size_t active_poll_count; @@ -60,4 +61,9 @@ WEPOLL_INTERNAL void ep_port_request_socket_update(ep_port_t* port_info, WEPOLL_INTERNAL void ep_port_cancel_socket_update(ep_port_t* port_info, ep_sock_t* sock_info); +WEPOLL_INTERNAL void ep_port_add_deleted_socket(ep_port_t* port_info, + ep_sock_t* sock_info); +WEPOLL_INTERNAL void ep_port_remove_deleted_socket(ep_port_t* port_info, + ep_sock_t* sock_info); + #endif /* WEPOLL_PORT_H_ */ diff --git a/src/sock.c b/src/sock.c index 77dc191..7733ca4 100644 --- a/src/sock.c +++ b/src/sock.c @@ -34,7 +34,7 @@ typedef struct _ep_sock_private { uint32_t user_events; uint32_t pending_events; _poll_status_t poll_status; - unsigned deleted : 1; + bool delete_pending; } _ep_sock_private_t; static DWORD _epoll_events_to_afd_events(uint32_t epoll_events) { @@ -155,7 +155,6 @@ static inline _ep_sock_private_t* _ep_sock_alloc(void) { } static inline void _ep_sock_free(_ep_sock_private_t* sock_private) { - assert(sock_private->poll_status == _POLL_IDLE); free(sock_private); } @@ -216,30 +215,41 @@ err1: return NULL; } -void ep_sock_delete(ep_port_t* port_info, ep_sock_t* sock_info) { +static void _ep_sock_delete(ep_port_t* port_info, + ep_sock_t* sock_info, + bool force) { _ep_sock_private_t* sock_private = _ep_sock_private(sock_info); - assert(!sock_private->deleted); - sock_private->deleted = true; + if (!sock_private->delete_pending) { + if (sock_private->poll_status == _POLL_PENDING) + _ep_sock_cancel_poll(sock_private); - if (sock_private->poll_status == _POLL_PENDING) - _ep_sock_cancel_poll(sock_private); + ep_port_cancel_socket_update(port_info, sock_info); + ep_port_del_socket(port_info, sock_info); - ep_port_del_socket(port_info, sock_info); - ep_port_cancel_socket_update(port_info, sock_info); - ep_port_release_poll_group(port_info, sock_private->poll_group); - sock_private->poll_group = NULL; + sock_private->delete_pending = true; + } /* If the poll request still needs to complete, the ep_sock object can't - * be free()d yet. `ep_sock_feed_event` will take care of this later. */ - if (sock_private->poll_status == _POLL_IDLE) + * be free()d yet. `ep_sock_feed_event()` or `ep_port_close()` will take care + * of this later. */ + if (force || sock_private->poll_status == _POLL_IDLE) { + /* Free the sock_info now. */ + ep_port_remove_deleted_socket(port_info, sock_info); + ep_port_release_poll_group(port_info, sock_private->poll_group); _ep_sock_free(sock_private); + } else { + /* Free the socket later. */ + ep_port_add_deleted_socket(port_info, sock_info); + } +} + +void ep_sock_delete(ep_port_t* port_info, ep_sock_t* sock_info) { + _ep_sock_delete(port_info, sock_info, false); } void ep_sock_force_delete(ep_port_t* port_info, ep_sock_t* sock_info) { - _ep_sock_private_t* sock_private = _ep_sock_private(sock_info); - sock_private->poll_status = _POLL_IDLE; - ep_sock_delete(port_info, sock_info); + _ep_sock_delete(port_info, sock_info, true); } int ep_sock_set_event(ep_port_t* port_info, @@ -265,6 +275,7 @@ int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) { _ep_sock_private_t* sock_private = _ep_sock_private(sock_info); bool socket_closed = false; + assert(!sock_private->delete_pending); if ((sock_private->poll_status == _POLL_PENDING) && (sock_private->user_events & _KNOWN_EPOLL_EVENTS & ~sock_private->pending_events) == 0) { @@ -331,10 +342,10 @@ int ep_sock_feed_event(ep_port_t* port_info, sock_private->poll_status = _POLL_IDLE; sock_private->pending_events = 0; - if (sock_private->deleted) { - /* Ignore completion for overlapped poll operation if the socket has been - * deleted; instead, free the socket. */ - _ep_sock_free(sock_private); + if (sock_private->delete_pending) { + /* Ignore completion for overlapped poll operation if the socket is pending + * deletion; instead, delete the socket. */ + ep_sock_delete(port_info, sock_info); return 0; } From 9114b4232a68bee2f87df6ae8391952f7cf9bdb2 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 05:36:09 +0100 Subject: [PATCH 11/25] test: add test to detect memory leak The leak itself was fixed in the previous commit. --- test/test-leak-1.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/test-leak-1.c diff --git a/test/test-leak-1.c b/test/test-leak-1.c new file mode 100644 index 0000000..6364dfc --- /dev/null +++ b/test/test-leak-1.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +#include "test-util.h" +#include "util.h" +#include "wepoll.h" +#include "win.h" + +int main(void) { + HANDLE ephnd; + SOCKET sock; + struct epoll_event ev; + int r; + + ephnd = epoll_create1(0); + check(ephnd != NULL); + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + check(sock != INVALID_SOCKET); + + ev.events = 0; + ev.data.u64 = 0; + r = epoll_ctl(ephnd, EPOLL_CTL_ADD, sock, &ev); + check(r == 0); + + r = epoll_wait(ephnd, &ev, 1, 0); + check(r == 0); + + r = epoll_ctl(ephnd, EPOLL_CTL_DEL, sock, NULL); + check(r == 0); + + r = epoll_close(ephnd); + check(r == 0); + + return 0; +} From ada9cb59106c0ec67a8d2f95452c78648298bd92 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 20:09:48 +0100 Subject: [PATCH 12/25] test: close epoll handle after running test-ctl-fuzz --- test/test-ctl-fuzz.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test-ctl-fuzz.c b/test/test-ctl-fuzz.c index 3ba25ed..89a1c24 100644 --- a/test/test-ctl-fuzz.c +++ b/test/test-ctl-fuzz.c @@ -88,5 +88,7 @@ int main(void) { } } while (total_time < RUN_TIME); + epoll_close(epfd); + return 0; } From 4d6cb0ff5bc16adc813f49e13688ef5280a8c65c Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 20:09:59 +0100 Subject: [PATCH 13/25] test: remove unnecessary include from test-error --- test/test-error.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test-error.c b/test/test-error.c index cb6cb43..28462dc 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -3,7 +3,6 @@ #include #include "test-util.h" -#include "util.h" #include "wepoll.h" #include "win.h" From 6182e1ec73bc6810fba2bc484d193dd261b75875 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 5 Dec 2017 20:30:04 +0100 Subject: [PATCH 14/25] test: add leak checker to all tests --- test/shared/leak-check.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/shared/leak-check.c diff --git a/test/shared/leak-check.c b/test/shared/leak-check.c new file mode 100644 index 0000000..a8706d3 --- /dev/null +++ b/test/shared/leak-check.c @@ -0,0 +1,40 @@ +#include +#include + +#include "test-util.h" + +#if defined(_MSC_VER) /* msvc */ +#pragma section(".CRT$XCU", read) +#define constructor(fn) \ + static void __cdecl fn(void); \ + __declspec(allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = fn; \ + static void __cdecl fn(void) +#else /* gcc/clang */ +#define constructor(fn) \ + static void fn(void) __attribute__((constructor)); \ + static void fn(void) +#endif + +static void __cdecl leak_check_finalize(void); + +constructor(leak_check_init) { + /* Enable leak checking. */ + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF); + + /* Write diagnostics to stderr. */ + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + + /* Register the finalization function to run when the program exits. */ + atexit(leak_check_finalize); +} + +void leak_check_finalize(void) { + /* Check if there were memory leaks. */ + int leaks_found = _CrtDumpMemoryLeaks(); + check(!leaks_found); +} From 86fbe53e70cbebaae3d2bada620106f851933e75 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 01:52:54 +0100 Subject: [PATCH 15/25] build: disable 'conditional expression is constant' warning Visual Studio 2013 often generates this warning when it encounters a `do { ... } while (0)` construct, which is commonly used in preprocessor macros. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b2909e..9ef363b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if(WIN32) endif() if(MSVC) - add_compile_options(/Wall /wd4201 /wd4242 /wd4710 /wd4711 /wd4820) + add_compile_options(/Wall /wd4127 /wd4201 /wd4242 /wd4710 /wd4711 /wd4820) else() add_compile_options(-Wall) endif() From 450292aebce9b9d2705477d3ea349155e7adedae Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 04:40:57 +0100 Subject: [PATCH 16/25] test: add missing errno.h include to test-oneshot-and-hangup --- test/test-oneshot-and-hangup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-oneshot-and-hangup.c b/test/test-oneshot-and-hangup.c index ecf58c9..ff1d731 100644 --- a/test/test-oneshot-and-hangup.c +++ b/test/test-oneshot-and-hangup.c @@ -1,3 +1,4 @@ +#include #include #include #include From 99ce86742401eea5550e0fb89b86ff9d52bd648a Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 04:41:51 +0100 Subject: [PATCH 17/25] src: polyfill the 'include' keyword for Visual Studio 2013 --- src/queue.c | 1 + src/tree.c | 1 + src/util.h | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/src/queue.c b/src/queue.c index 3ad927e..f52cb3d 100644 --- a/src/queue.c +++ b/src/queue.c @@ -2,6 +2,7 @@ #include #include "queue.h" +#include "util.h" void queue_init(queue_t* queue) { queue_node_init(&queue->head); diff --git a/src/tree.c b/src/tree.c index 743814b..d742b95 100644 --- a/src/tree.c +++ b/src/tree.c @@ -3,6 +3,7 @@ #include "error.h" #include "rb.h" #include "tree.h" +#include "util.h" static inline int _tree_compare(tree_node_t* a, tree_node_t* b) { if (a->key < b->key) diff --git a/src/util.h b/src/util.h index e59de48..eb42632 100644 --- a/src/util.h +++ b/src/util.h @@ -20,6 +20,11 @@ typedef intptr_t ssize_t; #define unused(v) ((void) (v)) +#if defined(_MSC_VER) && _MSC_VER < 1900 +/* Polyfill `inline` for msvc 12 (Visual Studio 2013) */ +#define inline __inline +#endif + #ifdef __clang__ /* Polyfill static_assert() because clang doesn't support it. */ #define static_assert(condition, message) typedef __attribute__( \ From 42ccc11634ff6629eb2684aec5a3955fedcf955f Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 04:42:37 +0100 Subject: [PATCH 18/25] src: suppress warnings caused by Windows SDK headers --- src/win.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/win.h b/src/win.h index 4fa10a0..68dc900 100644 --- a/src/win.h +++ b/src/win.h @@ -5,8 +5,12 @@ #define WIN32_LEAN_AND_MEAN #endif -#include -#include -#include +#pragma warning(push, 1) + +#include +#include +#include + +#pragma warning(pop) #endif From 30ca0a1319cc51195dfd160e9d8de9b9eb3abb0f Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 04:42:57 +0100 Subject: [PATCH 19/25] ci: add appveyor configuration file --- .appveyor.yml | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..c00e87f --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,48 @@ +version: b{build}-{branch} + +branches: + except: + - dist + +environment: + matrix: + - platform: x64 + generator: "Visual Studio 15 2017 Win64" + appveyor_build_worker_image: Visual Studio 2017 + - platform: x86 + generator: "Visual Studio 15 2017" + appveyor_build_worker_image: Visual Studio 2017 + - platform: x64 + generator: "Visual Studio 14 2015 Win64" + appveyor_build_worker_image: Visual Studio 2015 + - platform: x86 + generator: "Visual Studio 14 2015" + appveyor_build_worker_image: Visual Studio 2015 + - platform: x64 + generator: "Visual Studio 12 2013 Win64" + appveyor_build_worker_image: Visual Studio 2013 + - platform: x86 + generator: "Visual Studio 12 2013" + appveyor_build_worker_image: Visual Studio 2013 + +configuration: Debug + +clone_folder: c:\wepoll +shallow_clone: true +clone_depth: 1 + +init: + - cmd: set + +install: + - ps: Install-Product node 'Current' + +before_build: + - cmd: cmake . -G "%generator%" + +build_script: + - cmd: cmake --build . --config "%configuration%" --target wepoll.dll + - cmd: cmake --build . --config "%configuration%" --target wepoll-combined.dll + +test_script: + - cmd: cmake --build . --config "%configuration%" --target test-all From d8093dc16730ff4904c4517bb73d60ece765e237 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 04:43:17 +0100 Subject: [PATCH 20/25] doc: add build status badge to readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1165d3f..e76bfff 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # wepoll - epoll for windows +[![][ci status badge]][ci status link] + This library implements the [epoll][man epoll] API for Windows applications. It attempts to be efficient, and to match the Linux API as semantics as closely as possible. @@ -112,6 +114,9 @@ int epoll_wait(HANDLE ephnd, * TODO: expand * [man page][man epoll_wait] + +[ci status badge]: https://ci.appveyor.com/api/projects/status/github/piscisaureus/wepoll?branch=master&svg=true +[ci status link]: https://ci.appveyor.com/project/piscisaureus/wepoll/branch/master [dist]: https://github.com/piscisaureus/wepoll/tree/dist [man epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html [man epoll_create]: http://man7.org/linux/man-pages/man2/epoll_create.2.html From 7cf7ecfef68e29b590efc1c4f1a0afd78cc1da7b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 04:50:40 +0100 Subject: [PATCH 21/25] build: remove run-cmake.bat --- run-cmake.bat | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 run-cmake.bat diff --git a/run-cmake.bat b/run-cmake.bat deleted file mode 100644 index 1ef170b..0000000 --- a/run-cmake.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -rd /s/q CMakeFiles -del CMakeCache.txt *.cmake ALL_BUILD*.* ZERO_CHECK*.* -cmake.exe -G "Visual Studio 14 2015 Win64" %* From 5b7ebc3b93cc4230197b0d0c384d0ae58c34ea61 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 05:23:39 +0100 Subject: [PATCH 22/25] ci: remove unnecessary branch filter from appveyor configuration --- .appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index c00e87f..99d02e8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,9 +1,5 @@ version: b{build}-{branch} -branches: - except: - - dist - environment: matrix: - platform: x64 From b5864f3a0f18b75d7ad64509533c1bb7ecff4796 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 18:01:49 +0100 Subject: [PATCH 23/25] afd: report EPERM+ERROR_DEVICE_FEATURE_NOT_SUPPORTED for non-afd sockets --- src/afd.c | 4 +- src/error.c | 193 ++++++++++++++++++++++++++-------------------------- 2 files changed, 99 insertions(+), 98 deletions(-) diff --git a/src/afd.c b/src/afd.c index 48b0c4e..a2f9d24 100644 --- a/src/afd.c +++ b/src/afd.c @@ -130,7 +130,7 @@ static ssize_t _afd_get_protocol_info(SOCKET socket, /* Check if the protocol uses an msafd socket. */ if (id < 0) - return_error(-1, ERROR_NOT_SUPPORTED); + return_error(-1, ERROR_DEVICE_FEATURE_NOT_SUPPORTED); return id; } @@ -152,7 +152,7 @@ WEPOLL_INTERNAL ssize_t afd_get_protocol(SOCKET socket, * not being an AFD socket. If so, attempt to fetch the underlying base * socket, then try again to obtain protocol information. */ DWORD error = GetLastError(); - if (error != ERROR_NOT_SUPPORTED) + if (error != ERROR_DEVICE_FEATURE_NOT_SUPPORTED) return -1; afd_socket = _afd_get_base_socket(socket); diff --git a/src/error.c b/src/error.c index 753a8e1..40b0ea2 100644 --- a/src/error.c +++ b/src/error.c @@ -3,102 +3,103 @@ #include "error.h" #include "win.h" -#define _ERROR_ERRNO_MAP(X) \ - X(ERROR_ACCESS_DENIED, EACCES) \ - X(ERROR_ALREADY_EXISTS, EEXIST) \ - X(ERROR_BAD_COMMAND, EACCES) \ - X(ERROR_BAD_EXE_FORMAT, ENOEXEC) \ - X(ERROR_BAD_LENGTH, EACCES) \ - X(ERROR_BAD_NETPATH, ENOENT) \ - X(ERROR_BAD_NET_NAME, ENOENT) \ - X(ERROR_BAD_NET_RESP, ENETDOWN) \ - X(ERROR_BAD_PATHNAME, ENOENT) \ - X(ERROR_BROKEN_PIPE, EPIPE) \ - X(ERROR_CANNOT_MAKE, EACCES) \ - X(ERROR_COMMITMENT_LIMIT, ENOMEM) \ - X(ERROR_CONNECTION_ABORTED, ECONNABORTED) \ - X(ERROR_CONNECTION_ACTIVE, EISCONN) \ - X(ERROR_CONNECTION_REFUSED, ECONNREFUSED) \ - X(ERROR_CRC, EACCES) \ - X(ERROR_DIR_NOT_EMPTY, ENOTEMPTY) \ - X(ERROR_DISK_FULL, ENOSPC) \ - X(ERROR_DUP_NAME, EADDRINUSE) \ - X(ERROR_FILENAME_EXCED_RANGE, ENOENT) \ - X(ERROR_FILE_NOT_FOUND, ENOENT) \ - X(ERROR_GEN_FAILURE, EACCES) \ - X(ERROR_GRACEFUL_DISCONNECT, EPIPE) \ - X(ERROR_HOST_DOWN, EHOSTUNREACH) \ - X(ERROR_HOST_UNREACHABLE, EHOSTUNREACH) \ - X(ERROR_INSUFFICIENT_BUFFER, EFAULT) \ - X(ERROR_INVALID_ADDRESS, EADDRNOTAVAIL) \ - X(ERROR_INVALID_FUNCTION, EINVAL) \ - X(ERROR_INVALID_HANDLE, EBADF) \ - X(ERROR_INVALID_NETNAME, EADDRNOTAVAIL) \ - X(ERROR_INVALID_PARAMETER, EINVAL) \ - X(ERROR_INVALID_USER_BUFFER, EMSGSIZE) \ - X(ERROR_IO_PENDING, EINPROGRESS) \ - X(ERROR_LOCK_VIOLATION, EACCES) \ - X(ERROR_MORE_DATA, EMSGSIZE) \ - X(ERROR_NETNAME_DELETED, ECONNABORTED) \ - X(ERROR_NETWORK_ACCESS_DENIED, EACCES) \ - X(ERROR_NETWORK_BUSY, ENETDOWN) \ - X(ERROR_NETWORK_UNREACHABLE, ENETUNREACH) \ - X(ERROR_NOACCESS, EFAULT) \ - X(ERROR_NONPAGED_SYSTEM_RESOURCES, ENOMEM) \ - X(ERROR_NOT_ENOUGH_MEMORY, ENOMEM) \ - X(ERROR_NOT_ENOUGH_QUOTA, ENOMEM) \ - X(ERROR_NOT_FOUND, ENOENT) \ - X(ERROR_NOT_LOCKED, EACCES) \ - X(ERROR_NOT_READY, EACCES) \ - X(ERROR_NOT_SAME_DEVICE, EXDEV) \ - X(ERROR_NOT_SUPPORTED, EPERM) \ - X(ERROR_NO_MORE_FILES, ENOENT) \ - X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ - X(ERROR_OPERATION_ABORTED, EINTR) \ - X(ERROR_OUT_OF_PAPER, EACCES) \ - X(ERROR_PAGED_SYSTEM_RESOURCES, ENOMEM) \ - X(ERROR_PAGEFILE_QUOTA, ENOMEM) \ - X(ERROR_PATH_NOT_FOUND, ENOENT) \ - X(ERROR_PIPE_NOT_CONNECTED, EPIPE) \ - X(ERROR_PORT_UNREACHABLE, ECONNRESET) \ - X(ERROR_PROTOCOL_UNREACHABLE, ENETUNREACH) \ - X(ERROR_REM_NOT_LIST, ECONNREFUSED) \ - X(ERROR_REQUEST_ABORTED, EINTR) \ - X(ERROR_REQ_NOT_ACCEP, EWOULDBLOCK) \ - X(ERROR_SECTOR_NOT_FOUND, EACCES) \ - X(ERROR_SEM_TIMEOUT, ETIMEDOUT) \ - X(ERROR_SHARING_VIOLATION, EACCES) \ - X(ERROR_TOO_MANY_NAMES, ENOMEM) \ - X(ERROR_TOO_MANY_OPEN_FILES, EMFILE) \ - X(ERROR_UNEXP_NET_ERR, ECONNABORTED) \ - X(ERROR_WAIT_NO_CHILDREN, ECHILD) \ - X(ERROR_WORKING_SET_QUOTA, ENOMEM) \ - X(ERROR_WRITE_PROTECT, EACCES) \ - X(ERROR_WRONG_DISK, EACCES) \ - X(WSAEACCES, EACCES) \ - X(WSAEADDRINUSE, EADDRINUSE) \ - X(WSAEADDRNOTAVAIL, EADDRNOTAVAIL) \ - X(WSAEAFNOSUPPORT, EAFNOSUPPORT) \ - X(WSAECONNABORTED, ECONNABORTED) \ - X(WSAECONNREFUSED, ECONNREFUSED) \ - X(WSAECONNRESET, ECONNRESET) \ - X(WSAEDISCON, EPIPE) \ - X(WSAEFAULT, EFAULT) \ - X(WSAEHOSTDOWN, EHOSTUNREACH) \ - X(WSAEHOSTUNREACH, EHOSTUNREACH) \ - X(WSAEINTR, EINTR) \ - X(WSAEINVAL, EINVAL) \ - X(WSAEISCONN, EISCONN) \ - X(WSAEMSGSIZE, EMSGSIZE) \ - X(WSAENETDOWN, ENETDOWN) \ - X(WSAENETRESET, EHOSTUNREACH) \ - X(WSAENETUNREACH, ENETUNREACH) \ - X(WSAENOBUFS, ENOMEM) \ - X(WSAENOTCONN, ENOTCONN) \ - X(WSAENOTSOCK, ENOTSOCK) \ - X(WSAEOPNOTSUPP, EOPNOTSUPP) \ - X(WSAESHUTDOWN, EPIPE) \ - X(WSAETIMEDOUT, ETIMEDOUT) \ +#define _ERROR_ERRNO_MAP(X) \ + X(ERROR_ACCESS_DENIED, EACCES) \ + X(ERROR_ALREADY_EXISTS, EEXIST) \ + X(ERROR_BAD_COMMAND, EACCES) \ + X(ERROR_BAD_EXE_FORMAT, ENOEXEC) \ + X(ERROR_BAD_LENGTH, EACCES) \ + X(ERROR_BAD_NETPATH, ENOENT) \ + X(ERROR_BAD_NET_NAME, ENOENT) \ + X(ERROR_BAD_NET_RESP, ENETDOWN) \ + X(ERROR_BAD_PATHNAME, ENOENT) \ + X(ERROR_BROKEN_PIPE, EPIPE) \ + X(ERROR_CANNOT_MAKE, EACCES) \ + X(ERROR_COMMITMENT_LIMIT, ENOMEM) \ + X(ERROR_CONNECTION_ABORTED, ECONNABORTED) \ + X(ERROR_CONNECTION_ACTIVE, EISCONN) \ + X(ERROR_CONNECTION_REFUSED, ECONNREFUSED) \ + X(ERROR_CRC, EACCES) \ + X(ERROR_DEVICE_FEATURE_NOT_SUPPORTED, EPERM) \ + X(ERROR_DIR_NOT_EMPTY, ENOTEMPTY) \ + X(ERROR_DISK_FULL, ENOSPC) \ + X(ERROR_DUP_NAME, EADDRINUSE) \ + X(ERROR_FILENAME_EXCED_RANGE, ENOENT) \ + X(ERROR_FILE_NOT_FOUND, ENOENT) \ + X(ERROR_GEN_FAILURE, EACCES) \ + X(ERROR_GRACEFUL_DISCONNECT, EPIPE) \ + X(ERROR_HOST_DOWN, EHOSTUNREACH) \ + X(ERROR_HOST_UNREACHABLE, EHOSTUNREACH) \ + X(ERROR_INSUFFICIENT_BUFFER, EFAULT) \ + X(ERROR_INVALID_ADDRESS, EADDRNOTAVAIL) \ + X(ERROR_INVALID_FUNCTION, EINVAL) \ + X(ERROR_INVALID_HANDLE, EBADF) \ + X(ERROR_INVALID_NETNAME, EADDRNOTAVAIL) \ + X(ERROR_INVALID_PARAMETER, EINVAL) \ + X(ERROR_INVALID_USER_BUFFER, EMSGSIZE) \ + X(ERROR_IO_PENDING, EINPROGRESS) \ + X(ERROR_LOCK_VIOLATION, EACCES) \ + X(ERROR_MORE_DATA, EMSGSIZE) \ + X(ERROR_NETNAME_DELETED, ECONNABORTED) \ + X(ERROR_NETWORK_ACCESS_DENIED, EACCES) \ + X(ERROR_NETWORK_BUSY, ENETDOWN) \ + X(ERROR_NETWORK_UNREACHABLE, ENETUNREACH) \ + X(ERROR_NOACCESS, EFAULT) \ + X(ERROR_NONPAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_NOT_ENOUGH_MEMORY, ENOMEM) \ + X(ERROR_NOT_ENOUGH_QUOTA, ENOMEM) \ + X(ERROR_NOT_FOUND, ENOENT) \ + X(ERROR_NOT_LOCKED, EACCES) \ + X(ERROR_NOT_READY, EACCES) \ + X(ERROR_NOT_SAME_DEVICE, EXDEV) \ + X(ERROR_NOT_SUPPORTED, ENOTSUP) \ + X(ERROR_NO_MORE_FILES, ENOENT) \ + X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_OPERATION_ABORTED, EINTR) \ + X(ERROR_OUT_OF_PAPER, EACCES) \ + X(ERROR_PAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_PAGEFILE_QUOTA, ENOMEM) \ + X(ERROR_PATH_NOT_FOUND, ENOENT) \ + X(ERROR_PIPE_NOT_CONNECTED, EPIPE) \ + X(ERROR_PORT_UNREACHABLE, ECONNRESET) \ + X(ERROR_PROTOCOL_UNREACHABLE, ENETUNREACH) \ + X(ERROR_REM_NOT_LIST, ECONNREFUSED) \ + X(ERROR_REQUEST_ABORTED, EINTR) \ + X(ERROR_REQ_NOT_ACCEP, EWOULDBLOCK) \ + X(ERROR_SECTOR_NOT_FOUND, EACCES) \ + X(ERROR_SEM_TIMEOUT, ETIMEDOUT) \ + X(ERROR_SHARING_VIOLATION, EACCES) \ + X(ERROR_TOO_MANY_NAMES, ENOMEM) \ + X(ERROR_TOO_MANY_OPEN_FILES, EMFILE) \ + X(ERROR_UNEXP_NET_ERR, ECONNABORTED) \ + X(ERROR_WAIT_NO_CHILDREN, ECHILD) \ + X(ERROR_WORKING_SET_QUOTA, ENOMEM) \ + X(ERROR_WRITE_PROTECT, EACCES) \ + X(ERROR_WRONG_DISK, EACCES) \ + X(WSAEACCES, EACCES) \ + X(WSAEADDRINUSE, EADDRINUSE) \ + X(WSAEADDRNOTAVAIL, EADDRNOTAVAIL) \ + X(WSAEAFNOSUPPORT, EAFNOSUPPORT) \ + X(WSAECONNABORTED, ECONNABORTED) \ + X(WSAECONNREFUSED, ECONNREFUSED) \ + X(WSAECONNRESET, ECONNRESET) \ + X(WSAEDISCON, EPIPE) \ + X(WSAEFAULT, EFAULT) \ + X(WSAEHOSTDOWN, EHOSTUNREACH) \ + X(WSAEHOSTUNREACH, EHOSTUNREACH) \ + X(WSAEINTR, EINTR) \ + X(WSAEINVAL, EINVAL) \ + X(WSAEISCONN, EISCONN) \ + X(WSAEMSGSIZE, EMSGSIZE) \ + X(WSAENETDOWN, ENETDOWN) \ + X(WSAENETRESET, EHOSTUNREACH) \ + X(WSAENETUNREACH, ENETUNREACH) \ + X(WSAENOBUFS, ENOMEM) \ + X(WSAENOTCONN, ENOTCONN) \ + X(WSAENOTSOCK, ENOTSOCK) \ + X(WSAEOPNOTSUPP, EOPNOTSUPP) \ + X(WSAESHUTDOWN, EPIPE) \ + X(WSAETIMEDOUT, ETIMEDOUT) \ X(WSAEWOULDBLOCK, EWOULDBLOCK) errno_t err_map_win_error_to_errno(DWORD error) { From 097e4fef9a8be358aaa08fef7b64c4b8ab0b952b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 19:07:01 +0100 Subject: [PATCH 24/25] port: epoll_ctl() to report EBADF when both `op` and `sock` are invalid --- src/port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/port.c b/src/port.c index 64dabc1..f0631b7 100644 --- a/src/port.c +++ b/src/port.c @@ -320,7 +320,7 @@ static int _ep_port_ctl_op(ep_port_t* port_info, case EPOLL_CTL_DEL: return _ep_port_ctl_del(port_info, sock); default: - return_error(-1, ERROR_INVALID_PARAMETER); + return_handle_error(-1, sock, ERROR_INVALID_PARAMETER); } } From 24ae64e042cf5b106b26c2c8e81550b06f63a518 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 7 Dec 2017 19:09:35 +0100 Subject: [PATCH 25/25] test: add more test cases to test-error --- test/test-error.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/test-error.c b/test/test-error.c index 28462dc..b15cff9 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -52,8 +52,8 @@ int main(void) { { /* Test epoll_ctl() errors. */ - /* TODO: incomplete. */ HANDLE valid_ephnd; + SOCKET sock_valid; SOCKET sock_bad = (SOCKET) 0xbadbad; SOCKET sock_nonsock = (SOCKET) bad_type; struct epoll_event ev; @@ -62,9 +62,22 @@ int main(void) { valid_ephnd = epoll_create1(0); check(valid_ephnd != NULL); + sock_valid = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + check(sock_valid != INVALID_SOCKET); + ev.data.u64 = 0; ev.events = 0; + /* Invalid `ephnd` */ + r = epoll_ctl(NULL, EPOLL_CTL_ADD, sock_valid, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(INVALID_HANDLE_VALUE, EPOLL_CTL_ADD, sock_valid, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(bad_value, EPOLL_CTL_ADD, sock_valid, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(bad_type, EPOLL_CTL_ADD, sock_valid, &ev); + check_error(r < 0, EINVAL, ERROR_INVALID_PARAMETER); + /* Invalid `sock` */ r = epoll_ctl(valid_ephnd, EPOLL_CTL_ADD, 0, &ev); check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); @@ -75,6 +88,23 @@ int main(void) { r = epoll_ctl(valid_ephnd, EPOLL_CTL_ADD, sock_nonsock, &ev); check_error(r < 0, ENOTSOCK, WSAENOTSOCK); + /* Invalid `op` */ + r = epoll_ctl(valid_ephnd, -1, sock_valid, &ev); + check_error(r < 0, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_ctl(valid_ephnd, 0, sock_valid, &ev); + check_error(r < 0, EINVAL, ERROR_INVALID_PARAMETER); + r = epoll_ctl(valid_ephnd, 4, sock_valid, &ev); + check_error(r < 0, EINVAL, ERROR_INVALID_PARAMETER); + + /* Precedence - `EBADF` before `EINVAL`. */ + r = epoll_ctl(INVALID_HANDLE_VALUE, -1, sock_valid, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + r = epoll_ctl(valid_ephnd, -1, INVALID_SOCKET, &ev); + check_error(r < 0, EBADF, ERROR_INVALID_HANDLE); + /* TODO: bad `ephnd` type with invalid `sock`. */ + + r = closesocket(sock_valid); + check(r == 0); r = epoll_close(valid_ephnd); check(r == 0); }