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
This library implements the
[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html) API for
Windows applications. It attempts to be efficient, and to match the
Linux API and as closely as possible.
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.
## Rationale
Unlike Linux, OS X, and many other operating systems, Windows doesn't
have a good API for receiving socket state notifications. It only
supports the `select` and `WSAPoll` APIs, but they
[don't scale](https://daniel.haxx.se/docs/poll-vs-select.html)
and suffer from
[other issues](https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/).
[don't scale][select scale] and suffer from
[other issues][wsapoll broken].
Using I/O completion ports isn't always practical when software is
designed to be cross-platform. Wepoll offers an alternative that is
@ -36,9 +34,10 @@ to run on Linux.
## How to use
The library is distributed as a single source file (wepoll.c) and a
single header file (wepoll.h). Compile the .c file as part of your
project, and include the header wherever needed.
The library is [distributed][dist] as a single source file
([wepoll.c][wepoll.c]) and a single header file ([wepoll.h][wepoll.h]).
Compile the .c file as part of your project, and include the header
wherever needed.
## Compatibility
@ -61,10 +60,10 @@ HANDLE epoll_create1(int flags);
```
* 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.
* 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
@ -94,7 +93,7 @@ int epoll_ctl(HANDLE ephnd,
wepoll may not be able to detect this until the next call to
`epoll_wait()`.
* TODO: expand
* [man page](http://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
* [man page][man epoll_ctl]
### epoll_wait
@ -108,8 +107,17 @@ int epoll_wait(HANDLE ephnd,
* Receive socket events from an epoll port.
* Returns
- -1 on failure
- 0 when a timeout occurs
- \>0 the number of evens received
- 0 when a timeout occurs
- ≥1 the number of evens received
* 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>
/* clang-format off */
enum EPOLL_EVENTS {
EPOLLIN = 1 << 0,
EPOLLPRI = 1 << 1,
EPOLLOUT = 1 << 2,
EPOLLERR = 1 << 3,
EPOLLHUP = 1 << 4,
EPOLLRDNORM = 1 << 6,
EPOLLRDBAND = 1 << 7,
EPOLLWRNORM = 1 << 8,
EPOLLWRBAND = 1 << 9,
EPOLLMSG = 1 << 10,
EPOLLRDHUP = 1 << 13,
EPOLLIN = 1 << 0,
EPOLLPRI = 1 << 1,
EPOLLOUT = 1 << 2,
EPOLLERR = 1 << 3,
EPOLLHUP = 1 << 4,
EPOLLRDNORM = 1 << 6,
EPOLLRDBAND = 1 << 7,
EPOLLWRNORM = 1 << 8,
EPOLLWRBAND = 1 << 9,
EPOLLMSG = 1 << 10, /* Never reported. */
EPOLLRDHUP = 1 << 13,
EPOLLONESHOT = 1 << 31
};
#define EPOLLIN EPOLLIN
#define EPOLLPRI EPOLLPRI
#define EPOLLOUT EPOLLOUT
#define EPOLLERR EPOLLERR
#define EPOLLHUP EPOLLHUP
#define EPOLLRDNORM EPOLLRDNORM
#define EPOLLRDBAND EPOLLRDBAND
#define EPOLLWRNORM EPOLLWRNORM
#define EPOLLWRBAND EPOLLWRBAND
#define EPOLLMSG EPOLLMSG
#define EPOLLRDHUP EPOLLRDHUP
#define EPOLLONESHOT EPOLLONESHOT
#define EPOLLIN ((uint32_t) EPOLLIN)
#define EPOLLPRI ((uint32_t) EPOLLPRI)
#define EPOLLOUT ((uint32_t) EPOLLOUT)
#define EPOLLERR ((uint32_t) EPOLLERR)
#define EPOLLHUP ((uint32_t) EPOLLHUP)
#define EPOLLRDNORM ((uint32_t) EPOLLRDNORM)
#define EPOLLRDBAND ((uint32_t) EPOLLRDBAND)
#define EPOLLWRNORM ((uint32_t) EPOLLWRNORM)
#define EPOLLWRBAND ((uint32_t) EPOLLWRBAND)
#define EPOLLMSG ((uint32_t) EPOLLMSG)
#define EPOLLRDHUP ((uint32_t) EPOLLRDHUP)
#define EPOLLONESHOT ((uint32_t) EPOLLONESHOT)
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_MOD 2
#define EPOLL_CTL_DEL 3
/* clang-format on */
typedef void* HANDLE;
typedef uintptr_t SOCKET;
@ -75,8 +79,8 @@ typedef union epoll_data {
int fd;
uint32_t u32;
uint64_t u64;
SOCKET sock;
HANDLE hnd;
SOCKET sock; /* Windows specific */
HANDLE hnd; /* Windows specific */
} epoll_data_t;
struct epoll_event {
@ -118,6 +122,8 @@ WEPOLL_EXPORT int epoll_wait(HANDLE ephnd,
#include <WinSock2.h>
#include <Windows.h>
WEPOLL_INTERNAL int nt_global_init(void);
#ifndef _NTDEF_
typedef LONG NTSTATUS;
typedef NTSTATUS* PNTSTATUS;
@ -187,6 +193,73 @@ typedef NTSTATUS* PNTSTATUS;
#define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L)
#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>
#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 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 METHOD_BUFFERED 0
#define AFD_POLL 9
@ -2073,7 +2077,7 @@ reflock_tree_node_t* reflock_tree_del_and_ref(reflock_tree_t* rlt,
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);
if (rlt_node != NULL) {
@ -2093,7 +2097,7 @@ reflock_tree_node_t* reflock_tree_find_and_ref(reflock_tree_t* rlt,
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);
if (rlt_node != NULL)
reflock_ref(&rlt_node->reflock);
@ -2199,7 +2203,9 @@ void reflock_unref_and_destroy(reflock_t* reflock) {
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 {
OVERLAPPED overlapped;
@ -2433,7 +2439,7 @@ int ep_sock_set_event(ep_port_t* port_info,
sock_private->user_events = events;
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);
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));
if (sock_private->poll_status == _POLL_PENDING &&
(sock_private->user_events & _EP_EVENT_MASK &
if ((sock_private->poll_status == _POLL_PENDING) &&
(sock_private->user_events & _KNOWN_EPOLL_EVENTS &
~sock_private->pending_events) == 0) {
/* All the events the user is interested in are already being monitored
* by the pending poll request. It might spuriously complete because of an

View File

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