wepoll/src/poll-request.c
2017-09-10 20:39:42 +02:00

152 lines
4.4 KiB
C

#include <assert.h>
#include <malloc.h>
#include <stdbool.h>
#include <stdint.h>
#include "afd.h"
#include "epoll-socket.h"
#include "epoll.h"
#include "error.h"
#include "poll-request.h"
#include "util.h"
#include "win.h"
typedef struct poll_req {
OVERLAPPED overlapped;
AFD_POLL_INFO poll_info;
ep_sock_t* sock_info;
} poll_req_t;
static inline poll_req_t* _poll_req_alloc(void) {
poll_req_t* poll_req = malloc(sizeof *poll_req);
if (poll_req == NULL)
return_error(NULL, ERROR_NOT_ENOUGH_MEMORY);
return poll_req;
}
static inline poll_req_t* _poll_req_free(poll_req_t* poll_req) {
free(poll_req);
return NULL;
}
poll_req_t* poll_req_new(_ep_port_data_t* port_data, ep_sock_t* sock_info) {
poll_req_t* poll_req = _poll_req_alloc();
if (poll_req == NULL)
return NULL;
memset(poll_req, 0, sizeof *poll_req);
poll_req->sock_info = sock_info;
ep_sock_register_poll_req(port_data, sock_info);
return poll_req;
}
void poll_req_delete(_ep_port_data_t* port_data,
ep_sock_t* sock_info,
poll_req_t* poll_req) {
assert(poll_req != NULL);
ep_sock_unregister_poll_req(port_data, sock_info);
_poll_req_free(poll_req);
}
poll_req_t* overlapped_to_poll_req(OVERLAPPED* overlapped) {
return container_of(overlapped, poll_req_t, overlapped);
}
ep_sock_t* poll_req_get_sock_data(const poll_req_t* poll_req) {
return poll_req->sock_info;
}
static DWORD _epoll_events_to_afd_events(uint32_t epoll_events) {
DWORD afd_events;
/* These events should always be monitored. */
assert(epoll_events & EPOLLERR);
assert(epoll_events & EPOLLHUP);
afd_events = AFD_POLL_ABORT | AFD_POLL_CONNECT_FAIL | AFD_POLL_LOCAL_CLOSE;
if (epoll_events & (EPOLLIN | EPOLLRDNORM))
afd_events |= AFD_POLL_RECEIVE | AFD_POLL_ACCEPT;
if (epoll_events & (EPOLLPRI | EPOLLRDBAND))
afd_events |= AFD_POLL_RECEIVE_EXPEDITED;
if (epoll_events & (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND))
afd_events |= AFD_POLL_SEND | AFD_POLL_CONNECT;
return afd_events;
}
static uint32_t _afd_events_to_epoll_events(DWORD afd_events) {
uint32_t epoll_events = 0;
if (afd_events & (AFD_POLL_RECEIVE | AFD_POLL_ACCEPT))
epoll_events |= EPOLLIN | EPOLLRDNORM;
if (afd_events & AFD_POLL_RECEIVE_EXPEDITED)
epoll_events |= EPOLLPRI | EPOLLRDBAND;
if (afd_events & AFD_POLL_SEND)
epoll_events |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
if ((afd_events & AFD_POLL_DISCONNECT) && !(afd_events & AFD_POLL_ABORT))
epoll_events |= EPOLLIN | EPOLLRDHUP;
if (afd_events & AFD_POLL_ABORT)
epoll_events |= EPOLLHUP;
if (afd_events & AFD_POLL_CONNECT)
epoll_events |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
if (afd_events & AFD_POLL_CONNECT_FAIL)
epoll_events |= EPOLLERR;
return epoll_events;
}
int poll_req_submit(poll_req_t* poll_req,
uint32_t epoll_events,
SOCKET socket,
SOCKET driver_socket) {
int r;
memset(&poll_req->overlapped, 0, sizeof poll_req->overlapped);
poll_req->poll_info.Exclusive = TRUE;
poll_req->poll_info.NumberOfHandles = 1;
poll_req->poll_info.Timeout.QuadPart = INT64_MAX;
poll_req->poll_info.Handles[0].Handle = (HANDLE) socket;
poll_req->poll_info.Handles[0].Status = 0;
poll_req->poll_info.Handles[0].Events =
_epoll_events_to_afd_events(epoll_events);
r = afd_poll(driver_socket, &poll_req->poll_info, &poll_req->overlapped);
if (r != 0 && GetLastError() != ERROR_IO_PENDING)
return_error(-1);
return 0;
}
void poll_req_complete(const poll_req_t* poll_req,
uint32_t* epoll_events_out,
bool* socket_closed_out) {
const OVERLAPPED* overlapped = &poll_req->overlapped;
uint32_t epoll_events = 0;
bool socket_closed = false;
if (!NT_SUCCESS(overlapped->Internal)) {
/* The overlapped request itself failed, there are no events to consider.
*/
epoll_events = EPOLLERR;
} else if (poll_req->poll_info.NumberOfHandles < 1) {
/* This overlapped request succeeded but didn't report any events. */
} else {
/* Events related to our socket were reported. */
DWORD afd_events = poll_req->poll_info.Handles[0].Events;
if (afd_events & AFD_POLL_LOCAL_CLOSE)
socket_closed = true; /* Socket closed locally be silently dropped. */
else
epoll_events = _afd_events_to_epoll_events(afd_events);
}
*epoll_events_out = epoll_events;
*socket_closed_out = socket_closed;
}