afd: use IO_STATUS_BLOCK instead of OVERLAPPED to track async poll ops
This reduces per-socket memory usage, as the OVERLAPPED structure contains some fields that are never used.
This commit is contained in:
parent
a627f365fd
commit
d5f8f5f1b1
48
src/afd.c
48
src/afd.c
@ -55,33 +55,18 @@ error:
|
||||
|
||||
int afd_poll(HANDLE afd_helper_handle,
|
||||
AFD_POLL_INFO* poll_info,
|
||||
OVERLAPPED* overlapped) {
|
||||
IO_STATUS_BLOCK* iosb;
|
||||
HANDLE event;
|
||||
void* apc_context;
|
||||
IO_STATUS_BLOCK* io_status_block) {
|
||||
NTSTATUS status;
|
||||
|
||||
/* Blocking operation is not supported. */
|
||||
assert(overlapped != NULL);
|
||||
assert(io_status_block != NULL);
|
||||
|
||||
iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
|
||||
event = overlapped->hEvent;
|
||||
|
||||
/* Do what other windows APIs would do: if hEvent has it's lowest bit set,
|
||||
* don't post a completion to the completion port. */
|
||||
if ((uintptr_t) event & 1) {
|
||||
event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
|
||||
apc_context = NULL;
|
||||
} else {
|
||||
apc_context = overlapped;
|
||||
}
|
||||
|
||||
iosb->Status = STATUS_PENDING;
|
||||
io_status_block->Status = STATUS_PENDING;
|
||||
status = NtDeviceIoControlFile(afd_helper_handle,
|
||||
event,
|
||||
NULL,
|
||||
apc_context,
|
||||
iosb,
|
||||
NULL,
|
||||
io_status_block,
|
||||
io_status_block,
|
||||
IOCTL_AFD_POLL,
|
||||
poll_info,
|
||||
sizeof *poll_info,
|
||||
@ -95,3 +80,24 @@ int afd_poll(HANDLE afd_helper_handle,
|
||||
else
|
||||
return_set_error(-1, RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
int afd_cancel_poll(HANDLE afd_helper_handle,
|
||||
IO_STATUS_BLOCK* io_status_block) {
|
||||
NTSTATUS cancel_status;
|
||||
IO_STATUS_BLOCK cancel_iosb;
|
||||
|
||||
/* If the poll operation has already completed or has been cancelled earlier,
|
||||
* there's nothing left for us to do. */
|
||||
if (io_status_block->Status != STATUS_PENDING)
|
||||
return 0;
|
||||
|
||||
cancel_status =
|
||||
NtCancelIoFileEx(afd_helper_handle, io_status_block, &cancel_iosb);
|
||||
|
||||
/* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed
|
||||
* just before calling NtCancelIoFileEx(). This is not an error. */
|
||||
if (cancel_status == STATUS_SUCCESS || cancel_status == STATUS_NOT_FOUND)
|
||||
return 0;
|
||||
else
|
||||
return_set_error(-1, RtlNtStatusToDosError(cancel_status));
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ WEPOLL_INTERNAL int afd_create_helper_handle(HANDLE iocp_handle,
|
||||
|
||||
WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle,
|
||||
AFD_POLL_INFO* poll_info,
|
||||
OVERLAPPED* overlapped);
|
||||
IO_STATUS_BLOCK* io_status_block);
|
||||
WEPOLL_INTERNAL int afd_cancel_poll(HANDLE afd_helper_handle,
|
||||
IO_STATUS_BLOCK* io_status_block);
|
||||
|
||||
#endif /* WEPOLL_AFD_H_ */
|
||||
|
||||
11
src/nt.h
11
src/nt.h
@ -25,6 +25,10 @@ typedef NTSTATUS* PNTSTATUS;
|
||||
#define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_NOT_FOUND
|
||||
#define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L)
|
||||
#endif
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
NTSTATUS Status;
|
||||
ULONG_PTR Information;
|
||||
@ -65,6 +69,13 @@ typedef struct _OBJECT_ATTRIBUTES {
|
||||
(STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE)
|
||||
|
||||
#define NT_NTDLL_IMPORT_LIST(X) \
|
||||
X(NTSTATUS, \
|
||||
NTAPI, \
|
||||
NtCancelIoFileEx, \
|
||||
(HANDLE FileHandle, \
|
||||
PIO_STATUS_BLOCK IoRequestToCancel, \
|
||||
PIO_STATUS_BLOCK IoStatusBlock)) \
|
||||
\
|
||||
X(NTSTATUS, \
|
||||
NTAPI, \
|
||||
NtCreateFile, \
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "error.h"
|
||||
#include "nt.h"
|
||||
#include "poll-group.h"
|
||||
#include "port.h"
|
||||
#include "queue.h"
|
||||
@ -162,10 +163,11 @@ static int port__feed_events(port_state_t* port_state,
|
||||
DWORD i;
|
||||
|
||||
for (i = 0; i < iocp_event_count; i++) {
|
||||
OVERLAPPED* overlapped = iocp_events[i].lpOverlapped;
|
||||
IO_STATUS_BLOCK* io_status_block =
|
||||
(IO_STATUS_BLOCK*) iocp_events[i].lpOverlapped;
|
||||
struct epoll_event* ev = &epoll_events[epoll_event_count];
|
||||
|
||||
epoll_event_count += sock_feed_event(port_state, overlapped, ev);
|
||||
epoll_event_count += sock_feed_event(port_state, io_status_block, ev);
|
||||
}
|
||||
|
||||
return epoll_event_count;
|
||||
|
||||
25
src/sock.c
25
src/sock.c
@ -26,7 +26,7 @@ typedef enum sock__poll_status {
|
||||
} sock__poll_status_t;
|
||||
|
||||
typedef struct sock_state {
|
||||
OVERLAPPED overlapped;
|
||||
IO_STATUS_BLOCK io_status_block;
|
||||
AFD_POLL_INFO poll_info;
|
||||
queue_node_t queue_node;
|
||||
tree_node_t tree_node;
|
||||
@ -51,16 +51,11 @@ static inline void sock__free(sock_state_t* sock_state) {
|
||||
}
|
||||
|
||||
static int sock__cancel_poll(sock_state_t* sock_state) {
|
||||
HANDLE afd_helper_handle =
|
||||
poll_group_get_afd_helper_handle(sock_state->poll_group);
|
||||
assert(sock_state->poll_status == SOCK__POLL_PENDING);
|
||||
|
||||
/* CancelIoEx() may fail with ERROR_NOT_FOUND if the overlapped operation has
|
||||
* already completed. This is not a problem and we proceed normally. */
|
||||
if (!HasOverlappedIoCompleted(&sock_state->overlapped) &&
|
||||
!CancelIoEx(afd_helper_handle, &sock_state->overlapped) &&
|
||||
GetLastError() != ERROR_NOT_FOUND)
|
||||
return_map_error(-1);
|
||||
if (afd_cancel_poll(poll_group_get_afd_helper_handle(sock_state->poll_group),
|
||||
&sock_state->io_status_block) < 0)
|
||||
return -1;
|
||||
|
||||
sock_state->poll_status = SOCK__POLL_CANCELLED;
|
||||
sock_state->pending_events = 0;
|
||||
@ -237,11 +232,9 @@ int sock_update(port_state_t* port_state, sock_state_t* sock_state) {
|
||||
sock_state->poll_info.Handles[0].Events =
|
||||
sock__epoll_events_to_afd_events(sock_state->user_events);
|
||||
|
||||
memset(&sock_state->overlapped, 0, sizeof sock_state->overlapped);
|
||||
|
||||
if (afd_poll(poll_group_get_afd_helper_handle(sock_state->poll_group),
|
||||
&sock_state->poll_info,
|
||||
&sock_state->overlapped) < 0) {
|
||||
&sock_state->io_status_block) < 0) {
|
||||
switch (GetLastError()) {
|
||||
case ERROR_IO_PENDING:
|
||||
/* Overlapped poll operation in progress; this is expected. */
|
||||
@ -269,10 +262,10 @@ int sock_update(port_state_t* port_state, sock_state_t* sock_state) {
|
||||
}
|
||||
|
||||
int sock_feed_event(port_state_t* port_state,
|
||||
OVERLAPPED* overlapped,
|
||||
IO_STATUS_BLOCK* io_status_block,
|
||||
struct epoll_event* ev) {
|
||||
sock_state_t* sock_state =
|
||||
container_of(overlapped, sock_state_t, overlapped);
|
||||
container_of(io_status_block, sock_state_t, io_status_block);
|
||||
AFD_POLL_INFO* poll_info = &sock_state->poll_info;
|
||||
uint32_t epoll_events = 0;
|
||||
|
||||
@ -283,10 +276,10 @@ int sock_feed_event(port_state_t* port_state,
|
||||
/* Socket has been deleted earlier and can now be freed. */
|
||||
return sock__delete(port_state, sock_state, false);
|
||||
|
||||
} else if ((NTSTATUS) overlapped->Internal == STATUS_CANCELLED) {
|
||||
} else if (io_status_block->Status == STATUS_CANCELLED) {
|
||||
/* The poll request was cancelled by CancelIoEx. */
|
||||
|
||||
} else if (!NT_SUCCESS(overlapped->Internal)) {
|
||||
} else if (!NT_SUCCESS(io_status_block->Status)) {
|
||||
/* The overlapped request itself failed in an unexpected way. */
|
||||
epoll_events = EPOLLERR;
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define WEPOLL_SOCK_H_
|
||||
|
||||
#include "internal.h"
|
||||
#include "nt.h"
|
||||
#include "wepoll.h"
|
||||
#include "win.h"
|
||||
|
||||
@ -24,7 +25,7 @@ WEPOLL_INTERNAL int sock_set_event(port_state_t* port_state,
|
||||
WEPOLL_INTERNAL int sock_update(port_state_t* port_state,
|
||||
sock_state_t* sock_state);
|
||||
WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state,
|
||||
OVERLAPPED* overlapped,
|
||||
IO_STATUS_BLOCK* io_status_block,
|
||||
struct epoll_event* ev);
|
||||
|
||||
WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user