/* * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/* * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * * wepoll - epoll for Windows * https://github.com/piscisaureus/wepoll * * Copyright 2012-2020, Bert Belder <bertbelder@gmail.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
typedefunion epoll_data { void* ptr; int fd;
uint32_t u32;
uint64_t u64;
SOCKET sock; /* Windows specific */
HANDLE hnd; /* Windows specific */
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events and flags */
epoll_data_t data; /* User data variable */
};
/* By opening \Device\Afd without specifying any extended attributes, we'll * get a handle that lets us talk to the AFD driver, but that doesn't have an
* associated endpoint (so it's not a socket). */
status = NtCreateFile(&afd_device_handle,
SYNCHRONIZE,
&afd__device_attributes,
&iosb,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
0,
NULL,
0); if (status != STATUS_SUCCESS)
return_set_error(-1, RtlNtStatusToDosError(status));
if (CreateIoCompletionPort(afd_device_handle, iocp_handle, 0, 0) == NULL) goto error;
if (!SetFileCompletionNotificationModes(afd_device_handle,
FILE_SKIP_SET_EVENT_ON_HANDLE)) goto error;
int afd_cancel_poll(HANDLE afd_device_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;
/* 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));
}
/* The reflock is a special kind of lock that normally prevents a chunk of * memory from being freed, but does allow the chunk of memory to eventually be * released in a coordinated fashion. * * Under normal operation, threads increase and decrease the reference count, * which are wait-free operations. * * Exactly once during the reflock's lifecycle, a thread holding a reference to * the lock may "destroy" the lock; this operation blocks until all other * threads holding a reference to the lock have dereferenced it. After * "destroy" returns, the calling thread may assume that no other threads have * a reference to the lock. * * Attemmpting to lock or destroy a lock after reflock_unref_and_destroy() has * been called is invalid and results in undefined behavior. Therefore the user * should use another lock to guarantee that this can't happen.
*/
/* N.b.: the tree functions do not set errno or LastError when they fail. Each * of the API functions has at most one failure mode. It is up to the caller to
* set an appropriate error code when necessary. */
typedefstruct tree tree_t; typedefstruct tree_node tree_node_t;
port_state = port_state_from_handle_tree_node(tree_node);
r = port_ctl(port_state, op, sock, ev);
ts_tree_node_unref(tree_node);
if (r < 0) goto err;
return 0;
err: /* On Linux, in the case of epoll_ctl(), EBADF takes priority over other
* errors. Wepoll mimics this behavior. */
err_check_handle(ephnd);
err_check_handle((HANDLE) sock); return -1;
}
int epoll_wait(HANDLE ephnd, struct epoll_event* events, int maxevents, int timeout) {
ts_tree_node_t* tree_node;
port_state_t* port_state; int num_events;
if (maxevents <= 0)
return_set_error(-1, ERROR_INVALID_PARAMETER);
int err_check_handle(HANDLE handle) {
DWORD flags;
/* GetHandleInformation() succeeds when passed INVALID_HANDLE_VALUE, so check
* for this condition explicitly. */ if (handle == INVALID_HANDLE_VALUE)
return_set_error(-1, ERROR_INVALID_HANDLE);
if (!GetHandleInformation(handle, &flags))
return_map_error(-1);
/* N.b. that initialization order matters here. */ if (ws_global_init() < 0 || nt_global_init() < 0 ||
reflock_global_init() < 0 || epoll_global_init() < 0) returnFALSE;
init__done = true; returnTRUE;
}
int init(void) { if (!init__done &&
!InitOnceExecuteOnce(&init__once, init__once_callback, NULL, NULL)) /* `InitOnceExecuteOnce()` itself is infallible, and it doesn't set any * error code when the once-callback returns FALSE. We return -1 here to * indicate that global initialization failed; the failing init function is
* resposible for setting `errno` and calling `SetLastError()`. */ return -1;
return 0;
}
/* Set up a workaround for the following problem: * FARPROC addr = GetProcAddress(...); * MY_FUNC func = (MY_FUNC) addr; <-- GCC 8 warning/error. * MY_FUNC func = (MY_FUNC) (void*) addr; <-- MSVC warning/error. * To compile cleanly with either compiler, do casts with this "bridge" type:
* MY_FUNC func = (MY_FUNC) (nt__fn_ptr_cast_t) addr; */ #ifdef __GNUC__ typedefvoid* nt__fn_ptr_cast_t; #else typedef FARPROC nt__fn_ptr_cast_t; #endif
/* Walk the queue, submitting new poll requests for every socket that needs
* it. */ while (!queue_is_empty(sock_update_queue)) {
queue_node_t* queue_node = queue_first(sock_update_queue);
sock_state_t* sock_state = sock_state_from_queue_node(queue_node);
if (sock_update(port_state, sock_state) < 0) return -1;
/* sock_update() removes the socket from the update queue. */
}
return 0;
}
staticinlinevoid port__update_events_if_polling(port_state_t* port_state) { if (port_state->active_poll_count > 0)
port__update_events(port_state);
}
for (i = 0; i < iocp_event_count; i++) {
IO_STATUS_BLOCK* io_status_block =
(IO_STATUS_BLOCK*) iocp_events[i].lpOverlapped; struct epoll_event* ev = &epoll_events[epoll_event_count];
int port_wait(port_state_t* port_state, struct epoll_event* events, int maxevents, int timeout) {
OVERLAPPED_ENTRY stack_iocp_events[PORT__MAX_ON_STACK_COMPLETIONS];
OVERLAPPED_ENTRY* iocp_events;
uint64_t due = 0;
DWORD gqcs_timeout; int result;
/* Check whether `maxevents` is in range. */ if (maxevents <= 0)
return_set_error(-1, ERROR_INVALID_PARAMETER);
/* Decide whether the IOCP completion list can live on the stack, or allocate
* memory for it on the heap. */ if ((size_t) maxevents <= array_count(stack_iocp_events)) {
iocp_events = stack_iocp_events;
} elseif ((iocp_events =
malloc((size_t) maxevents * sizeof *iocp_events)) == NULL) {
iocp_events = stack_iocp_events;
maxevents = array_count(stack_iocp_events);
}
/* Compute the timeout for GetQueuedCompletionStatus, and the wait end
* time, if the user specified a timeout other than zero or infinite. */ if (timeout > 0) {
due = GetTickCount64() + (uint64_t) timeout;
gqcs_timeout = (DWORD) timeout;
} elseif (timeout == 0) {
gqcs_timeout = 0;
} else {
gqcs_timeout = INFINITE;
}
EnterCriticalSection(&port_state->lock);
/* Dequeue completion packets until either at least one interesting event
* has been discovered, or the timeout is reached. */ for (;;) {
uint64_t now;
result = port__poll(
port_state, events, iocp_events, (DWORD) maxevents, gqcs_timeout); if (result < 0 || result > 0) break; /* Result, error, or time-out. */
if (timeout < 0) continue; /* When timeout is negative, never time out. */
/* Update time. */
now = GetTickCount64();
/* Do not allow the due time to be in the past. */ if (now >= due) {
SetLastError(WAIT_TIMEOUT); break;
}
staticvoid reflock__signal_event(void* address) {
NTSTATUS status =
NtReleaseKeyedEvent(reflock__keyed_event, address, FALSE, NULL); if (status != STATUS_SUCCESS)
abort();
}
staticvoid reflock__await_event(void* address) {
NTSTATUS status =
NtWaitForKeyedEvent(reflock__keyed_event, address, FALSE, NULL); if (status != STATUS_SUCCESS)
abort();
}
void reflock_ref(reflock_t* reflock) { long state = InterlockedAdd(&reflock->state, REFLOCK__REF);
/* Verify that the counter didn't overflow and the lock isn't destroyed. */
assert((state & REFLOCK__DESTROY_MASK) == 0);
unused_var(state);
}
void reflock_unref(reflock_t* reflock) { long state = InterlockedAdd(&reflock->state, -REFLOCK__REF);
/* Verify that the lock was referenced and not already destroyed. */
assert((state & REFLOCK__DESTROY_MASK & ~REFLOCK__DESTROY) == 0);
if (state == REFLOCK__DESTROY)
reflock__signal_event(reflock);
}
void reflock_unref_and_destroy(reflock_t* reflock) { long state =
InterlockedAdd(&reflock->state, REFLOCK__DESTROY - REFLOCK__REF); long ref_count = state & REFLOCK__REF_MASK;
/* Verify that the lock was referenced and not already destroyed. */
assert((state & REFLOCK__DESTROY_MASK) == REFLOCK__DESTROY);
if (ref_count != 0)
reflock__await_event(reflock);
state = InterlockedExchange(&reflock->state, REFLOCK__POISON);
assert(state == REFLOCK__DESTROY);
}
/* If the poll request still needs to complete, the sock_state object can't * be free()d yet. `sock_feed_event()` or `port_close()` will take care
* of this later. */ if (force || sock_state->poll_status == SOCK__POLL_IDLE) { /* Free the sock_state now. */
port_remove_deleted_socket(port_state, sock_state);
poll_group_release(sock_state->poll_group);
sock__free(sock_state);
} else { /* Free the socket later. */
port_add_deleted_socket(port_state, sock_state);
}
int sock_set_event(port_state_t* port_state,
sock_state_t* sock_state, conststruct epoll_event* ev) { /* EPOLLERR and EPOLLHUP are always reported, even when not requested by the * caller. However they are disabled after a event has been reported for a
* socket for which the EPOLLONESHOT flag was set. */
uint32_t events = ev->events | EPOLLERR | EPOLLHUP;
if ((events & SOCK__KNOWN_EPOLL_EVENTS & ~sock_state->pending_events) != 0)
port_request_socket_update(port_state, sock_state);
return 0;
}
staticinline DWORD sock__epoll_events_to_afd_events(uint32_t epoll_events) { /* Always monitor for AFD_POLL_LOCAL_CLOSE, which is triggered when the
* socket is closed with closesocket() or CloseHandle(). */
DWORD afd_events = 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; if (epoll_events & (EPOLLIN | EPOLLRDNORM | EPOLLRDHUP))
afd_events |= AFD_POLL_DISCONNECT; if (epoll_events & EPOLLHUP)
afd_events |= AFD_POLL_ABORT; if (epoll_events & EPOLLERR)
afd_events |= AFD_POLL_CONNECT_FAIL;
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)
epoll_events |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; if (afd_events & AFD_POLL_ABORT)
epoll_events |= EPOLLHUP; if (afd_events & AFD_POLL_CONNECT_FAIL) /* Linux reports all these events after connect() has failed. */
epoll_events |=
EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLRDNORM | EPOLLWRNORM | EPOLLRDHUP;
return epoll_events;
}
int sock_update(port_state_t* port_state, sock_state_t* sock_state) {
assert(!sock_state->delete_pending);
if ((sock_state->poll_status == SOCK__POLL_PENDING) &&
(sock_state->user_events & SOCK__KNOWN_EPOLL_EVENTS &
~sock_state->pending_events) == 0) { /* All the events the user is interested in are already being monitored by * the pending poll operation. It might spuriously complete because of an * event that we're no longer interested in; when that happens we'll submit
* a new poll operation with the updated event mask. */
} elseif (sock_state->poll_status == SOCK__POLL_PENDING) { /* A poll operation is already pending, but it's not monitoring for all the * events that the user is interested in. Therefore, cancel the pending * poll operation; when we receive it's completion package, a new poll
* operation will be submitted with the correct event mask. */ if (sock__cancel_poll(sock_state) < 0) return -1;
} elseif (sock_state->poll_status == SOCK__POLL_CANCELLED) { /* The poll operation has already been cancelled, we're still waiting for
* it to return. For now, there's nothing that needs to be done. */
if (afd_poll(poll_group_get_afd_device_handle(sock_state->poll_group),
&sock_state->poll_info,
&sock_state->io_status_block) < 0) { switch (GetLastError()) { case ERROR_IO_PENDING: /* Overlapped poll operation in progress; this is expected. */ break; case ERROR_INVALID_HANDLE: /* Socket closed; it'll be dropped from the epoll set. */ return sock__delete(port_state, sock_state, false); default: /* Other errors are propagated to the caller. */
return_map_error(-1);
}
}
/* The poll request was successfully submitted. */
sock_state->poll_status = SOCK__POLL_PENDING;
sock_state->pending_events = sock_state->user_events;
if (sock_state->delete_pending) { /* Socket has been deleted earlier and can now be freed. */ return sock__delete(port_state, sock_state, false);
} elseif (io_status_block->Status == STATUS_CANCELLED) { /* The poll request was cancelled by CancelIoEx. */
} elseif (!NT_SUCCESS(io_status_block->Status)) { /* The overlapped request itself failed in an unexpected way. */
epoll_events = EPOLLERR;
} elseif (poll_info->NumberOfHandles < 1) { /* This poll operation succeeded but didn't report any socket events. */
} elseif (poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) { /* The poll operation reported that the socket was closed. */ return sock__delete(port_state, sock_state, false);
} else { /* Events related to our socket were reported. */
epoll_events =
sock__afd_events_to_epoll_events(poll_info->Handles[0].Events);
}
/* Requeue the socket so a new poll request will be submitted. */
port_request_socket_update(port_state, sock_state);
/* Filter out events that the user didn't ask for. */
epoll_events &= sock_state->user_events;
/* Return if there are no epoll events to report. */ if (epoll_events == 0) return 0;
/* If the socket has the EPOLLONESHOT flag set, unmonitor all events,
* even EPOLLERR and EPOLLHUP. But always keep looking for closed sockets. */ if (sock_state->user_events & EPOLLONESHOT)
sock_state->user_events = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.