b5a613c378
This updates the IOHandle type in eventhandler_fuchsia to use zx_port_cancel_key to cancel outstanding asynchronous waits instead of the (older) zx_port_cancel. The cancel_key operation requires only the key used to register the initial wait and not the originating handle. This means that the IOHandle operation no longer has to store the original handle or worry about its lifetime. Tested: Patched locally in a test embedder using this logic Change-Id: Iec0ec632a7dd438a34e482a12b5b298be56ce476 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501840 Reviewed-by: Zijie He <zijiehe@google.com> Auto-Submit: James Robinson <jamesr@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
694 lines
24 KiB
C++
694 lines
24 KiB
C++
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "platform/globals.h"
|
|
#if defined(DART_HOST_OS_FUCHSIA)
|
|
|
|
#include "bin/eventhandler.h"
|
|
#include "bin/eventhandler_fuchsia.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <zircon/status.h>
|
|
#include <zircon/syscalls.h>
|
|
#include <zircon/syscalls/object.h>
|
|
#include <zircon/syscalls/port.h>
|
|
|
|
#include "bin/fdutils.h"
|
|
#include "bin/lockers.h"
|
|
#include "bin/socket.h"
|
|
#include "bin/thread.h"
|
|
#include "bin/utils.h"
|
|
#include "platform/hashmap.h"
|
|
#include "platform/syslog.h"
|
|
#include "platform/utils.h"
|
|
|
|
// The EventHandler for Fuchsia uses its "ports v2" API:
|
|
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/docs/syscalls/port_create.md
|
|
// This API does not have epoll()-like edge triggering (EPOLLET). Since clients
|
|
// of the EventHandler expect edge-triggered notifications, we must simulate it.
|
|
// When a packet from zx_port_wait() indicates that a signal is asserted for a
|
|
// handle, we unsubscribe from that signal until the event that asserted the
|
|
// signal can be processed. For example:
|
|
//
|
|
// 1. We get ZX_SOCKET_WRITABLE from zx_port_wait() for a handle.
|
|
// 2. We send kOutEvent to the Dart thread.
|
|
// 3. We unsubscribe from further ZX_SOCKET_WRITABLE signals for the handle.
|
|
// 4. Some time later the Dart thread actually does a write().
|
|
// 5. After writing, the Dart thread resubscribes to write events.
|
|
//
|
|
// We use the same procedure for ZX_SOCKET_READABLE, and read()/accept().
|
|
|
|
// define EVENTHANDLER_LOG_ERROR to get log messages only for errors.
|
|
// define EVENTHANDLER_LOG_INFO to get log messages for both information and
|
|
// errors.
|
|
// #define EVENTHANDLER_LOG_INFO 1
|
|
#define EVENTHANDLER_LOG_ERROR 1
|
|
#if defined(EVENTHANDLER_LOG_INFO) || defined(EVENTHANDLER_LOG_ERROR)
|
|
#define LOG_ERR(msg, ...) \
|
|
{ \
|
|
int err = errno; \
|
|
Syslog::PrintErr("Dart EventHandler ERROR: %s:%d: " msg, __FILE__, \
|
|
__LINE__, ##__VA_ARGS__); \
|
|
errno = err; \
|
|
}
|
|
#if defined(EVENTHANDLER_LOG_INFO)
|
|
#define LOG_INFO(msg, ...) \
|
|
Syslog::Print("Dart EventHandler INFO: %s:%d: " msg, __FILE__, __LINE__, \
|
|
##__VA_ARGS__)
|
|
#else
|
|
#define LOG_INFO(msg, ...)
|
|
#endif // defined(EVENTHANDLER_LOG_INFO)
|
|
#else
|
|
#define LOG_ERR(msg, ...)
|
|
#define LOG_INFO(msg, ...)
|
|
#endif // defined(EVENTHANDLER_LOG_INFO) || defined(EVENTHANDLER_LOG_ERROR)
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
intptr_t IOHandle::Read(void* buffer, intptr_t num_bytes) {
|
|
MutexLocker ml(&mutex_);
|
|
const ssize_t read_bytes = NO_RETRY_EXPECTED(read(fd_, buffer, num_bytes));
|
|
const int err = errno;
|
|
LOG_INFO("IOHandle::Read: fd = %ld. read %ld bytes\n", fd_, read_bytes);
|
|
|
|
// Track the number of bytes available to read.
|
|
if (read_bytes > 0) {
|
|
available_bytes_ -=
|
|
(available_bytes_ >= read_bytes) ? read_bytes : available_bytes_;
|
|
}
|
|
|
|
// If we have read all available bytes, or if there was an error, then
|
|
// re-enable read events. We re-enable read events even if read() returns
|
|
// an error. The error might be, e.g. EWOULDBLOCK, in which case
|
|
// resubscription is necessary. Logic in the caller decides which errors
|
|
// are real, and which are ignore-and-continue.
|
|
if ((available_bytes_ == 0) || (read_bytes < 0)) {
|
|
// Resubscribe to read events.
|
|
read_events_enabled_ = true;
|
|
if (wait_key_ == 0) {
|
|
LOG_ERR("IOHandle::Read calling AsyncWaitLocked with wait_key_ == 0");
|
|
}
|
|
if (!AsyncWaitLocked(ZX_HANDLE_INVALID, POLLIN, wait_key_)) {
|
|
LOG_ERR("IOHandle::AsyncWait failed for fd = %ld\n", fd_);
|
|
}
|
|
}
|
|
|
|
errno = err;
|
|
return read_bytes;
|
|
}
|
|
|
|
intptr_t IOHandle::Write(const void* buffer, intptr_t num_bytes) {
|
|
MutexLocker ml(&mutex_);
|
|
const ssize_t written_bytes =
|
|
NO_RETRY_EXPECTED(write(fd_, buffer, num_bytes));
|
|
const int err = errno;
|
|
LOG_INFO("IOHandle::Write: fd = %ld. wrote %ld bytes\n", fd_, written_bytes);
|
|
|
|
// Resubscribe to write events.
|
|
write_events_enabled_ = true;
|
|
if (wait_key_ == 0) {
|
|
LOG_ERR("IOHandle::Write calling AsyncWaitLocked with wait_key_ == 0");
|
|
}
|
|
if (!AsyncWaitLocked(ZX_HANDLE_INVALID, POLLOUT, wait_key_)) {
|
|
LOG_ERR("IOHandle::AsyncWait failed for fd = %ld\n", fd_);
|
|
}
|
|
|
|
errno = err;
|
|
return written_bytes;
|
|
}
|
|
|
|
intptr_t IOHandle::Accept(RawAddr* addr) {
|
|
MutexLocker ml(&mutex_);
|
|
const intptr_t socket =
|
|
NO_RETRY_EXPECTED(accept(fd_, &addr->addr, &addr->size));
|
|
const int err = errno;
|
|
LOG_INFO("IOHandle::Accept: fd = %ld. socket = %ld\n", fd_, socket);
|
|
|
|
// Re-subscribe to read events.
|
|
read_events_enabled_ = true;
|
|
if (wait_key_ == 0) {
|
|
LOG_ERR("IOHandle::Accept calling AsyncWaitLocked with wait_key_ == 0");
|
|
}
|
|
if (!AsyncWaitLocked(ZX_HANDLE_INVALID, POLLIN, wait_key_)) {
|
|
LOG_ERR("IOHandle::AsyncWait failed for fd = %ld\n", fd_);
|
|
}
|
|
|
|
errno = err;
|
|
return socket;
|
|
}
|
|
|
|
intptr_t IOHandle::AvailableBytes() {
|
|
MutexLocker ml(&mutex_);
|
|
ASSERT(fd_ >= 0);
|
|
intptr_t available = FDUtils::AvailableBytes(fd_);
|
|
LOG_INFO("IOHandle::AvailableBytes(): fd = %ld, bytes = %ld\n", fd_,
|
|
available);
|
|
if (available < 0) {
|
|
// If there is an error, we set available to 1 to trigger a read event that
|
|
// then propagates the error.
|
|
available = 1;
|
|
}
|
|
available_bytes_ = available;
|
|
return available;
|
|
}
|
|
|
|
void IOHandle::Close() {
|
|
MutexLocker ml(&mutex_);
|
|
VOID_NO_RETRY_EXPECTED(close(fd_));
|
|
}
|
|
|
|
uint32_t IOHandle::MaskToEpollEvents(intptr_t mask) {
|
|
MutexLocker ml(&mutex_);
|
|
// Do not ask for POLLERR and POLLHUP explicitly as they are
|
|
// triggered anyway.
|
|
uint32_t events = 0;
|
|
// Do not subscribe to read closed events when kCloseEvent has already been
|
|
// sent to the Dart thread.
|
|
if (close_events_enabled_) {
|
|
events |= POLLRDHUP;
|
|
}
|
|
if (read_events_enabled_ && ((mask & (1 << kInEvent)) != 0)) {
|
|
events |= POLLIN;
|
|
}
|
|
if (write_events_enabled_ && ((mask & (1 << kOutEvent)) != 0)) {
|
|
events |= POLLOUT;
|
|
}
|
|
return events;
|
|
}
|
|
|
|
intptr_t IOHandle::EpollEventsToMask(intptr_t events) {
|
|
if ((events & POLLERR) != 0) {
|
|
// Return error only if POLLIN is present.
|
|
return ((events & POLLIN) != 0) ? (1 << kErrorEvent) : 0;
|
|
}
|
|
intptr_t event_mask = 0;
|
|
if ((events & POLLIN) != 0) {
|
|
event_mask |= (1 << kInEvent);
|
|
}
|
|
if ((events & POLLOUT) != 0) {
|
|
event_mask |= (1 << kOutEvent);
|
|
}
|
|
if ((events & (POLLHUP | POLLRDHUP)) != 0) {
|
|
event_mask |= (1 << kCloseEvent);
|
|
}
|
|
return event_mask;
|
|
}
|
|
|
|
bool IOHandle::AsyncWaitLocked(zx_handle_t port,
|
|
uint32_t events,
|
|
uint64_t key) {
|
|
LOG_INFO("IOHandle::AsyncWaitLocked: fd = %ld\n", fd_);
|
|
if (key == 0) {
|
|
LOG_ERR("IOHandle::AsyncWaitLocked called with key == 0");
|
|
}
|
|
// The call to fdio_unsafe_fd_to_io() in the DescriptorInfo constructor may
|
|
// have returned nullptr. If it did, propagate the problem up to Dart.
|
|
if (fdio_ == nullptr) {
|
|
LOG_ERR("fdio_unsafe_fd_to_io(%ld) returned nullptr\n", fd_);
|
|
return false;
|
|
}
|
|
|
|
zx_handle_t handle;
|
|
zx_signals_t signals;
|
|
fdio_unsafe_wait_begin(fdio_, events, &handle, &signals);
|
|
if (handle == ZX_HANDLE_INVALID) {
|
|
LOG_ERR("fd = %ld fdio_unsafe_wait_begin returned an invalid handle\n",
|
|
fd_);
|
|
return false;
|
|
}
|
|
|
|
// Remember the port. Use the remembered port if the argument "port" is
|
|
// ZX_HANDLE_INVALID.
|
|
ASSERT((port != ZX_HANDLE_INVALID) || (port_ != ZX_HANDLE_INVALID));
|
|
if ((port_ == ZX_HANDLE_INVALID) || (port != ZX_HANDLE_INVALID)) {
|
|
port_ = port;
|
|
}
|
|
|
|
wait_key_ = key;
|
|
LOG_INFO("zx_object_wait_async(fd = %ld, signals = %x)\n", fd_, signals);
|
|
zx_status_t status =
|
|
zx_object_wait_async(handle, port_, key, signals, ZX_WAIT_ASYNC_ONCE);
|
|
if (status != ZX_OK) {
|
|
LOG_ERR("zx_object_wait_async failed: %s\n", zx_status_get_string(status));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IOHandle::AsyncWait(zx_handle_t port, uint32_t events, uint64_t key) {
|
|
MutexLocker ml(&mutex_);
|
|
return AsyncWaitLocked(port, events, key);
|
|
}
|
|
|
|
void IOHandle::CancelWait(zx_handle_t port, uint64_t key) {
|
|
MutexLocker ml(&mutex_);
|
|
LOG_INFO("IOHandle::CancelWait: fd = %ld\n", fd_);
|
|
ASSERT(port != ZX_HANDLE_INVALID);
|
|
if (key == 0) {
|
|
LOG_ERR("IOHandle::CancelWait calling zx_port_cancel_key with key == 0");
|
|
}
|
|
zx_status_t status = zx_port_cancel_key(port, 0u, key);
|
|
if ((status != ZX_OK) && (status != ZX_ERR_NOT_FOUND)) {
|
|
LOG_ERR("zx_port_cancel_key failed: %s\n", zx_status_get_string(status));
|
|
}
|
|
}
|
|
|
|
uint32_t IOHandle::WaitEnd(zx_signals_t observed) {
|
|
MutexLocker ml(&mutex_);
|
|
uint32_t events = 0;
|
|
fdio_unsafe_wait_end(fdio_, observed, &events);
|
|
LOG_INFO("IOHandle::WaitEnd: fd = %ld, events = %x\n", fd_, events);
|
|
return events;
|
|
}
|
|
|
|
// This function controls the simulation of edge-triggering. It is responsible
|
|
// for removing events from the event mask when they should be suppressed, and
|
|
// for suppressing future events. Events are unsuppressed by their respective
|
|
// operations by the Dart thread on the socket---that is, where the
|
|
// *_events_enabled_ flags are set to true.
|
|
intptr_t IOHandle::ToggleEvents(intptr_t event_mask) {
|
|
MutexLocker ml(&mutex_);
|
|
// If write events are disabled, then remove the kOutEvent bit from the
|
|
// event mask.
|
|
if (!write_events_enabled_) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"de-asserting kOutEvent\n",
|
|
fd_);
|
|
event_mask = event_mask & ~(1 << kOutEvent);
|
|
}
|
|
// If the kOutEvent bit is set, then suppress future write events until the
|
|
// Dart thread writes.
|
|
if ((event_mask & (1 << kOutEvent)) != 0) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"asserting kOutEvent and disabling\n",
|
|
fd_);
|
|
write_events_enabled_ = false;
|
|
}
|
|
|
|
// If read events are disabled, then remove the kInEvent bit from the event
|
|
// mask.
|
|
if (!read_events_enabled_) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"de-asserting kInEvent\n",
|
|
fd_);
|
|
event_mask = event_mask & ~(1 << kInEvent);
|
|
}
|
|
// We may get In events without available bytes, so we must make sure there
|
|
// are actually bytes, or we will never resubscribe (due to a short-circuit
|
|
// on the Dart side).
|
|
//
|
|
// This happens due to how packets get enqueued on the port with all signals
|
|
// asserted at that time. Sometimes we enqueue a packet due to
|
|
// zx_object_wait_async e.g. for POLLOUT (writability) while the socket is
|
|
// readable and while we have a Read queued up on the Dart side. This packet
|
|
// will also have POLLIN (readable) asserted. We may then perform the Read
|
|
// and drain the socket before our zx_port_wait is serviced, at which point
|
|
// when we process the packet for POLLOUT with its stale POLLIN (readable)
|
|
// signal, the socket is no longer actually readable.
|
|
//
|
|
// As a detail, negative available bytes (errors) are handled specially; see
|
|
// IOHandle::AvailableBytes for more information.
|
|
if ((event_mask & (1 << kInEvent)) != 0) {
|
|
if (FDUtils::AvailableBytes(fd_) != 0) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"asserting kInEvent and disabling with bytes available\n",
|
|
fd_);
|
|
read_events_enabled_ = false;
|
|
}
|
|
// Also suppress future read events if we get a kCloseEvent. This is to
|
|
// account for POLLIN being set by Fuchsia when the socket is read-closed.
|
|
if ((event_mask & (1 << kCloseEvent)) != 0) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"asserting kInEvent and disabling due to a close event\n",
|
|
fd_);
|
|
read_events_enabled_ = false;
|
|
}
|
|
}
|
|
|
|
// If the close events are disabled, then remove the kCloseEvent bit from the
|
|
// event mask.
|
|
if (!close_events_enabled_) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"de-asserting kCloseEvent\n",
|
|
fd_);
|
|
event_mask = event_mask & ~(1 << kCloseEvent);
|
|
}
|
|
// If the kCloseEvent bit is set, then suppress future close events, they will
|
|
// be ignored by the Dart thread. See _NativeSocket.multiplex in
|
|
// socket_patch.dart.
|
|
if ((event_mask & (1 << kCloseEvent)) != 0) {
|
|
LOG_INFO(
|
|
"IOHandle::ToggleEvents: fd = %ld "
|
|
"asserting kCloseEvent and disabling\n",
|
|
fd_);
|
|
close_events_enabled_ = false;
|
|
}
|
|
return event_mask;
|
|
}
|
|
|
|
void EventHandlerImplementation::AddToPort(zx_handle_t port_handle,
|
|
DescriptorInfo* di) {
|
|
const uint32_t events = di->io_handle()->MaskToEpollEvents(di->Mask());
|
|
const uint64_t key = reinterpret_cast<uint64_t>(di);
|
|
if (key == 0) {
|
|
LOG_ERR(
|
|
"EventHandlerImplementation::AddToPort calling AsyncWait with key == "
|
|
"0");
|
|
}
|
|
if (!di->io_handle()->AsyncWait(port_handle, events, key)) {
|
|
di->NotifyAllDartPorts(1 << kCloseEvent);
|
|
}
|
|
}
|
|
|
|
void EventHandlerImplementation::RemoveFromPort(zx_handle_t port_handle,
|
|
DescriptorInfo* di) {
|
|
const uint64_t key = reinterpret_cast<uint64_t>(di);
|
|
if (key == 0) {
|
|
LOG_ERR(
|
|
"EventHandlerImplementation::RemoveFromPort calling CancelWait with "
|
|
"key == 0");
|
|
}
|
|
di->io_handle()->CancelWait(port_handle, key);
|
|
}
|
|
|
|
EventHandlerImplementation::EventHandlerImplementation()
|
|
: socket_map_(&SimpleHashMap::SamePointerValue, 16) {
|
|
shutdown_ = false;
|
|
// Create the port.
|
|
port_handle_ = ZX_HANDLE_INVALID;
|
|
zx_status_t status = zx_port_create(0, &port_handle_);
|
|
if (status != ZX_OK) {
|
|
// This is a FATAL because the VM won't work at all if we can't create this
|
|
// port.
|
|
FATAL("zx_port_create failed: %s\n", zx_status_get_string(status));
|
|
}
|
|
ASSERT(port_handle_ != ZX_HANDLE_INVALID);
|
|
}
|
|
|
|
static void DeleteDescriptorInfo(void* info) {
|
|
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(info);
|
|
LOG_INFO("Closed %ld\n", di->io_handle()->fd());
|
|
di->Close();
|
|
delete di;
|
|
}
|
|
|
|
EventHandlerImplementation::~EventHandlerImplementation() {
|
|
socket_map_.Clear(DeleteDescriptorInfo);
|
|
zx_handle_close(port_handle_);
|
|
port_handle_ = ZX_HANDLE_INVALID;
|
|
}
|
|
|
|
void EventHandlerImplementation::UpdatePort(intptr_t old_mask,
|
|
DescriptorInfo* di) {
|
|
const intptr_t new_mask = di->Mask();
|
|
if ((old_mask != 0) && (new_mask == 0)) {
|
|
RemoveFromPort(port_handle_, di);
|
|
} else if ((old_mask == 0) && (new_mask != 0)) {
|
|
AddToPort(port_handle_, di);
|
|
} else if ((old_mask != 0) && (new_mask != 0)) {
|
|
ASSERT((old_mask == new_mask) || !di->IsListeningSocket());
|
|
RemoveFromPort(port_handle_, di);
|
|
AddToPort(port_handle_, di);
|
|
}
|
|
}
|
|
|
|
DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
|
|
intptr_t fd,
|
|
bool is_listening) {
|
|
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
|
|
ASSERT(handle->fd() >= 0);
|
|
SimpleHashMap::Entry* entry =
|
|
socket_map_.Lookup(GetHashmapKeyFromFd(handle->fd()),
|
|
GetHashmapHashFromFd(handle->fd()), true);
|
|
ASSERT(entry != nullptr);
|
|
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(entry->value);
|
|
if (di == nullptr) {
|
|
// If there is no data in the hash map for this file descriptor a
|
|
// new DescriptorInfo for the file descriptor is inserted.
|
|
if (is_listening) {
|
|
di = new DescriptorInfoMultiple(fd);
|
|
} else {
|
|
di = new DescriptorInfoSingle(fd);
|
|
}
|
|
entry->value = di;
|
|
}
|
|
ASSERT(fd == di->fd());
|
|
return di;
|
|
}
|
|
|
|
void EventHandlerImplementation::WakeupHandler(intptr_t id,
|
|
Dart_Port dart_port,
|
|
int64_t data) {
|
|
COMPILE_ASSERT(sizeof(InterruptMessage) <= sizeof(zx_packet_user_t));
|
|
zx_port_packet_t pkt;
|
|
InterruptMessage* msg = reinterpret_cast<InterruptMessage*>(&pkt.user);
|
|
pkt.key = kInterruptPacketKey;
|
|
msg->id = id;
|
|
msg->dart_port = dart_port;
|
|
msg->data = data;
|
|
zx_status_t status = zx_port_queue(port_handle_, &pkt);
|
|
if (status != ZX_OK) {
|
|
// This is a FATAL because the VM won't work at all if we can't send any
|
|
// messages to the EventHandler thread.
|
|
FATAL("zx_port_queue failed: %s\n", zx_status_get_string(status));
|
|
}
|
|
}
|
|
|
|
void EventHandlerImplementation::HandleInterrupt(InterruptMessage* msg) {
|
|
if (msg->id == kTimerId) {
|
|
LOG_INFO("HandleInterrupt read timer update\n");
|
|
timeout_queue_.UpdateTimeout(msg->dart_port, msg->data);
|
|
return;
|
|
} else if (msg->id == kShutdownId) {
|
|
LOG_INFO("HandleInterrupt read shutdown\n");
|
|
shutdown_ = true;
|
|
return;
|
|
}
|
|
ASSERT((msg->data & COMMAND_MASK) != 0);
|
|
LOG_INFO("HandleInterrupt command:\n");
|
|
Socket* socket = reinterpret_cast<Socket*>(msg->id);
|
|
RefCntReleaseScope<Socket> rs(socket);
|
|
if (socket->fd() == -1) {
|
|
return;
|
|
}
|
|
IOHandle* io_handle = reinterpret_cast<IOHandle*>(socket->fd());
|
|
const intptr_t fd = io_handle->fd();
|
|
DescriptorInfo* di =
|
|
GetDescriptorInfo(socket->fd(), IS_LISTENING_SOCKET(msg->data));
|
|
ASSERT(io_handle == di->io_handle());
|
|
if (IS_COMMAND(msg->data, kShutdownReadCommand)) {
|
|
ASSERT(!di->IsListeningSocket());
|
|
// Close the socket for reading.
|
|
LOG_INFO("\tSHUT_RD: %ld\n", fd);
|
|
VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_RD));
|
|
} else if (IS_COMMAND(msg->data, kShutdownWriteCommand)) {
|
|
ASSERT(!di->IsListeningSocket());
|
|
// Close the socket for writing.
|
|
LOG_INFO("\tSHUT_WR: %ld\n", fd);
|
|
VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_WR));
|
|
} else if (IS_COMMAND(msg->data, kCloseCommand)) {
|
|
// Close the socket and free system resources and move on to next
|
|
// message.
|
|
const intptr_t old_mask = di->Mask();
|
|
Dart_Port port = msg->dart_port;
|
|
if (port != ILLEGAL_PORT) {
|
|
di->RemovePort(port);
|
|
}
|
|
const intptr_t new_mask = di->Mask();
|
|
UpdatePort(old_mask, di);
|
|
|
|
LOG_INFO("\tCLOSE: %ld: %lx -> %lx\n", fd, old_mask, new_mask);
|
|
if (di->IsListeningSocket()) {
|
|
// We only close the socket file descriptor from the operating
|
|
// system if there are no other dart socket objects which
|
|
// are listening on the same (address, port) combination.
|
|
ListeningSocketRegistry* registry = ListeningSocketRegistry::Instance();
|
|
|
|
MutexLocker locker(registry->mutex());
|
|
|
|
if (registry->CloseSafe(socket)) {
|
|
ASSERT(new_mask == 0);
|
|
socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
|
|
di->Close();
|
|
delete di;
|
|
socket->CloseFd();
|
|
}
|
|
socket->SetClosedFd();
|
|
} else {
|
|
ASSERT(new_mask == 0);
|
|
socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
|
|
di->Close();
|
|
delete di;
|
|
socket->CloseFd();
|
|
}
|
|
if (port != 0) {
|
|
const bool success = DartUtils::PostInt32(port, 1 << kDestroyedEvent);
|
|
if (!success) {
|
|
LOG_INFO("Failed to post destroy event to port %ld\n", port);
|
|
}
|
|
}
|
|
} else if (IS_COMMAND(msg->data, kReturnTokenCommand)) {
|
|
const int count = TOKEN_COUNT(msg->data);
|
|
const intptr_t old_mask = di->Mask();
|
|
LOG_INFO("\t Return Token: %ld: %lx\n", fd, old_mask);
|
|
di->ReturnTokens(msg->dart_port, count);
|
|
UpdatePort(old_mask, di);
|
|
} else if (IS_COMMAND(msg->data, kSetEventMaskCommand)) {
|
|
// `events` can only have kInEvent/kOutEvent flags set.
|
|
const intptr_t events = msg->data & EVENT_MASK;
|
|
ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
|
|
|
|
const intptr_t old_mask = di->Mask();
|
|
LOG_INFO("\t Set Event Mask: %ld: %lx %lx\n", fd, old_mask,
|
|
msg->data & EVENT_MASK);
|
|
di->SetPortAndMask(msg->dart_port, msg->data & EVENT_MASK);
|
|
UpdatePort(old_mask, di);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void EventHandlerImplementation::HandlePacket(zx_port_packet_t* pkt) {
|
|
if (pkt->key == 0) {
|
|
LOG_ERR("HandlePacket called with pkt->key==0");
|
|
return;
|
|
}
|
|
LOG_INFO("HandlePacket: Got event packet: key=%lx\n", pkt->key);
|
|
LOG_INFO("HandlePacket: Got event packet: type=%x\n", pkt->type);
|
|
LOG_INFO("HandlePacket: Got event packet: status=%d\n", pkt->status);
|
|
|
|
if (pkt->type == ZX_PKT_TYPE_USER) {
|
|
ASSERT(pkt->key == kInterruptPacketKey);
|
|
InterruptMessage* msg = reinterpret_cast<InterruptMessage*>(&pkt->user);
|
|
HandleInterrupt(msg);
|
|
return;
|
|
}
|
|
|
|
if (pkt->type != ZX_PKT_TYPE_SIGNAL_ONE) {
|
|
LOG_ERR("HandlePacket: Got unexpected packet type: key=%x\n", pkt->type);
|
|
return;
|
|
}
|
|
|
|
// Handle pkt->type == ZX_PKT_TYPE_SIGNAL_ONE
|
|
LOG_INFO("HandlePacket: Got event packet: observed = %x\n",
|
|
pkt->signal.observed);
|
|
LOG_INFO("HandlePacket: Got event packet: count = %ld\n", pkt->signal.count);
|
|
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(pkt->key);
|
|
zx_signals_t observed = pkt->signal.observed;
|
|
const intptr_t old_mask = di->Mask();
|
|
const uint32_t epoll_event = di->io_handle()->WaitEnd(observed);
|
|
intptr_t event_mask = IOHandle::EpollEventsToMask(epoll_event);
|
|
if ((event_mask & (1 << kErrorEvent)) != 0) {
|
|
di->NotifyAllDartPorts(event_mask);
|
|
} else if (event_mask != 0) {
|
|
event_mask = di->io_handle()->ToggleEvents(event_mask);
|
|
if (event_mask != 0) {
|
|
Dart_Port port = di->NextNotifyDartPort(event_mask);
|
|
ASSERT(port != 0);
|
|
bool success = DartUtils::PostInt32(port, event_mask);
|
|
if (!success) {
|
|
// This can happen if e.g. the isolate that owns the port has died
|
|
// for some reason.
|
|
LOG_INFO("Failed to post event to port %ld\n", port);
|
|
}
|
|
}
|
|
}
|
|
UpdatePort(old_mask, di);
|
|
}
|
|
|
|
int64_t EventHandlerImplementation::GetTimeout() const {
|
|
if (!timeout_queue_.HasTimeout()) {
|
|
return kInfinityTimeout;
|
|
}
|
|
int64_t millis =
|
|
timeout_queue_.CurrentTimeout() - TimerUtils::GetCurrentMonotonicMillis();
|
|
return (millis < 0) ? 0 : millis;
|
|
}
|
|
|
|
void EventHandlerImplementation::HandleTimeout() {
|
|
if (timeout_queue_.HasTimeout()) {
|
|
int64_t millis = timeout_queue_.CurrentTimeout() -
|
|
TimerUtils::GetCurrentMonotonicMillis();
|
|
if (millis <= 0) {
|
|
DartUtils::PostNull(timeout_queue_.CurrentPort());
|
|
timeout_queue_.RemoveCurrent();
|
|
}
|
|
}
|
|
}
|
|
|
|
void EventHandlerImplementation::Poll(uword args) {
|
|
EventHandler* handler = reinterpret_cast<EventHandler*>(args);
|
|
EventHandlerImplementation* handler_impl = &handler->delegate_;
|
|
ASSERT(handler_impl != nullptr);
|
|
|
|
zx_port_packet_t pkt;
|
|
while (!handler_impl->shutdown_) {
|
|
int64_t millis = handler_impl->GetTimeout();
|
|
ASSERT((millis == kInfinityTimeout) || (millis >= 0));
|
|
|
|
LOG_INFO("zx_port_wait(millis = %ld)\n", millis);
|
|
zx_status_t status = zx_port_wait(handler_impl->port_handle_,
|
|
millis == kInfinityTimeout
|
|
? ZX_TIME_INFINITE
|
|
: zx_deadline_after(ZX_MSEC(millis)),
|
|
&pkt);
|
|
if (status == ZX_ERR_TIMED_OUT) {
|
|
handler_impl->HandleTimeout();
|
|
} else if (status != ZX_OK) {
|
|
FATAL("zx_port_wait failed: %s\n", zx_status_get_string(status));
|
|
} else {
|
|
handler_impl->HandleTimeout();
|
|
handler_impl->HandlePacket(&pkt);
|
|
}
|
|
}
|
|
DEBUG_ASSERT(ReferenceCounted<Socket>::instances() == 0);
|
|
handler->NotifyShutdownDone();
|
|
}
|
|
|
|
void EventHandlerImplementation::Start(EventHandler* handler) {
|
|
Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll,
|
|
reinterpret_cast<uword>(handler));
|
|
}
|
|
|
|
void EventHandlerImplementation::Shutdown() {
|
|
SendData(kShutdownId, 0, 0);
|
|
}
|
|
|
|
void EventHandlerImplementation::SendData(intptr_t id,
|
|
Dart_Port dart_port,
|
|
int64_t data) {
|
|
WakeupHandler(id, dart_port, data);
|
|
}
|
|
|
|
void* EventHandlerImplementation::GetHashmapKeyFromFd(intptr_t fd) {
|
|
// The hashmap does not support keys with value 0.
|
|
return reinterpret_cast<void*>(fd + 1);
|
|
}
|
|
|
|
uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) {
|
|
// The hashmap does not support keys with value 0.
|
|
return dart::Utils::WordHash(fd + 1);
|
|
}
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|
|
|
|
#endif // defined(DART_HOST_OS_FUCHSIA)
|