| // Copyright (c) 2013, 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_WINDOWS) | |
| #include "bin/eventhandler.h" | |
| #include "bin/eventhandler_win.h" | |
| #include <fcntl.h> // NOLINT | |
| #include <io.h> // NOLINT | |
| #include <mswsock.h> // NOLINT | |
| #include <winsock2.h> // NOLINT | |
| #include <ws2tcpip.h> // NOLINT | |
| #include <utility> | |
| #include "bin/builtin.h" | |
| #include "bin/dartutils.h" | |
| #include "bin/lockers.h" | |
| #include "bin/process.h" | |
| #include "bin/socket.h" | |
| #include "bin/thread.h" | |
| #include "bin/utils.h" | |
| #include "platform/syslog.h" | |
| #include "platform/utils.h" | |
| namespace dart { | |
| namespace bin { | |
| // kBufferSize must be >= kMaxUDPPackageLength so that a complete UDP packet | |
| // can fit in the buffer. | |
| static constexpr int kBufferSize = 64 * 1024; | |
| static constexpr int kStdOverlappedBufferSize = 16 * 1024; | |
| static constexpr int kMaxUDPPackageLength = 64 * 1024; | |
| // For AcceptEx there needs to be buffer storage for address | |
| // information for two addresses (local and remote address). The | |
| // AcceptEx documentation says: "This value must be at least 16 | |
| // bytes more than the maximum address length for the transport | |
| // protocol in use." | |
| static constexpr int kAcceptExAddressAdditionalBytes = 16; | |
| static constexpr int kAcceptExAddressStorageSize = | |
| sizeof(SOCKADDR_STORAGE) + kAcceptExAddressAdditionalBytes; | |
| static constexpr intptr_t kOutEventMask = 1 << kOutEvent; | |
| static constexpr intptr_t kInEventMask = 1 << kInEvent; | |
| static bool DispatchEventIfEnabled(Handle* handle, intptr_t event_mask) { | |
| if ((handle->Mask() & event_mask) != 0) { | |
| DartUtils::PostInt32(handle->NextNotifyDartPort(event_mask), event_mask); | |
| return true; | |
| } | |
| return false; | |
| } | |
| static bool DispatchOutEventIfEnabled(Handle* handle) { | |
| return DispatchEventIfEnabled(handle, kOutEventMask); | |
| } | |
| static bool DispatchInEventIfEnabled(Handle* handle) { | |
| return DispatchEventIfEnabled(handle, kInEventMask); | |
| } | |
| OverlappedBuffer::OverlappedBuffer(Handle* handle, | |
| int buffer_size, | |
| Operation operation) | |
| : buflen_(buffer_size), operation_(operation), handle_(handle) { | |
| memset(GetBufferStart(), 0, GetBufferSize()); | |
| if (operation == kRecvFrom) { | |
| // Reserve part of the buffer for the length of source address. | |
| ASSERT(buflen_ > sizeof(RawAddr)); | |
| buflen_ -= sizeof(RawAddr); | |
| from_ = reinterpret_cast<RawAddr*>(GetBufferStart() + GetBufferSize()); | |
| // WSARecv needs to know how many bytes it can write. | |
| from_->size = sizeof(sockaddr_storage); | |
| } else { | |
| from_ = nullptr; | |
| } | |
| index_ = 0; | |
| data_length_ = 0; | |
| // Retain handle for the duration of the operation. | |
| handle->Retain(); | |
| } | |
| OverlappedBuffer::~OverlappedBuffer() { | |
| // If handle was not detached from the buffer release the reference | |
| // we were holding to it. | |
| if (handle_ != nullptr) { | |
| handle_->Release(); | |
| } | |
| if (client_ != INVALID_SOCKET) { | |
| closesocket(client_); | |
| } | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateBuffer( | |
| Handle* handle, | |
| int buffer_size, | |
| Operation operation) { | |
| OverlappedBuffer* buffer = | |
| new (buffer_size) OverlappedBuffer(handle, buffer_size, operation); | |
| return std::unique_ptr<OverlappedBuffer>{buffer}; | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateAcceptBuffer( | |
| Handle* handle, | |
| ADDRESS_FAMILY sa_family) { | |
| auto result = | |
| AllocateBuffer(handle, 2 * kAcceptExAddressStorageSize, kAccept); | |
| result->client_ = | |
| socket(sa_family, SOCK_STREAM, sa_family == AF_UNIX ? 0 : IPPROTO_TCP); | |
| return result; | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateReadBuffer( | |
| Handle* handle, | |
| int buffer_size) { | |
| return AllocateBuffer(handle, buffer_size, kRead); | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateRecvFromBuffer( | |
| Handle* handle, | |
| int buffer_size) { | |
| // For calling recvfrom additional buffer space is needed for the source | |
| // address information. | |
| buffer_size += sizeof(RawAddr); | |
| return AllocateBuffer(handle, buffer_size, kRecvFrom); | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateWriteBuffer( | |
| Handle* handle, | |
| int buffer_size) { | |
| return AllocateBuffer(handle, buffer_size, kWrite); | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateSendToBuffer( | |
| Handle* handle, | |
| int buffer_size) { | |
| return AllocateBuffer(handle, buffer_size, kSendTo); | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateDisconnectBuffer( | |
| Handle* handle) { | |
| return AllocateBuffer(handle, 0, kDisconnect); | |
| } | |
| std::unique_ptr<OverlappedBuffer> OverlappedBuffer::AllocateConnectBuffer( | |
| Handle* handle) { | |
| return AllocateBuffer(handle, 0, kConnect); | |
| } | |
| // CONTAINING_RECORD definition from winnt.h triggers ubsan | |
| // "member access within null pointer of type" error. | |
| #define CONTAINING_RECORD(address, type, field) \ | |
| reinterpret_cast<type*>((PCHAR)(address) - (ULONG_PTR)offsetof(type, field)) | |
| OverlappedBuffer* OverlappedBuffer::GetFromOverlapped(OVERLAPPED* overlapped) { | |
| OverlappedBuffer* buffer = | |
| CONTAINING_RECORD(overlapped, OverlappedBuffer, overlapped_); | |
| return buffer; | |
| } | |
| int OverlappedBuffer::Read(void* buffer, int num_bytes) { | |
| if (num_bytes > GetRemainingLength()) { | |
| num_bytes = GetRemainingLength(); | |
| } | |
| memmove(buffer, GetBufferStart() + index_, num_bytes); | |
| index_ += num_bytes; | |
| return num_bytes; | |
| } | |
| int OverlappedBuffer::Write(const void* buffer, int num_bytes) { | |
| ASSERT(num_bytes == buflen_); | |
| memmove(GetBufferStart(), buffer, num_bytes); | |
| data_length_ = num_bytes; | |
| return num_bytes; | |
| } | |
| int OverlappedBuffer::GetRemainingLength() { | |
| ASSERT(operation_ == kRead || operation_ == kRecvFrom); | |
| return data_length_ - index_; | |
| } | |
| Handle::Handle(intptr_t handle, | |
| Type type, | |
| Handle::SupportsOverlappedIO supports_overlapped_io /* = kYes */) | |
| : ReferenceCounted(), | |
| DescriptorInfoBase(handle), | |
| monitor_(), | |
| type_(type), | |
| handle_(reinterpret_cast<HANDLE>(handle)), | |
| data_ready_() { | |
| if (supports_overlapped_io == SupportsOverlappedIO::kYes) { | |
| EventHandler::delegate()->AssociateWithCompletionPort(this); | |
| } else { | |
| flags_ |= 1 << kDoesNotSupportOverlappedIO; | |
| } | |
| } | |
| Handle::~Handle() {} | |
| void Handle::Close() { | |
| MonitorLocker ml(&monitor_); | |
| CloseLocked(&ml); | |
| } | |
| void Handle::CloseLocked(MonitorLocker* ml) { | |
| if (!supports_overlapped_io()) { | |
| // If the handle uses synchronous I/O (e.g. stdin), cancel any pending | |
| // operation before closing the handle, so the read thread is not blocked. | |
| BOOL result = CancelIoEx(handle_, nullptr); | |
| ASSERT(result || (GetLastError() == ERROR_NOT_FOUND)); | |
| } | |
| if (!IsClosing()) { | |
| // Close the socket and set the closing state. This close method can be | |
| // called again if this socket has pending IO operations in flight. | |
| MarkClosing(); | |
| // Perform handle type specific closing. | |
| DoCloseLocked(ml); | |
| } | |
| ASSERT(IsHandleClosed()); | |
| } | |
| void Handle::DoCloseLocked(MonitorLocker* ml) { | |
| if (!IsHandleClosed()) { | |
| CloseHandle(handle_); | |
| handle_ = INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| bool Handle::HasPendingRead() { | |
| return pending_read_ != nullptr; | |
| } | |
| bool Handle::HasPendingWrite() { | |
| return pending_write_ != nullptr; | |
| } | |
| void Handle::WaitForReadThreadStarted() { | |
| MonitorLocker ml(&monitor_); | |
| while (read_thread_starting_) { | |
| ml.Wait(); | |
| } | |
| } | |
| void Handle::WaitForReadThreadFinished() { | |
| HANDLE to_join = nullptr; | |
| { | |
| MonitorLocker ml(&monitor_); | |
| if (read_thread_ != INVALID_HANDLE_VALUE) { | |
| while (!read_thread_finished_) { | |
| ml.Wait(); | |
| } | |
| read_thread_finished_ = false; | |
| to_join = read_thread_; | |
| read_thread_ = INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| if (to_join != nullptr) { | |
| // Join the read thread. | |
| DWORD res = WaitForSingleObject(to_join, INFINITE); | |
| CloseHandle(to_join); | |
| ASSERT(res == WAIT_OBJECT_0); | |
| } | |
| } | |
| void Handle::ReadComplete(std::unique_ptr<OverlappedBuffer> buffer) { | |
| WaitForReadThreadStarted(); | |
| { | |
| MonitorLocker ml(&monitor_); | |
| // Currently only one outstanding read at the time. | |
| ASSERT(pending_read_ == buffer.get()); | |
| ASSERT(data_ready_ == nullptr); | |
| if (!IsClosing()) { | |
| data_ready_ = std::move(buffer); | |
| } | |
| pending_read_ = nullptr; | |
| } | |
| WaitForReadThreadFinished(); | |
| } | |
| void Handle::WriteComplete(std::unique_ptr<OverlappedBuffer> buffer) { | |
| MonitorLocker ml(&monitor_); | |
| // Currently only one outstanding write at the time. | |
| ASSERT(pending_write_ == buffer.get()); | |
| pending_write_ = nullptr; | |
| } | |
| // Helper method which returns a real HANDLE for the current thread. | |
| static HANDLE GetCurrentThreadHandle() { | |
| HANDLE thread_handle = INVALID_HANDLE_VALUE; | |
| if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), | |
| GetCurrentProcess(), &thread_handle, | |
| /*dwDesiredAccess=*/0, FALSE, DUPLICATE_SAME_ACCESS)) { | |
| FATAL("Failed to obtain thread handle"); | |
| } | |
| return thread_handle; | |
| } | |
| void Handle::NotifyReadThreadStarted() { | |
| MonitorLocker ml(&monitor_); | |
| ASSERT(read_thread_starting_); | |
| ASSERT(read_thread_ == INVALID_HANDLE_VALUE); | |
| read_thread_ = GetCurrentThreadHandle(); | |
| read_thread_starting_ = false; | |
| ml.Notify(); | |
| } | |
| void Handle::NotifyReadThreadFinished() { | |
| MonitorLocker ml(&monitor_); | |
| ASSERT(!read_thread_finished_); | |
| ASSERT(read_thread_ != INVALID_HANDLE_VALUE); | |
| read_thread_finished_ = true; | |
| ml.Notify(); | |
| } | |
| void Handle::ReadSyncCompleteAsync() { | |
| NotifyReadThreadStarted(); | |
| ASSERT(HasPendingRead()); | |
| ASSERT(pending_read_->GetBufferSize() >= kStdOverlappedBufferSize); | |
| DWORD buffer_size = pending_read_->GetBufferSize(); | |
| if (GetFileType(handle_) == FILE_TYPE_CHAR) { | |
| buffer_size = kStdOverlappedBufferSize; | |
| } | |
| char* buffer_start = pending_read_->GetBufferStart(); | |
| DWORD bytes_read = 0; | |
| BOOL ok = | |
| ::ReadFile(handle_, buffer_start, buffer_size, &bytes_read, nullptr); | |
| if (!ok) { | |
| bytes_read = 0; | |
| } | |
| OVERLAPPED* overlapped = pending_read_->GetCleanOverlapped(); | |
| ok = PostQueuedCompletionStatus(EventHandler::delegate()->completion_port(), | |
| bytes_read, reinterpret_cast<ULONG_PTR>(this), | |
| overlapped); | |
| if (!ok) { | |
| FATAL("PostQueuedCompletionStatus failed"); | |
| } | |
| NotifyReadThreadFinished(); | |
| } | |
| bool Handle::IssueReadLocked(MonitorLocker* ml) { | |
| ASSERT(type_ != kListenSocket); | |
| ASSERT(!HasPendingRead()); | |
| auto buffer = OverlappedBuffer::AllocateReadBuffer(this, kBufferSize); | |
| // Must initialize pending_read_ before issuing async operation, because | |
| // completion will race with this code. | |
| pending_read_ = buffer.get(); | |
| if (supports_overlapped_io()) { | |
| BOOL ok = | |
| ::ReadFile(handle_, buffer->GetBufferStart(), buffer->GetBufferSize(), | |
| nullptr, buffer->GetCleanOverlapped()); | |
| if (ok || (GetLastError() == ERROR_IO_PENDING)) { | |
| // Completing asynchronously. | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_read_ = nullptr; | |
| HandleIssueError(); | |
| return false; | |
| } else { | |
| // Completing asynchronously through thread. | |
| Retain(); | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| read_thread_starting_ = true; | |
| Thread::Start( | |
| "dart:io ReadFile", | |
| [](uword args) { | |
| auto handle = reinterpret_cast<Handle*>(args); | |
| handle->ReadSyncCompleteAsync(); | |
| handle->Release(); | |
| }, | |
| reinterpret_cast<uword>(this)); | |
| return true; | |
| } | |
| } | |
| bool Handle::IssueRecvFromLocked(MonitorLocker* ml) { | |
| return false; | |
| } | |
| bool Handle::IssueWriteLocked(MonitorLocker* ml, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| ASSERT(type_ != kListenSocket); | |
| ASSERT(!HasPendingWrite()); | |
| ASSERT(buffer->operation() == OverlappedBuffer::kWrite); | |
| // Must initialize pending_write_ before issuing asynchronous operation, | |
| // because completion will race with this code. | |
| pending_write_ = buffer.get(); | |
| BOOL ok = | |
| ::WriteFile(handle_, buffer->GetBufferStart(), buffer->GetBufferSize(), | |
| nullptr, buffer->GetCleanOverlapped()); | |
| if (ok || (GetLastError() == ERROR_IO_PENDING)) { | |
| // Completing asynchronously. | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_write_ = nullptr; | |
| HandleIssueError(); | |
| return false; | |
| } | |
| bool Handle::IssueSendToLocked(MonitorLocker* ml, | |
| std::unique_ptr<OverlappedBuffer> buffer, | |
| const RawAddr& addr) { | |
| return false; | |
| } | |
| static void HandleClosed(Handle* handle) { | |
| if (!handle->IsClosing()) { | |
| handle->NotifyAllDartPorts(1 << kCloseEvent); | |
| } | |
| } | |
| static void HandleError(Handle* handle) { | |
| handle->set_last_error(WSAGetLastError()); | |
| handle->MarkError(); | |
| if (!handle->IsClosing()) { | |
| handle->NotifyAllDartPorts(1 << kErrorEvent); | |
| } | |
| } | |
| void Handle::HandleIssueError() { | |
| DWORD error = GetLastError(); | |
| if (error == ERROR_BROKEN_PIPE) { | |
| HandleClosed(this); | |
| } else { | |
| HandleError(this); | |
| } | |
| SetLastError(error); | |
| } | |
| bool FileHandle::IsClosed() { | |
| return IsClosing() && !HasPendingRead() && !HasPendingWrite(); | |
| } | |
| void DirectoryWatchHandle::Start() { | |
| MonitorLocker ml(&monitor_); | |
| IssueReadLocked(&ml); | |
| } | |
| bool DirectoryWatchHandle::IsClosed() { | |
| return IsClosing() && !HasPendingRead(); | |
| } | |
| bool DirectoryWatchHandle::IssueReadLocked(MonitorLocker* ml) { | |
| // It may have been started before, as we start the directory-handler when | |
| // we create it. | |
| if (HasPendingRead() || (data_ready_ != nullptr)) { | |
| return true; | |
| } | |
| auto buffer = OverlappedBuffer::AllocateReadBuffer(this, kBufferSize); | |
| // Set up pending_read_ before ReadDirectoryChangesW because it might be | |
| // needed in ReadComplete invoked on event loop thread right away if data is | |
| // also ready right away. | |
| pending_read_ = buffer.get(); | |
| BOOL ok = ReadDirectoryChangesW( | |
| handle_, buffer->GetBufferStart(), buffer->GetBufferSize(), recursive_, | |
| events_, nullptr, buffer->GetCleanOverlapped(), nullptr); | |
| if (ok || (GetLastError() == ERROR_IO_PENDING)) { | |
| // Completing asynchronously. | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_read_ = nullptr; | |
| return false; | |
| } | |
| void DirectoryWatchHandle::Stop() { | |
| MonitorLocker ml(&monitor_); | |
| // Stop the outstanding read, so we can close the handle. | |
| if (HasPendingRead()) { | |
| CancelIoEx(handle(), pending_read_->GetCleanOverlapped()); | |
| // Don't dispose of the buffer, as it will still complete (with length 0). | |
| } | |
| DoCloseLocked(&ml); | |
| } | |
| void SocketHandle::HandleIssueError() { | |
| int error = WSAGetLastError(); | |
| if (error == WSAECONNRESET) { | |
| HandleClosed(this); | |
| } else { | |
| HandleError(this); | |
| } | |
| WSASetLastError(error); | |
| } | |
| bool ListenSocket::IssueAcceptLocked(MonitorLocker* ml) { | |
| auto buffer = OverlappedBuffer::AllocateAcceptBuffer(this, sa_family_); | |
| DWORD received; | |
| BOOL ok; | |
| ok = EventHandler::delegate()->accept_ex()( | |
| socket(), buffer->client(), buffer->GetBufferStart(), | |
| 0, // For now don't receive data with accept. | |
| kAcceptExAddressStorageSize, kAcceptExAddressStorageSize, &received, | |
| buffer->GetCleanOverlapped()); | |
| if (ok || WSAGetLastError() == WSA_IO_PENDING) { | |
| pending_accept_count_++; | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| HandleError(this); | |
| return false; | |
| } | |
| bool ListenSocket::StartAccept() { | |
| MonitorLocker ml(&monitor_); | |
| // Always keep 5 outstanding accepts going, to enhance performance. | |
| for (intptr_t i = 0; i < kMinIssuedAccepts; i++) { | |
| if (!IssueAcceptLocked(&ml)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| void ListenSocket::AcceptComplete(std::unique_ptr<OverlappedBuffer> buffer) { | |
| MonitorLocker ml(&monitor_); | |
| if (!IsClosing()) { | |
| // Update the accepted socket to support the full range of API calls. | |
| SOCKET s = socket(); | |
| int rc = setsockopt(buffer->client(), SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, | |
| reinterpret_cast<char*>(&s), sizeof(s)); | |
| if (rc == NO_ERROR) { | |
| SOCKET client = buffer->client(); | |
| buffer->DetachClient(); | |
| // getpeername() returns incorrect results when used with a socket that | |
| // was accepted using overlapped I/O. AcceptEx includes the remote | |
| // address in its result so retrieve it using GetAcceptExSockaddrs and | |
| // save it. | |
| LPSOCKADDR local_addr; | |
| int local_addr_length; | |
| LPSOCKADDR remote_addr; | |
| int remote_addr_length; | |
| EventHandler::delegate()->get_accept_ex_sockaddrs()( | |
| buffer->GetBufferStart(), 0, kAcceptExAddressStorageSize, | |
| kAcceptExAddressStorageSize, &local_addr, &local_addr_length, | |
| &remote_addr, &remote_addr_length); | |
| auto raw_remote_addr = std::make_unique<RawAddr>(); | |
| memmove(&raw_remote_addr->addr, remote_addr, remote_addr_length); | |
| raw_remote_addr->size = remote_addr_length; | |
| // Insert the accepted socket into the list. | |
| ClientSocket* client_socket = | |
| new ClientSocket(client, std::move(raw_remote_addr)); | |
| client_socket->mark_connected(); | |
| if (accepted_head_ == nullptr) { | |
| accepted_head_ = client_socket; | |
| accepted_tail_ = client_socket; | |
| } else { | |
| ASSERT(accepted_tail_ != nullptr); | |
| accepted_tail_->set_next(client_socket); | |
| accepted_tail_ = client_socket; | |
| } | |
| accepted_count_++; | |
| } | |
| } | |
| pending_accept_count_--; | |
| DispatchCompletedAcceptsLocked(&ml); | |
| } | |
| void ListenSocket::DispatchCompletedAcceptsLocked(MonitorLocker* ml) { | |
| if (IsClosing()) { | |
| return; | |
| } | |
| for (int i = 0; i < accepted_count(); i++) { | |
| if (!DispatchInEventIfEnabled(this)) { | |
| break; | |
| } | |
| } | |
| } | |
| static void NotifyDestroyedIfClosed(Handle* handle) { | |
| if (handle->IsClosed()) { | |
| handle->NotifyAllDartPorts(1 << kDestroyedEvent); | |
| handle->RemoveAllPorts(); | |
| } | |
| } | |
| void ListenSocket::DoCloseLocked(MonitorLocker* ml) { | |
| closesocket(socket()); | |
| handle_ = INVALID_HANDLE_VALUE; | |
| // Get rid of connections already accepted. | |
| ClientSocket* next_client = accepted_head_; | |
| while (next_client != nullptr) { | |
| ClientSocket* client = next_client; | |
| next_client = client->next(); | |
| client->set_next(nullptr); | |
| client->Close(); | |
| NotifyDestroyedIfClosed(client); | |
| client->Release(); | |
| } | |
| accepted_head_ = accepted_tail_ = nullptr; | |
| accepted_count_ = 0; | |
| } | |
| ClientSocket* ListenSocket::Accept() { | |
| MonitorLocker ml(&monitor_); | |
| ClientSocket* result = nullptr; | |
| if (accepted_head_ != nullptr) { | |
| result = accepted_head_; | |
| accepted_head_ = accepted_head_->next(); | |
| if (accepted_head_ == nullptr) { | |
| accepted_tail_ = nullptr; | |
| } | |
| result->set_next(nullptr); | |
| accepted_count_--; | |
| } | |
| // We have less than 5 pending accepts and are not closing try to queue | |
| // another accept. | |
| if (!IsClosing() && (pending_accept_count_ < kMinIssuedAccepts)) { | |
| IssueAcceptLocked(&ml); | |
| } | |
| return result; | |
| } | |
| bool ListenSocket::IsClosed() { | |
| return IsClosing() && !HasPendingAccept(); | |
| } | |
| intptr_t Handle::Available() { | |
| MonitorLocker ml(&monitor_); | |
| if (data_ready_ == nullptr) { | |
| return 0; | |
| } | |
| return data_ready_->GetRemainingLength(); | |
| } | |
| bool Handle::DataReady() { | |
| return data_ready_ != nullptr; | |
| } | |
| intptr_t Handle::Read(void* buffer, intptr_t num_bytes) { | |
| MonitorLocker ml(&monitor_); | |
| if (data_ready_ == nullptr) { | |
| return 0; | |
| } | |
| num_bytes = | |
| data_ready_->Read(buffer, Utils::Minimum<intptr_t>(num_bytes, INT_MAX)); | |
| if (data_ready_->IsEmpty()) { | |
| data_ready_ = nullptr; | |
| if (!IsClosing() && !IsClosedRead()) { | |
| IssueReadLocked(&ml); | |
| } | |
| } | |
| return num_bytes; | |
| } | |
| intptr_t Handle::RecvFrom(void* buffer, intptr_t num_bytes, RawAddr* from) { | |
| MonitorLocker ml(&monitor_); | |
| if (data_ready_ == nullptr) { | |
| return 0; | |
| } | |
| num_bytes = | |
| data_ready_->Read(buffer, Utils::Minimum<intptr_t>(num_bytes, INT_MAX)); | |
| *from = *data_ready_->from(); | |
| // Always dispose of the buffer, as UDP messages must be read in their | |
| // entirety to match how recvfrom works in a socket. | |
| data_ready_ = nullptr; | |
| if (!IsClosing() && !IsClosedRead()) { | |
| IssueRecvFromLocked(&ml); | |
| } | |
| return num_bytes; | |
| } | |
| intptr_t Handle::Write(const void* data, intptr_t num_bytes) { | |
| MonitorLocker ml(&monitor_); | |
| if (HasPendingWrite() || IsClosed()) { | |
| return 0; | |
| } | |
| if (num_bytes > kBufferSize) { | |
| num_bytes = kBufferSize; | |
| } | |
| ASSERT(supports_overlapped_io()); | |
| int truncated_bytes = Utils::Minimum<intptr_t>(num_bytes, INT_MAX); | |
| auto buffer = OverlappedBuffer::AllocateWriteBuffer(this, truncated_bytes); | |
| buffer->Write(data, truncated_bytes); | |
| if (!IssueWriteLocked(&ml, std::move(buffer))) { | |
| return -1; | |
| } | |
| return truncated_bytes; | |
| } | |
| intptr_t Handle::SendTo(const void* data, | |
| intptr_t num_bytes, | |
| const RawAddr& addr) { | |
| MonitorLocker ml(&monitor_); | |
| if (HasPendingWrite() || IsClosed()) { | |
| return 0; | |
| } | |
| if (num_bytes > kBufferSize) { | |
| ASSERT(kBufferSize >= kMaxUDPPackageLength); | |
| // The provided buffer is larger than the maximum UDP datagram size so | |
| // return an error immediately. If the buffer were larger and the data were | |
| // actually passed to `WSASendTo()` then the operation would fail with | |
| // ERROR_INVALID_USER_BUFFER anyway. | |
| SetLastError(ERROR_INVALID_USER_BUFFER); | |
| return -1; | |
| } | |
| auto buffer = OverlappedBuffer::AllocateSendToBuffer(this, num_bytes); | |
| buffer->Write(data, num_bytes); | |
| if (!IssueSendToLocked(&ml, std::move(buffer), addr)) { | |
| return -1; | |
| } | |
| return num_bytes; | |
| } | |
| Mutex* StdHandle::stdin_mutex_ = new Mutex(); | |
| StdHandle* StdHandle::stdin_ = nullptr; | |
| StdHandle* StdHandle::Stdin(HANDLE handle) { | |
| MutexLocker ml(stdin_mutex_); | |
| if (stdin_ == nullptr) { | |
| stdin_ = new StdHandle(handle); | |
| } | |
| return stdin_; | |
| } | |
| void StdHandle::RunWriteLoop() { | |
| MonitorLocker ml(&monitor_); | |
| write_thread_running_ = true; | |
| thread_handle_ = GetCurrentThreadHandle(); | |
| // Notify we have started. | |
| ml.Notify(); | |
| while (write_thread_running_) { | |
| ml.Wait(Monitor::kNoTimeout); | |
| if (HasPendingWrite()) { | |
| // We woke up and had a pending write. Execute it. | |
| WriteSyncCompleteAsync(); | |
| } | |
| } | |
| write_thread_exists_ = false; | |
| ml.Notify(); | |
| } | |
| void StdHandle::WriteSyncCompleteAsync() { | |
| ASSERT(HasPendingWrite()); | |
| DWORD bytes_written = -1; | |
| BOOL ok = | |
| ::WriteFile(handle_, pending_write_->GetBufferStart(), | |
| pending_write_->GetBufferSize(), &bytes_written, nullptr); | |
| if (!ok) { | |
| bytes_written = 0; | |
| } | |
| thread_wrote_ += bytes_written; | |
| OVERLAPPED* overlapped = pending_write_->GetCleanOverlapped(); | |
| ok = PostQueuedCompletionStatus( | |
| EventHandler::delegate()->completion_port(), bytes_written, | |
| reinterpret_cast<ULONG_PTR>(this), overlapped); | |
| if (!ok) { | |
| FATAL("PostQueuedCompletionStatus failed"); | |
| } | |
| } | |
| intptr_t StdHandle::Write(const void* buffer, intptr_t num_bytes) { | |
| MonitorLocker ml(&monitor_); | |
| if (HasPendingWrite()) { | |
| return 0; | |
| } | |
| if (num_bytes > kBufferSize) { | |
| num_bytes = kBufferSize; | |
| } | |
| // In the case of stdout and stderr, OverlappedIO is not supported. | |
| // Here we'll instead use a thread, to make it async. | |
| // This code is actually never exposed to the user, as stdout and stderr is | |
| // not available as a RawSocket, but only wrapped in a Socket. | |
| // Note that we return '0', unless a thread have already completed a write. | |
| if (thread_wrote_ > 0) { | |
| if (num_bytes > thread_wrote_) { | |
| num_bytes = thread_wrote_; | |
| } | |
| thread_wrote_ -= num_bytes; | |
| return num_bytes; | |
| } | |
| if (!write_thread_exists_) { | |
| write_thread_exists_ = true; | |
| // The write thread gets a reference to the Handle, which it places in | |
| // the events it puts on the IO completion port. | |
| Retain(); | |
| Thread::Start( | |
| "dart:io WriteFile", | |
| [](uword args) { | |
| auto handle = reinterpret_cast<StdHandle*>(args); | |
| handle->RunWriteLoop(); | |
| handle->Release(); | |
| }, | |
| reinterpret_cast<uword>(this)); | |
| while (!write_thread_running_) { | |
| // Wait until we the thread is running. | |
| ml.Wait(Monitor::kNoTimeout); | |
| } | |
| } | |
| // Only queue up to INT_MAX bytes. | |
| int truncated_bytes = Utils::Minimum<intptr_t>(num_bytes, INT_MAX); | |
| // Create buffer and notify thread about the new handle. | |
| pending_write_ = | |
| OverlappedBuffer::AllocateWriteBuffer(this, truncated_bytes).release(); | |
| pending_write_->Write(buffer, truncated_bytes); | |
| ml.Notify(); | |
| return 0; | |
| } | |
| void StdHandle::DoCloseLocked(MonitorLocker* ml) { | |
| if (write_thread_exists_) { | |
| write_thread_running_ = false; | |
| ml->Notify(); | |
| while (write_thread_exists_) { | |
| ml->Wait(Monitor::kNoTimeout); | |
| } | |
| // Join the thread. | |
| DWORD res = WaitForSingleObject(thread_handle_, INFINITE); | |
| CloseHandle(thread_handle_); | |
| ASSERT(res == WAIT_OBJECT_0); | |
| } | |
| Handle::DoCloseLocked(ml); | |
| MutexLocker stdin_mutex_locker(stdin_mutex_); | |
| stdin_->Release(); | |
| StdHandle::stdin_ = nullptr; | |
| } | |
| #if defined(DEBUG) | |
| intptr_t ClientSocket::disconnecting_ = 0; | |
| #endif | |
| void ClientSocket::Shutdown(int how) { | |
| shutdown(socket(), how); | |
| if (how == SD_RECEIVE) { | |
| MarkClosedRead(); | |
| } | |
| if (how == SD_SEND) { | |
| MarkClosedWrite(); | |
| } | |
| if (how == SD_BOTH) { | |
| MarkClosedRead(); | |
| MarkClosedWrite(); | |
| } | |
| } | |
| void ClientSocket::DoCloseLocked(MonitorLocker* ml) { | |
| // Always do a shutdown before initiating a disconnect. | |
| shutdown(socket(), SD_BOTH); | |
| IssueDisconnectLocked(ml); | |
| handle_ = INVALID_HANDLE_VALUE; | |
| } | |
| bool ClientSocket::IssueReadLocked(MonitorLocker* ml) { | |
| ASSERT(!HasPendingRead()); | |
| // TODO(sgjesse): Use a MTU value here. Only the loopback adapter can | |
| // handle 64k datagrams. | |
| auto buffer = OverlappedBuffer::AllocateReadBuffer(this, 65536); | |
| DWORD flags; | |
| flags = 0; | |
| pending_read_ = buffer.get(); | |
| int rc = WSARecv(socket(), buffer->GetWASBUF(), 1, nullptr, &flags, | |
| buffer->GetCleanOverlapped(), nullptr); | |
| if ((rc == NO_ERROR) || (WSAGetLastError() == WSA_IO_PENDING)) { | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_read_ = nullptr; | |
| HandleIssueError(); | |
| return false; | |
| } | |
| bool ClientSocket::IssueWriteLocked(MonitorLocker* ml, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| ASSERT(!HasPendingWrite()); | |
| ASSERT(buffer->operation() == OverlappedBuffer::kWrite); | |
| pending_write_ = buffer.get(); | |
| int rc = WSASend(socket(), pending_write_->GetWASBUF(), 1, nullptr, 0, | |
| pending_write_->GetCleanOverlapped(), nullptr); | |
| if ((rc == NO_ERROR) || (WSAGetLastError() == WSA_IO_PENDING)) { | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_write_ = nullptr; | |
| HandleIssueError(); | |
| return false; | |
| } | |
| void ClientSocket::IssueDisconnectLocked(MonitorLocker* ml) { | |
| auto buffer = OverlappedBuffer::AllocateDisconnectBuffer(this); | |
| BOOL ok = EventHandler::delegate()->disconnect_ex()( | |
| socket(), buffer->GetCleanOverlapped(), TF_REUSE_SOCKET, 0); | |
| // DisconnectEx works like other OverlappedIO APIs, where we can get either an | |
| // immediate success or delayed operation by WSA_IO_PENDING being set. | |
| if (ok || (WSAGetLastError() != WSA_IO_PENDING)) { | |
| DisconnectComplete(); | |
| } else { | |
| // Completing asynchronously. | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| } | |
| // When the Dart side receives this event, it may decide to close its Dart | |
| // ports. When all ports are closed, the VM will shut down. The EventHandler | |
| // will then shut down. If the EventHandler shuts down before this | |
| // asynchronous disconnect finishes, this ClientSocket will be leaked. | |
| // TODO(dart:io): Retain a list of client sockets that are in the process of | |
| // disconnecting. Disconnect them forcefully, and clean up their resources | |
| // when the EventHandler shuts down. | |
| NotifyAllDartPorts(1 << kDestroyedEvent); | |
| RemoveAllPorts(); | |
| #if defined(DEBUG) | |
| disconnecting_++; | |
| #endif | |
| } | |
| void ClientSocket::DisconnectComplete() { | |
| closesocket(socket()); | |
| data_ready_ = nullptr; | |
| mark_closed(); | |
| #if defined(DEBUG) | |
| disconnecting_--; | |
| #endif | |
| } | |
| void ClientSocket::ConnectComplete() { | |
| // Update socket to support full socket API, after ConnectEx completed. | |
| setsockopt(socket(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nullptr, 0); | |
| // If the port is set, we already listen for this socket in Dart. | |
| // Handle the cases here. | |
| if (!IsClosedRead() && ((Mask() & kInEventMask) != 0)) { | |
| MonitorLocker ml(&monitor_); | |
| IssueReadLocked(&ml); | |
| } | |
| if (!IsClosedWrite()) { | |
| DispatchOutEventIfEnabled(this); | |
| } | |
| } | |
| bool ClientSocket::IsClosed() { | |
| return connected_ && closed_ && !HasPendingRead() && !HasPendingWrite(); | |
| } | |
| bool ClientSocket::PopulateRemoteAddr(RawAddr& addr) { | |
| if (!remote_addr_) { | |
| return false; | |
| } | |
| addr = *remote_addr_; | |
| return true; | |
| } | |
| bool DatagramSocket::IssueSendToLocked(MonitorLocker* ml, | |
| std::unique_ptr<OverlappedBuffer> buffer, | |
| const RawAddr& addr) { | |
| ASSERT(!HasPendingWrite()); | |
| ASSERT(buffer->operation() == OverlappedBuffer::kSendTo); | |
| pending_write_ = buffer.get(); | |
| int rc = WSASendTo(socket(), pending_write_->GetWASBUF(), 1, nullptr, 0, | |
| &addr.addr, addr.size, | |
| pending_write_->GetCleanOverlapped(), nullptr); | |
| if ((rc == NO_ERROR) || (WSAGetLastError() == WSA_IO_PENDING)) { | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_write_ = nullptr; | |
| HandleIssueError(); | |
| return false; | |
| } | |
| bool DatagramSocket::IssueRecvFromLocked(MonitorLocker* ml) { | |
| ASSERT(!HasPendingRead()); | |
| auto buffer = | |
| OverlappedBuffer::AllocateRecvFromBuffer(this, kMaxUDPPackageLength); | |
| pending_read_ = buffer.get(); | |
| DWORD flags = 0; | |
| int rc = WSARecvFrom(socket(), buffer->GetWASBUF(), 1, nullptr, &flags, | |
| &buffer->from()->addr, &buffer->from()->size, | |
| buffer->GetCleanOverlapped(), nullptr); | |
| if ((rc == NO_ERROR) || (WSAGetLastError() == WSA_IO_PENDING)) { | |
| buffer.release(); // HandleIOCompletion will take ownership. | |
| return true; | |
| } | |
| pending_read_ = nullptr; | |
| HandleIssueError(); | |
| return false; | |
| } | |
| bool DatagramSocket::IsClosed() { | |
| return IsClosing() && !HasPendingRead() && !HasPendingWrite(); | |
| } | |
| void DatagramSocket::DoCloseLocked(MonitorLocker* ml) { | |
| // Just close the socket. This will cause any queued requests to be aborted. | |
| closesocket(socket()); | |
| MarkClosedRead(); | |
| MarkClosedWrite(); | |
| handle_ = INVALID_HANDLE_VALUE; | |
| } | |
| void EventHandlerImplementation::HandleInterrupt(InterruptMessage* msg) { | |
| if (msg->id == kTimerId) { | |
| // Change of timeout request. Just set the new timeout and port as the | |
| // completion thread will use the new timeout value for its next wait. | |
| timeout_queue_.UpdateTimeout(msg->dart_port, msg->data); | |
| } else if (msg->id == kShutdownId) { | |
| shutdown_ = true; | |
| } else { | |
| Socket* socket = reinterpret_cast<Socket*>(msg->id); | |
| RefCntReleaseScope<Socket> rs(socket); | |
| if (socket->fd() == -1) { | |
| return; | |
| } | |
| Handle* handle = reinterpret_cast<Handle*>(socket->fd()); | |
| ASSERT(handle != nullptr); | |
| handle->Retain(); | |
| RefCntReleaseScope<Handle> rh(handle); | |
| MonitorLocker hl(&handle->monitor_); | |
| switch (msg->data & COMMAND_MASK) { | |
| case 1 << kReturnTokenCommand: | |
| handle->ReturnTokens(msg->dart_port, TOKEN_COUNT(msg->data)); | |
| break; | |
| case 1 << kSetEventMaskCommand: { | |
| // `events` can only have kInEvent/kOutEvent flags set. | |
| intptr_t events = msg->data & EVENT_MASK; | |
| ASSERT(0 == (events & ~(kInEventMask | kOutEventMask))); | |
| handle->SetPortAndMask(msg->dart_port, events); | |
| if (handle->is_listen_socket()) { | |
| static_cast<ListenSocket*>(handle)->DispatchCompletedAcceptsLocked( | |
| &hl); | |
| } else { | |
| // Issue a read. | |
| if ((handle->Mask() & kInEventMask) != 0) { | |
| if (handle->is_datagram_socket()) { | |
| handle->IssueRecvFromLocked(&hl); | |
| } else if (!handle->is_client_socket() || | |
| reinterpret_cast<ClientSocket*>(handle) | |
| ->is_connected()) { | |
| handle->IssueReadLocked(&hl); | |
| } | |
| } | |
| // If out events (can write events) have been requested, and there | |
| // are no pending writes, meaning any writes are already complete, | |
| // post an out event immediately. | |
| // | |
| // Client sockets are only notified if they are connected. | |
| if ((events & kOutEventMask) != 0 && !handle->HasPendingWrite()) { | |
| if (!handle->is_client_socket() || | |
| reinterpret_cast<ClientSocket*>(handle)->is_connected()) { | |
| DispatchOutEventIfEnabled(handle); | |
| } | |
| } | |
| // Similarly, if in events (can read events) have been requested, and | |
| // there is pending data available, post an in event immediately. | |
| if ((events & kInEventMask) != 0) { | |
| if (handle->data_ready_ != nullptr && | |
| !handle->data_ready_->IsEmpty()) { | |
| DispatchInEventIfEnabled(handle); | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| case 1 << kShutdownReadCommand: { | |
| ASSERT(handle->is_client_socket()); | |
| ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(handle); | |
| client_socket->Shutdown(SD_RECEIVE); | |
| break; | |
| } | |
| case 1 << kShutdownWriteCommand: { | |
| ASSERT(handle->is_client_socket()); | |
| ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(handle); | |
| client_socket->Shutdown(SD_SEND); | |
| break; | |
| } | |
| case 1 << kCloseCommand: { | |
| if (IS_SIGNAL_SOCKET(msg->data)) { | |
| Process::ClearSignalHandlerByFd(socket->fd(), socket->isolate_port()); | |
| } | |
| bool can_close_handle = true; | |
| if (handle->is_listen_socket()) { | |
| // We only close the socket file descriptor 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)) { | |
| // Other sockets are listening to the same OS socket. Do not close | |
| // OS socket, but deassociate it from the message port and tell | |
| // Dart side that this socket was destroyed. | |
| can_close_handle = false; | |
| handle->RemovePort(msg->dart_port); | |
| DartUtils::PostInt32(msg->dart_port, 1 << kDestroyedEvent); | |
| socket->SetClosedFd(); | |
| } | |
| } | |
| if (can_close_handle) { | |
| // Set response port so that kDestroyedEvent notification is | |
| // delivered to the listener. | |
| handle->SetPortAndMask(msg->dart_port, 0); | |
| handle->CloseLocked(&hl); | |
| socket->CloseFd(); | |
| } | |
| break; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| NotifyDestroyedIfClosed(handle); | |
| } | |
| } | |
| void EventHandlerImplementation::HandleAccept( | |
| ListenSocket* listen_socket, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| listen_socket->AcceptComplete(std::move(buffer)); | |
| } | |
| void EventHandlerImplementation::HandleRead( | |
| Handle* handle, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| buffer->set_data_length(bytes); | |
| handle->ReadComplete(std::move(buffer)); | |
| if (bytes > 0) { | |
| if (!handle->IsClosing()) { | |
| DispatchInEventIfEnabled(handle); | |
| } | |
| } else { | |
| handle->MarkClosedRead(); | |
| if (bytes == 0) { | |
| HandleClosed(handle); | |
| } else { | |
| HandleError(handle); | |
| } | |
| } | |
| } | |
| void EventHandlerImplementation::HandleRecvFrom( | |
| Handle* handle, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| ASSERT(handle->is_datagram_socket()); | |
| if (bytes >= 0) { | |
| buffer->set_data_length(bytes); | |
| handle->ReadComplete(std::move(buffer)); | |
| if (!handle->IsClosing()) { | |
| DispatchInEventIfEnabled(handle); | |
| } | |
| } else { | |
| HandleError(handle); | |
| } | |
| } | |
| void EventHandlerImplementation::HandleWrite( | |
| Handle* handle, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| handle->WriteComplete(std::move(buffer)); | |
| if (bytes >= 0) { | |
| if (!handle->IsError() && !handle->IsClosing()) { | |
| ASSERT(!handle->is_client_socket() || | |
| reinterpret_cast<ClientSocket*>(handle)->is_connected()); | |
| DispatchOutEventIfEnabled(handle); | |
| } | |
| } else { | |
| HandleError(handle); | |
| } | |
| } | |
| void EventHandlerImplementation::HandleDisconnect( | |
| ClientSocket* client_socket, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| client_socket->DisconnectComplete(); | |
| } | |
| void EventHandlerImplementation::HandleConnect( | |
| ClientSocket* client_socket, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer) { | |
| if (bytes < 0) { | |
| HandleError(client_socket); | |
| } else { | |
| client_socket->ConnectComplete(); | |
| } | |
| client_socket->mark_connected(); | |
| } | |
| void EventHandlerImplementation::HandleTimeout() { | |
| if (!timeout_queue_.HasTimeout()) { | |
| return; | |
| } | |
| DartUtils::PostNull(timeout_queue_.CurrentPort()); | |
| timeout_queue_.RemoveCurrent(); | |
| } | |
| static const char* OperationName(OverlappedBuffer::Operation op) { | |
| switch (op) { | |
| case OverlappedBuffer::kAccept: | |
| return "Accept"; | |
| case OverlappedBuffer::kRead: | |
| return "Read"; | |
| case OverlappedBuffer::kRecvFrom: | |
| return "RecvFrom"; | |
| case OverlappedBuffer::kWrite: | |
| return "Write"; | |
| case OverlappedBuffer::kSendTo: | |
| return "SendTo"; | |
| case OverlappedBuffer::kDisconnect: | |
| return "Disconnect"; | |
| case OverlappedBuffer::kConnect: | |
| return "Connect"; | |
| } | |
| return "?"; | |
| } | |
| void EventHandlerImplementation::HandleIOCompletion(int32_t bytes, | |
| ULONG_PTR key, | |
| OVERLAPPED* overlapped) { | |
| std::unique_ptr<OverlappedBuffer> buffer( | |
| OverlappedBuffer::GetFromOverlapped(overlapped)); | |
| Handle* handle = reinterpret_cast<Handle*>(key); | |
| RefCntReleaseScope<Handle> release(buffer->StealHandle()); | |
| switch (buffer->operation()) { | |
| case OverlappedBuffer::kAccept: { | |
| HandleAccept(static_cast<ListenSocket*>(handle), std::move(buffer)); | |
| break; | |
| } | |
| case OverlappedBuffer::kRead: { | |
| HandleRead(handle, bytes, std::move(buffer)); | |
| break; | |
| } | |
| case OverlappedBuffer::kRecvFrom: { | |
| HandleRecvFrom(handle, bytes, std::move(buffer)); | |
| break; | |
| } | |
| case OverlappedBuffer::kWrite: | |
| case OverlappedBuffer::kSendTo: { | |
| HandleWrite(handle, bytes, std::move(buffer)); | |
| break; | |
| } | |
| case OverlappedBuffer::kDisconnect: { | |
| HandleDisconnect(static_cast<ClientSocket*>(handle), bytes, | |
| std::move(buffer)); | |
| break; | |
| } | |
| case OverlappedBuffer::kConnect: { | |
| HandleConnect(static_cast<ClientSocket*>(handle), bytes, | |
| std::move(buffer)); | |
| break; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| } | |
| NotifyDestroyedIfClosed(handle); | |
| } | |
| void EventHandlerImplementation::HandleCompletionOrInterrupt( | |
| BOOL ok, | |
| DWORD bytes, | |
| ULONG_PTR key, | |
| OVERLAPPED* overlapped) { | |
| if (!ok) { | |
| // Treat ERROR_CONNECTION_ABORTED as connection closed. | |
| // The error ERROR_OPERATION_ABORTED is set for pending | |
| // accept requests for a listen socket which is closed. | |
| // ERROR_NETNAME_DELETED occurs when the client closes | |
| // the socket it is reading from. | |
| DWORD last_error = GetLastError(); | |
| if ((last_error == ERROR_CONNECTION_ABORTED) || | |
| (last_error == ERROR_OPERATION_ABORTED) || | |
| (last_error == ERROR_NETNAME_DELETED) || | |
| (last_error == ERROR_BROKEN_PIPE)) { | |
| ASSERT(bytes == 0); | |
| HandleIOCompletion(bytes, key, overlapped); | |
| } else if (last_error == ERROR_MORE_DATA) { | |
| // Don't ASSERT no bytes in this case. This can happen if the receive | |
| // buffer for datagram sockets is too small to contain a full datagram, | |
| // and in this case bytes hold the bytes that was read. | |
| HandleIOCompletion(-1, key, overlapped); | |
| } else { | |
| ASSERT(bytes == 0); | |
| HandleIOCompletion(-1, key, overlapped); | |
| } | |
| } else if (key == NULL) { | |
| // A key of nullptr signals an interrupt message. | |
| InterruptMessage* msg = reinterpret_cast<InterruptMessage*>(overlapped); | |
| HandleInterrupt(msg); | |
| delete msg; | |
| } else { | |
| HandleIOCompletion(bytes, key, overlapped); | |
| } | |
| } | |
| EventHandlerImplementation::EventHandlerImplementation() { | |
| completion_port_ = | |
| CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, NULL, 1); | |
| if (completion_port_ == nullptr) { | |
| FATAL("Completion port creation failed"); | |
| } | |
| } | |
| namespace { | |
| template <typename F> | |
| void GetSocketExtensionFunction(SOCKET socket, GUID guid, F* result) { | |
| DWORD bytes; | |
| int status = | |
| WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), | |
| result, sizeof(F), &bytes, nullptr, nullptr); | |
| if (status == SOCKET_ERROR) { | |
| FATAL("Failed to get a pointer to the extension function."); | |
| } | |
| } | |
| } // namespace | |
| void EventHandlerImplementation::InitializeSocketExtensions() { | |
| if (socket_extensions_initialized_.load()) { | |
| return; | |
| } | |
| MonitorLocker ml(&monitor_); | |
| SOCKET dummy = socket(AF_INET, SOCK_STREAM, 0); | |
| GetSocketExtensionFunction(dummy, WSAID_ACCEPTEX, &accept_ex_); | |
| GetSocketExtensionFunction(dummy, WSAID_CONNECTEX, &connect_ex_); | |
| GetSocketExtensionFunction(dummy, WSAID_DISCONNECTEX, &disconnect_ex_); | |
| GetSocketExtensionFunction(dummy, WSAID_GETACCEPTEXSOCKADDRS, | |
| &get_accept_ex_sockaddrs_); | |
| socket_extensions_initialized_.store(true); | |
| closesocket(dummy); | |
| } | |
| EventHandlerImplementation::~EventHandlerImplementation() { | |
| // Join the handler thread. | |
| DWORD res = WaitForSingleObject(handler_thread_, INFINITE); | |
| CloseHandle(handler_thread_); | |
| ASSERT(res == WAIT_OBJECT_0); | |
| CloseHandle(completion_port_); | |
| } | |
| void EventHandlerImplementation::AssociateWithCompletionPort(Handle* handle) { | |
| HANDLE result = | |
| CreateIoCompletionPort(handle->handle(), completion_port_, | |
| reinterpret_cast<ULONG_PTR>(handle), 0); | |
| if (result == nullptr) { | |
| FATAL("Failed to associate handle with completion port"); | |
| } | |
| } | |
| int64_t EventHandlerImplementation::GetTimeout() { | |
| if (!timeout_queue_.HasTimeout()) { | |
| return kInfinityTimeout; | |
| } | |
| int64_t millis = | |
| timeout_queue_.CurrentTimeout() - TimerUtils::GetCurrentMonotonicMillis(); | |
| return (millis < 0) ? 0 : millis; | |
| } | |
| void EventHandlerImplementation::SendData(intptr_t id, | |
| Dart_Port dart_port, | |
| int64_t data) { | |
| InterruptMessage* msg = new InterruptMessage; | |
| msg->id = id; | |
| msg->dart_port = dart_port; | |
| msg->data = data; | |
| BOOL ok = PostQueuedCompletionStatus(completion_port_, 0, NULL, | |
| reinterpret_cast<OVERLAPPED*>(msg)); | |
| if (!ok) { | |
| FATAL("PostQueuedCompletionStatus failed"); | |
| } | |
| } | |
| void EventHandlerImplementation::EventHandlerEntry(uword args) { | |
| EventHandler* handler = reinterpret_cast<EventHandler*>(args); | |
| EventHandlerImplementation* handler_impl = &handler->delegate_; | |
| ASSERT(handler_impl != nullptr); | |
| { | |
| MonitorLocker ml(&handler_impl->monitor_); | |
| handler_impl->handler_thread_ = GetCurrentThreadHandle(); | |
| ml.Notify(); | |
| } | |
| DWORD bytes; | |
| ULONG_PTR key; | |
| OVERLAPPED* overlapped; | |
| BOOL ok; | |
| while (!handler_impl->shutdown_) { | |
| int64_t millis = handler_impl->GetTimeout(); | |
| ASSERT(millis == kInfinityTimeout || millis >= 0); | |
| if (millis > kMaxInt32) { | |
| millis = kMaxInt32; | |
| } | |
| ASSERT(sizeof(int32_t) == sizeof(DWORD)); | |
| DWORD timeout = static_cast<DWORD>(millis); | |
| ok = GetQueuedCompletionStatus(handler_impl->completion_port(), &bytes, | |
| &key, &overlapped, timeout); | |
| if (!ok && (overlapped == nullptr)) { | |
| if (GetLastError() == ERROR_ABANDONED_WAIT_0) { | |
| // The completion port should never be closed. | |
| Syslog::Print("Completion port closed\n"); | |
| UNREACHABLE(); | |
| } else { | |
| // Timeout is signalled by false result and nullptr in overlapped. | |
| handler_impl->HandleTimeout(); | |
| } | |
| } else { | |
| handler_impl->HandleCompletionOrInterrupt(ok, bytes, key, overlapped); | |
| } | |
| } | |
| // In a Debug build, drain the IO completion port to make sure we aren't | |
| // leaking any (non-disconnecting) Handles. In a Release build, we don't care | |
| // because the VM is going down, and the asserts below are Debug-only. | |
| #if defined(DEBUG) | |
| while (true) { | |
| ok = GetQueuedCompletionStatus(handler_impl->completion_port(), &bytes, | |
| &key, &overlapped, 0); | |
| if (!ok && (overlapped == nullptr)) { | |
| // There was an error or nothing is ready. Assume the port is drained. | |
| break; | |
| } | |
| handler_impl->HandleCompletionOrInterrupt(ok, bytes, key, overlapped); | |
| } | |
| // The eventhandler thread is going down so there should be no more live | |
| // Handles or Sockets. | |
| // TODO(dart:io): It would be nice to be able to assert here that: | |
| // ReferenceCounted<Handle>::instances() == 0; | |
| // However, we cannot at the moment. See the TODO on: | |
| // ClientSocket::IssueDisconnect() | |
| // Furthermore, if the Dart program references stdin, but does not | |
| // explicitly close it, then the StdHandle for it will be leaked to here. | |
| const intptr_t stdin_leaked = (StdHandle::StdinPtr() == nullptr) ? 0 : 1; | |
| DEBUG_ASSERT(ReferenceCounted<Handle>::instances() == | |
| ClientSocket::disconnecting() + stdin_leaked); | |
| DEBUG_ASSERT(ReferenceCounted<Socket>::instances() == 0); | |
| #endif // defined(DEBUG) | |
| handler->NotifyShutdownDone(); | |
| } | |
| void EventHandlerImplementation::Start(EventHandler* handler) { | |
| Thread::Start("dart:io EventHandler", EventHandlerEntry, | |
| reinterpret_cast<uword>(handler)); | |
| { | |
| MonitorLocker ml(&monitor_); | |
| while (handler_thread_ == INVALID_HANDLE_VALUE) { | |
| ml.Wait(); | |
| } | |
| } | |
| } | |
| void EventHandlerImplementation::Shutdown() { | |
| SendData(kShutdownId, 0, 0); | |
| } | |
| } // namespace bin | |
| } // namespace dart | |
| #endif // defined(DART_HOST_OS_WINDOWS) |