version 1.1.0

This commit is contained in:
Bert Belder 2017-12-04 00:06:28 +01:00
commit c751fb935f
3 changed files with 159 additions and 141 deletions

View File

@ -1,18 +1,16 @@
# wepoll - epoll for windows # wepoll - epoll for windows
This library implements the This library implements the [epoll][man epoll] API for Windows
[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html) API for applications. It attempts to be efficient, and to match the Linux API
Windows applications. It attempts to be efficient, and to match the as semantics as closely as possible.
Linux API and as closely as possible.
## Rationale ## Rationale
Unlike Linux, OS X, and many other operating systems, Windows doesn't Unlike Linux, OS X, and many other operating systems, Windows doesn't
have a good API for receiving socket state notifications. It only have a good API for receiving socket state notifications. It only
supports the `select` and `WSAPoll` APIs, but they supports the `select` and `WSAPoll` APIs, but they
[don't scale](https://daniel.haxx.se/docs/poll-vs-select.html) [don't scale][select scale] and suffer from
and suffer from [other issues][wsapoll broken].
[other issues](https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/).
Using I/O completion ports isn't always practical when software is Using I/O completion ports isn't always practical when software is
designed to be cross-platform. Wepoll offers an alternative that is designed to be cross-platform. Wepoll offers an alternative that is
@ -36,9 +34,10 @@ to run on Linux.
## How to use ## How to use
The library is distributed as a single source file (wepoll.c) and a The library is [distributed][dist] as a single source file
single header file (wepoll.h). Compile the .c file as part of your ([wepoll.c][wepoll.c]) and a single header file ([wepoll.h][wepoll.h]).
project, and include the header wherever needed. Compile the .c file as part of your project, and include the header
wherever needed.
## Compatibility ## Compatibility
@ -61,10 +60,10 @@ HANDLE epoll_create1(int flags);
``` ```
* Create a new epoll instance (port). * Create a new epoll instance (port).
* `size` is ignored but most be nonzero. * `size` is ignored but most be greater than zero.
* `flags` must be zero as there are no supported flags. * `flags` must be zero as there are no supported flags.
* Returns `INVALID_HANDLE_VALUE` on failure. * Returns `INVALID_HANDLE_VALUE` on failure.
* [man page](http://man7.org/linux/man-pages/man2/epoll_create.2.html) * [man page][man epoll_create]
### epoll_close ### epoll_close
@ -94,7 +93,7 @@ int epoll_ctl(HANDLE ephnd,
wepoll may not be able to detect this until the next call to wepoll may not be able to detect this until the next call to
`epoll_wait()`. `epoll_wait()`.
* TODO: expand * TODO: expand
* [man page](http://man7.org/linux/man-pages/man2/epoll_ctl.2.html) * [man page][man epoll_ctl]
### epoll_wait ### epoll_wait
@ -108,8 +107,17 @@ int epoll_wait(HANDLE ephnd,
* Receive socket events from an epoll port. * Receive socket events from an epoll port.
* Returns * Returns
- -1 on failure - -1 on failure
- 0 when a timeout occurs - 0 when a timeout occurs
- \>0 the number of evens received - ≥1 the number of evens received
* TODO: expand * TODO: expand
* [man page](http://man7.org/linux/man-pages/man2/epoll_wait.2.html) * [man page][man epoll_wait]
[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
[man epoll_ctl]: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
[man epoll_wait]: http://man7.org/linux/man-pages/man2/epoll_wait.2.html
[select scale]: https://daniel.haxx.se/docs/poll-vs-select.html
[wsapoll broken]: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
[wepoll.c]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.c
[wepoll.h]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.h

206
wepoll.c
View File

@ -35,38 +35,42 @@
#include <stdint.h> #include <stdint.h>
/* clang-format off */
enum EPOLL_EVENTS { enum EPOLL_EVENTS {
EPOLLIN = 1 << 0, EPOLLIN = 1 << 0,
EPOLLPRI = 1 << 1, EPOLLPRI = 1 << 1,
EPOLLOUT = 1 << 2, EPOLLOUT = 1 << 2,
EPOLLERR = 1 << 3, EPOLLERR = 1 << 3,
EPOLLHUP = 1 << 4, EPOLLHUP = 1 << 4,
EPOLLRDNORM = 1 << 6, EPOLLRDNORM = 1 << 6,
EPOLLRDBAND = 1 << 7, EPOLLRDBAND = 1 << 7,
EPOLLWRNORM = 1 << 8, EPOLLWRNORM = 1 << 8,
EPOLLWRBAND = 1 << 9, EPOLLWRBAND = 1 << 9,
EPOLLMSG = 1 << 10, EPOLLMSG = 1 << 10, /* Never reported. */
EPOLLRDHUP = 1 << 13, EPOLLRDHUP = 1 << 13,
EPOLLONESHOT = 1 << 31 EPOLLONESHOT = 1 << 31
}; };
#define EPOLLIN EPOLLIN #define EPOLLIN ((uint32_t) EPOLLIN)
#define EPOLLPRI EPOLLPRI #define EPOLLPRI ((uint32_t) EPOLLPRI)
#define EPOLLOUT EPOLLOUT #define EPOLLOUT ((uint32_t) EPOLLOUT)
#define EPOLLERR EPOLLERR #define EPOLLERR ((uint32_t) EPOLLERR)
#define EPOLLHUP EPOLLHUP #define EPOLLHUP ((uint32_t) EPOLLHUP)
#define EPOLLRDNORM EPOLLRDNORM #define EPOLLRDNORM ((uint32_t) EPOLLRDNORM)
#define EPOLLRDBAND EPOLLRDBAND #define EPOLLRDBAND ((uint32_t) EPOLLRDBAND)
#define EPOLLWRNORM EPOLLWRNORM #define EPOLLWRNORM ((uint32_t) EPOLLWRNORM)
#define EPOLLWRBAND EPOLLWRBAND #define EPOLLWRBAND ((uint32_t) EPOLLWRBAND)
#define EPOLLMSG EPOLLMSG #define EPOLLMSG ((uint32_t) EPOLLMSG)
#define EPOLLRDHUP EPOLLRDHUP #define EPOLLRDHUP ((uint32_t) EPOLLRDHUP)
#define EPOLLONESHOT EPOLLONESHOT #define EPOLLONESHOT ((uint32_t) EPOLLONESHOT)
#define EPOLL_CTL_ADD 1 #define EPOLL_CTL_ADD 1
#define EPOLL_CTL_MOD 2 #define EPOLL_CTL_MOD 2
#define EPOLL_CTL_DEL 3 #define EPOLL_CTL_DEL 3
/* clang-format on */
typedef void* HANDLE; typedef void* HANDLE;
typedef uintptr_t SOCKET; typedef uintptr_t SOCKET;
@ -75,8 +79,8 @@ typedef union epoll_data {
int fd; int fd;
uint32_t u32; uint32_t u32;
uint64_t u64; uint64_t u64;
SOCKET sock; SOCKET sock; /* Windows specific */
HANDLE hnd; HANDLE hnd; /* Windows specific */
} epoll_data_t; } epoll_data_t;
struct epoll_event { struct epoll_event {
@ -118,6 +122,8 @@ WEPOLL_EXPORT int epoll_wait(HANDLE ephnd,
#include <WinSock2.h> #include <WinSock2.h>
#include <Windows.h> #include <Windows.h>
WEPOLL_INTERNAL int nt_global_init(void);
#ifndef _NTDEF_ #ifndef _NTDEF_
typedef LONG NTSTATUS; typedef LONG NTSTATUS;
typedef NTSTATUS* PNTSTATUS; typedef NTSTATUS* PNTSTATUS;
@ -187,6 +193,73 @@ typedef NTSTATUS* PNTSTATUS;
#define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) #define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L)
#endif #endif
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG Reserved);
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
#define NTDLL_IMPORT_LIST(X) \
X(NTSTATUS, \
NTAPI, \
NtDeviceIoControlFile, \
(HANDLE FileHandle, \
HANDLE Event, \
PIO_APC_ROUTINE ApcRoutine, \
PVOID ApcContext, \
PIO_STATUS_BLOCK IoStatusBlock, \
ULONG IoControlCode, \
PVOID InputBuffer, \
ULONG InputBufferLength, \
PVOID OutputBuffer, \
ULONG OutputBufferLength)) \
\
X(ULONG, WINAPI, RtlNtStatusToDosError, (NTSTATUS Status)) \
\
X(NTSTATUS, \
NTAPI, \
NtCreateKeyedEvent, \
(PHANDLE handle, \
ACCESS_MASK access, \
POBJECT_ATTRIBUTES attr, \
ULONG flags)) \
\
X(NTSTATUS, \
NTAPI, \
NtWaitForKeyedEvent, \
(HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout)) \
\
X(NTSTATUS, \
NTAPI, \
NtReleaseKeyedEvent, \
(HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout))
#define X(return_type, attributes, name, parameters) \
WEPOLL_INTERNAL_EXTERN return_type(attributes* name) parameters;
NTDLL_IMPORT_LIST(X)
#undef X
#include <stddef.h> #include <stddef.h>
#ifndef _SSIZE_T_DEFINED #ifndef _SSIZE_T_DEFINED
@ -297,75 +370,6 @@ static const GUID AFD_PROVIDER_GUID_LIST[] = {
WEPOLL_INTERNAL errno_t err_map_win_error_to_errno(DWORD error); WEPOLL_INTERNAL errno_t err_map_win_error_to_errno(DWORD error);
WEPOLL_INTERNAL void err_set_win_error(DWORD error); WEPOLL_INTERNAL void err_set_win_error(DWORD error);
WEPOLL_INTERNAL int nt_global_init(void);
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG Reserved);
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
#define NTDLL_IMPORT_LIST(X) \
X(NTSTATUS, \
NTAPI, \
NtDeviceIoControlFile, \
(HANDLE FileHandle, \
HANDLE Event, \
PIO_APC_ROUTINE ApcRoutine, \
PVOID ApcContext, \
PIO_STATUS_BLOCK IoStatusBlock, \
ULONG IoControlCode, \
PVOID InputBuffer, \
ULONG InputBufferLength, \
PVOID OutputBuffer, \
ULONG OutputBufferLength)) \
\
X(ULONG, WINAPI, RtlNtStatusToDosError, (NTSTATUS Status)) \
\
X(NTSTATUS, \
NTAPI, \
NtCreateKeyedEvent, \
(PHANDLE handle, \
ACCESS_MASK access, \
POBJECT_ATTRIBUTES attr, \
ULONG flags)) \
\
X(NTSTATUS, \
NTAPI, \
NtWaitForKeyedEvent, \
(HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout)) \
\
X(NTSTATUS, \
NTAPI, \
NtReleaseKeyedEvent, \
(HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout))
#define X(return_type, attributes, name, parameters) \
WEPOLL_INTERNAL_EXTERN return_type(attributes* name) parameters;
NTDLL_IMPORT_LIST(X)
#undef X
#define FILE_DEVICE_NETWORK 0x00000012 #define FILE_DEVICE_NETWORK 0x00000012
#define METHOD_BUFFERED 0 #define METHOD_BUFFERED 0
#define AFD_POLL 9 #define AFD_POLL 9
@ -2073,7 +2077,7 @@ reflock_tree_node_t* reflock_tree_del_and_ref(reflock_tree_t* rlt,
AcquireSRWLockExclusive(&rlt->lock); AcquireSRWLockExclusive(&rlt->lock);
tree_node = tree_find(&rlt->tree, (uintptr_t) key); tree_node = tree_find(&rlt->tree, key);
rlt_node = safe_container_of(tree_node, reflock_tree_node_t, tree_node); rlt_node = safe_container_of(tree_node, reflock_tree_node_t, tree_node);
if (rlt_node != NULL) { if (rlt_node != NULL) {
@ -2093,7 +2097,7 @@ reflock_tree_node_t* reflock_tree_find_and_ref(reflock_tree_t* rlt,
AcquireSRWLockShared(&rlt->lock); AcquireSRWLockShared(&rlt->lock);
tree_node = tree_find(&rlt->tree, (uintptr_t) key); tree_node = tree_find(&rlt->tree, key);
rlt_node = safe_container_of(tree_node, reflock_tree_node_t, tree_node); rlt_node = safe_container_of(tree_node, reflock_tree_node_t, tree_node);
if (rlt_node != NULL) if (rlt_node != NULL)
reflock_ref(&rlt_node->reflock); reflock_ref(&rlt_node->reflock);
@ -2199,7 +2203,9 @@ void reflock_unref_and_destroy(reflock_t* reflock) {
assert(state == _DESTROY); assert(state == _DESTROY);
} }
#define _EP_EVENT_MASK 0xffff #define _KNOWN_EPOLL_EVENTS \
(EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | \
EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLRDHUP)
typedef struct _poll_req { typedef struct _poll_req {
OVERLAPPED overlapped; OVERLAPPED overlapped;
@ -2433,7 +2439,7 @@ int ep_sock_set_event(ep_port_t* port_info,
sock_private->user_events = events; sock_private->user_events = events;
sock_private->user_data = ev->data; sock_private->user_data = ev->data;
if ((events & _EP_EVENT_MASK & ~(sock_private->pending_events)) != 0) if ((events & _KNOWN_EPOLL_EVENTS & ~sock_private->pending_events) != 0)
ep_port_request_socket_update(port_info, sock_info); ep_port_request_socket_update(port_info, sock_info);
return 0; return 0;
@ -2446,8 +2452,8 @@ int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) {
assert(ep_port_is_socket_update_pending(port_info, sock_info)); assert(ep_port_is_socket_update_pending(port_info, sock_info));
if (sock_private->poll_status == _POLL_PENDING && if ((sock_private->poll_status == _POLL_PENDING) &&
(sock_private->user_events & _EP_EVENT_MASK & (sock_private->user_events & _KNOWN_EPOLL_EVENTS &
~sock_private->pending_events) == 0) { ~sock_private->pending_events) == 0) {
/* All the events the user is interested in are already being monitored /* All the events the user is interested in are already being monitored
* by the pending poll request. It might spuriously complete because of an * by the pending poll request. It might spuriously complete because of an

View File

@ -38,38 +38,42 @@
#include <stdint.h> #include <stdint.h>
/* clang-format off */
enum EPOLL_EVENTS { enum EPOLL_EVENTS {
EPOLLIN = 1 << 0, EPOLLIN = 1 << 0,
EPOLLPRI = 1 << 1, EPOLLPRI = 1 << 1,
EPOLLOUT = 1 << 2, EPOLLOUT = 1 << 2,
EPOLLERR = 1 << 3, EPOLLERR = 1 << 3,
EPOLLHUP = 1 << 4, EPOLLHUP = 1 << 4,
EPOLLRDNORM = 1 << 6, EPOLLRDNORM = 1 << 6,
EPOLLRDBAND = 1 << 7, EPOLLRDBAND = 1 << 7,
EPOLLWRNORM = 1 << 8, EPOLLWRNORM = 1 << 8,
EPOLLWRBAND = 1 << 9, EPOLLWRBAND = 1 << 9,
EPOLLMSG = 1 << 10, EPOLLMSG = 1 << 10, /* Never reported. */
EPOLLRDHUP = 1 << 13, EPOLLRDHUP = 1 << 13,
EPOLLONESHOT = 1 << 31 EPOLLONESHOT = 1 << 31
}; };
#define EPOLLIN EPOLLIN #define EPOLLIN ((uint32_t) EPOLLIN)
#define EPOLLPRI EPOLLPRI #define EPOLLPRI ((uint32_t) EPOLLPRI)
#define EPOLLOUT EPOLLOUT #define EPOLLOUT ((uint32_t) EPOLLOUT)
#define EPOLLERR EPOLLERR #define EPOLLERR ((uint32_t) EPOLLERR)
#define EPOLLHUP EPOLLHUP #define EPOLLHUP ((uint32_t) EPOLLHUP)
#define EPOLLRDNORM EPOLLRDNORM #define EPOLLRDNORM ((uint32_t) EPOLLRDNORM)
#define EPOLLRDBAND EPOLLRDBAND #define EPOLLRDBAND ((uint32_t) EPOLLRDBAND)
#define EPOLLWRNORM EPOLLWRNORM #define EPOLLWRNORM ((uint32_t) EPOLLWRNORM)
#define EPOLLWRBAND EPOLLWRBAND #define EPOLLWRBAND ((uint32_t) EPOLLWRBAND)
#define EPOLLMSG EPOLLMSG #define EPOLLMSG ((uint32_t) EPOLLMSG)
#define EPOLLRDHUP EPOLLRDHUP #define EPOLLRDHUP ((uint32_t) EPOLLRDHUP)
#define EPOLLONESHOT EPOLLONESHOT #define EPOLLONESHOT ((uint32_t) EPOLLONESHOT)
#define EPOLL_CTL_ADD 1 #define EPOLL_CTL_ADD 1
#define EPOLL_CTL_MOD 2 #define EPOLL_CTL_MOD 2
#define EPOLL_CTL_DEL 3 #define EPOLL_CTL_DEL 3
/* clang-format on */
typedef void* HANDLE; typedef void* HANDLE;
typedef uintptr_t SOCKET; typedef uintptr_t SOCKET;
@ -78,8 +82,8 @@ typedef union epoll_data {
int fd; int fd;
uint32_t u32; uint32_t u32;
uint64_t u64; uint64_t u64;
SOCKET sock; SOCKET sock; /* Windows specific */
HANDLE hnd; HANDLE hnd; /* Windows specific */
} epoll_data_t; } epoll_data_t;
struct epoll_event { struct epoll_event {