88ec09f653
The asynchronous waiting mechanism used by the eventhandler thread on Fuchsia is inherently level-triggered. However, since the upper layers of the dart:io socket implementation expect edge-triggering, the Fuchsia eventhandler has to emulate edge-triggering. Previously, this edge-triggering was emulated for kInEvent and kOutEvent, but not kCloseEvent. It was not thought that simulating edge-triggering for kCloseEvent was necessary since the socket would soon be going down, and that a few redundant messages would be harmless. This turned out not to be the case. In practice, the eventhandler thread may flood the Dart thread with kCloseEvent messages. This CL adds the edge-triggering emulation for kCloseEvent. The Dart thread will no longer be flooded with kCloseEvent messages. Unfortunately, the eventhandler thread may still spin on Fuchsia's equivalent of POLLHUP signals until the Dart thread destroys the socket, as these cannot be unsubscribed from. Fixes b/150902962 Change-Id: I2784220197771508689504156a761842ac164c8e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/143888 Reviewed-by: Zichang Guo <zichangguo@google.com> Commit-Queue: Zach Anderson <zra@google.com>
663 lines
23 KiB
C++
663 lines
23 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(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 (!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 (!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(struct sockaddr* addr, socklen_t* addrlen) {
|
|
MutexLocker ml(&mutex_);
|
|
const intptr_t socket = NO_RETRY_EXPECTED(accept(fd_, addr, addrlen));
|
|
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 (!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::AsyncWait: fd = %ld\n", fd_);
|
|
// The call to fdio_unsafe_fd_to_io() in the DescriptorInfo constructor may
|
|
// have returned NULL. If it did, propagate the problem up to Dart.
|
|
if (fdio_ == NULL) {
|
|
LOG_ERR("fdio_unsafe_fd_to_io(%ld) returned NULL\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;
|
|
}
|
|
|
|
handle_ = handle;
|
|
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);
|
|
ASSERT(handle_ != ZX_HANDLE_INVALID);
|
|
zx_status_t status = zx_port_cancel(port, handle_, key);
|
|
if ((status != ZX_OK) && (status != ZX_ERR_NOT_FOUND)) {
|
|
LOG_ERR("zx_port_cancel 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 supressed, and
|
|
// for supressing future events. Events are unsupressed 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 supress 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 supress 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 supress 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 (!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);
|
|
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.
|
|
FATAL1("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(!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 != NULL);
|
|
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(entry->value);
|
|
if (di == NULL) {
|
|
// 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.
|
|
FATAL1("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) {
|
|
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;
|
|
}
|
|
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 != NULL);
|
|
|
|
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) {
|
|
FATAL1("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) {
|
|
int result =
|
|
Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll,
|
|
reinterpret_cast<uword>(handler));
|
|
if (result != 0) {
|
|
FATAL1("Failed to start event handler thread %d", result);
|
|
}
|
|
}
|
|
|
|
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(HOST_OS_FUCHSIA)
|