epoll: split up epoll_ctl() implementation

This commit is contained in:
Bert Belder 2017-09-04 09:33:16 +02:00
parent 7baf2c9e73
commit 65d44c9d7e

View File

@ -117,167 +117,169 @@ epoll_t epoll_create(void) {
return (epoll_t) port_data;
}
int _ep_ctl_add(_ep_port_data_t* port_data,
uintptr_t sock,
struct epoll_event* ev) {
_ep_sock_data_t* sock_data;
_ep_io_req_t* io_req;
SOCKET base_sock, peer_sock;
WSAPROTOCOL_INFOW protocol_info;
DWORD bytes;
int len;
/* Try to obtain a base handle for the socket, so we can bypass LSPs
* that get in the way if we want to talk to the kernel directly. If
* it fails we try if we work with the original socket. Note that on
* windows XP/2k3 this will always fail since they don't support the
* SIO_BASE_HANDLE ioctl.
*/
base_sock = sock;
WSAIoctl(sock,
SIO_BASE_HANDLE,
NULL,
0,
&base_sock,
sizeof base_sock,
&bytes,
NULL,
NULL);
/* Obtain protocol information about the socket. */
len = sizeof protocol_info;
if (getsockopt(base_sock,
SOL_SOCKET,
SO_PROTOCOL_INFOW,
(char*) &protocol_info,
&len) != 0)
return_error(-1);
peer_sock = _ep_get_peer_socket(port_data, &protocol_info);
if (peer_sock == INVALID_SOCKET)
return -1;
sock_data = malloc(sizeof *sock_data);
if (sock_data == NULL)
return_error(-1, ERROR_NOT_ENOUGH_MEMORY);
io_req = malloc(sizeof *io_req);
if (io_req == NULL) {
free(sock_data);
return_error(-1, ERROR_NOT_ENOUGH_MEMORY);
}
sock_data->sock = sock;
sock_data->base_sock = base_sock;
sock_data->io_req_generation = 0;
sock_data->submitted_events = 0;
sock_data->registered_events = ev->events | EPOLLERR | EPOLLHUP;
sock_data->user_data = ev->data.u64;
sock_data->peer_sock = peer_sock;
sock_data->free_io_req = io_req;
sock_data->flags = 0;
if (RB_INSERT(_ep_sock_data_tree, &port_data->sock_data_tree, sock_data) !=
NULL) {
/* Socket was already added. */
free(sock_data);
free(io_req);
return_error(-1, ERROR_ALREADY_EXISTS);
}
/* Add to attention list */
ATTN_LIST_ADD(port_data, sock_data);
return_success(0);
}
int _ep_ctl_mod(_ep_port_data_t* port_data,
uintptr_t sock,
struct epoll_event* ev) {
_ep_sock_data_t lookup;
_ep_sock_data_t* sock_data;
lookup.sock = sock;
sock_data = RB_FIND(_ep_sock_data_tree, &port_data->sock_data_tree, &lookup);
if (sock_data == NULL)
return_error(-1, ERROR_NOT_FOUND); /* Socket not in epoll set. */
if (ev->events & _EP_EVENT_MASK & ~sock_data->submitted_events) {
if (sock_data->free_io_req == NULL) {
_ep_io_req_t* io_req = malloc(sizeof *io_req);
if (io_req == NULL)
return_error(-1, ERROR_NOT_ENOUGH_MEMORY);
sock_data->free_io_req = NULL;
}
/* Add to attention list, if not already added. */
if (!(sock_data->flags & _EP_SOCK_LISTED)) {
ATTN_LIST_ADD(port_data, sock_data);
}
}
sock_data->registered_events = ev->events | EPOLLERR | EPOLLHUP;
sock_data->user_data = ev->data.u64;
return_success(0);
}
int _ep_ctl_del(_ep_port_data_t* port_data, uintptr_t sock) {
_ep_sock_data_t lookup;
_ep_sock_data_t* sock_data;
lookup.sock = sock;
sock_data = RB_FIND(_ep_sock_data_tree, &port_data->sock_data_tree, &lookup);
if (sock_data == NULL)
/* Socket has not been registered with epoll instance. */
return_error(-1, ERROR_NOT_FOUND); /* Socket not in epoll set. */
RB_REMOVE(_ep_sock_data_tree, &port_data->sock_data_tree, sock_data);
free(sock_data->free_io_req);
sock_data->flags |= _EP_SOCK_DELETED;
/* Remove from attention list. */
if (sock_data->flags & _EP_SOCK_LISTED) {
if (sock_data->attn_list_prev != NULL)
sock_data->attn_list_prev->attn_list_next = sock_data->attn_list_next;
if (sock_data->attn_list_next != NULL)
sock_data->attn_list_next->attn_list_prev = sock_data->attn_list_prev;
if (port_data->attn_list == sock_data)
port_data->attn_list = sock_data->attn_list_next;
sock_data->attn_list_prev = NULL;
sock_data->attn_list_next = NULL;
sock_data->flags &= ~_EP_SOCK_LISTED;
}
if (sock_data->submitted_events == 0) {
assert(sock_data->io_req_generation == 0);
free(sock_data);
} else {
/* There are still one or more io requests pending. Wait for
* all pending requests to return before freeing.
*/
assert(sock_data->io_req_generation > 0);
}
return_success(0);
}
int epoll_ctl(epoll_t port_handle,
int op,
uintptr_t sock,
struct epoll_event* ev) {
_ep_port_data_t* port_data;
SOCKET base_sock;
port_data = (_ep_port_data_t*) port_handle;
_ep_port_data_t* port_data = (_ep_port_data_t*) port_handle;
switch (op) {
case EPOLL_CTL_ADD: {
_ep_sock_data_t* sock_data;
_ep_io_req_t* io_req;
SOCKET peer_sock;
WSAPROTOCOL_INFOW protocol_info;
DWORD bytes;
int len;
/* Try to obtain a base handle for the socket, so we can bypass LSPs
* that get in the way if we want to talk to the kernel directly. If
* it fails we try if we work with the original socket. Note that on
* windows XP/2k3 this will always fail since they don't support the
* SIO_BASE_HANDLE ioctl.
*/
base_sock = sock;
WSAIoctl(sock,
SIO_BASE_HANDLE,
NULL,
0,
&base_sock,
sizeof base_sock,
&bytes,
NULL,
NULL);
/* Obtain protocol information about the socket. */
len = sizeof protocol_info;
if (getsockopt(base_sock,
SOL_SOCKET,
SO_PROTOCOL_INFOW,
(char*) &protocol_info,
&len) != 0)
return_error(-1);
peer_sock = _ep_get_peer_socket(port_data, &protocol_info);
if (peer_sock == INVALID_SOCKET)
return -1;
sock_data = malloc(sizeof *sock_data);
if (sock_data == NULL)
return_error(-1, ERROR_NOT_ENOUGH_MEMORY);
io_req = malloc(sizeof *io_req);
if (io_req == NULL) {
free(sock_data);
return_error(-1, ERROR_NOT_ENOUGH_MEMORY);
}
sock_data->sock = sock;
sock_data->base_sock = base_sock;
sock_data->io_req_generation = 0;
sock_data->submitted_events = 0;
sock_data->registered_events = ev->events | EPOLLERR | EPOLLHUP;
sock_data->user_data = ev->data.u64;
sock_data->peer_sock = peer_sock;
sock_data->free_io_req = io_req;
sock_data->flags = 0;
if (RB_INSERT(_ep_sock_data_tree,
&port_data->sock_data_tree,
sock_data) != NULL) {
/* Socket was already added. */
free(sock_data);
free(io_req);
return_error(-1, ERROR_ALREADY_EXISTS);
}
/* Add to attention list */
ATTN_LIST_ADD(port_data, sock_data);
return_success(0);
}
case EPOLL_CTL_MOD: {
_ep_sock_data_t lookup;
_ep_sock_data_t* sock_data;
lookup.sock = sock;
sock_data =
RB_FIND(_ep_sock_data_tree, &port_data->sock_data_tree, &lookup);
if (sock_data == NULL)
return_error(-1, ERROR_NOT_FOUND); /* Socket not in epoll set. */
if (ev->events & _EP_EVENT_MASK & ~sock_data->submitted_events) {
if (sock_data->free_io_req == NULL) {
_ep_io_req_t* io_req = malloc(sizeof *io_req);
if (io_req == NULL)
return_error(-1, ERROR_NOT_ENOUGH_MEMORY);
sock_data->free_io_req = NULL;
}
/* Add to attention list, if not already added. */
if (!(sock_data->flags & _EP_SOCK_LISTED)) {
ATTN_LIST_ADD(port_data, sock_data);
}
}
sock_data->registered_events = ev->events | EPOLLERR | EPOLLHUP;
sock_data->user_data = ev->data.u64;
return_success(0);
}
case EPOLL_CTL_DEL: {
_ep_sock_data_t lookup;
_ep_sock_data_t* sock_data;
lookup.sock = sock;
sock_data =
RB_FIND(_ep_sock_data_tree, &port_data->sock_data_tree, &lookup);
if (sock_data == NULL)
/* Socket has not been registered with epoll instance. */
return_error(-1, ERROR_NOT_FOUND); /* Socket not in epoll set. */
RB_REMOVE(_ep_sock_data_tree, &port_data->sock_data_tree, sock_data);
free(sock_data->free_io_req);
sock_data->flags |= _EP_SOCK_DELETED;
/* Remove from attention list. */
if (sock_data->flags & _EP_SOCK_LISTED) {
if (sock_data->attn_list_prev != NULL)
sock_data->attn_list_prev->attn_list_next =
sock_data->attn_list_next;
if (sock_data->attn_list_next != NULL)
sock_data->attn_list_next->attn_list_prev =
sock_data->attn_list_prev;
if (port_data->attn_list == sock_data)
port_data->attn_list = sock_data->attn_list_next;
sock_data->attn_list_prev = NULL;
sock_data->attn_list_next = NULL;
sock_data->flags &= ~_EP_SOCK_LISTED;
}
if (sock_data->submitted_events == 0) {
assert(sock_data->io_req_generation == 0);
free(sock_data);
} else {
/* There are still one or more io requests pending. Wait for
* all pending requests to return before freeing.
*/
assert(sock_data->io_req_generation > 0);
}
return_success(0);
}
default:
return_error(-1, ERROR_INVALID_PARAMETER);
case EPOLL_CTL_ADD:
return _ep_ctl_add(port_data, sock, ev);
case EPOLL_CTL_MOD:
return _ep_ctl_mod(port_data, sock, ev);
case EPOLL_CTL_DEL:
return _ep_ctl_del(port_data, sock);
}
return_error(-1, ERROR_INVALID_PARAMETER);
}
int epoll_wait(epoll_t port_handle,