// 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(TARGET_OS_WINDOWS) #include "bin/eventhandler.h" #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include "bin/builtin.h" #include "bin/dartutils.h" #include "bin/log.h" #include "bin/socket.h" #include "bin/utils.h" #include "platform/thread.h" namespace dart { namespace bin { static const int kBufferSize = 64 * 1024; static const int kStdOverlappedBufferSize = 16 * 1024; static const int kInfinityTimeout = -1; static const int kTimeoutId = -1; static const int kShutdownId = -2; OverlappedBuffer* OverlappedBuffer::AllocateBuffer(int buffer_size, Operation operation) { OverlappedBuffer* buffer = new(buffer_size) OverlappedBuffer(buffer_size, operation); return buffer; } OverlappedBuffer* OverlappedBuffer::AllocateAcceptBuffer(int buffer_size) { OverlappedBuffer* buffer = AllocateBuffer(buffer_size, kAccept); return buffer; } OverlappedBuffer* OverlappedBuffer::AllocateReadBuffer(int buffer_size) { return AllocateBuffer(buffer_size, kRead); } OverlappedBuffer* OverlappedBuffer::AllocateWriteBuffer(int buffer_size) { return AllocateBuffer(buffer_size, kWrite); } OverlappedBuffer* OverlappedBuffer::AllocateDisconnectBuffer() { return AllocateBuffer(0, kDisconnect); } void OverlappedBuffer::DisposeBuffer(OverlappedBuffer* buffer) { delete buffer; } 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(); } memcpy(buffer, GetBufferStart() + index_, num_bytes); index_ += num_bytes; return num_bytes; } int OverlappedBuffer::Write(const void* buffer, int num_bytes) { ASSERT(num_bytes == buflen_); memcpy(GetBufferStart(), buffer, num_bytes); data_length_ = num_bytes; return num_bytes; } int OverlappedBuffer::GetRemainingLength() { ASSERT(operation_ == kRead); return data_length_ - index_; } Handle::Handle(HANDLE handle) : handle_(reinterpret_cast(handle)), port_(0), mask_(0), completion_port_(INVALID_HANDLE_VALUE), event_handler_(NULL), data_ready_(NULL), pending_read_(NULL), pending_write_(NULL), last_error_(NOERROR), flags_(0) { InitializeCriticalSection(&cs_); } Handle::Handle(HANDLE handle, Dart_Port port) : handle_(reinterpret_cast(handle)), port_(port), mask_(0), completion_port_(INVALID_HANDLE_VALUE), event_handler_(NULL), data_ready_(NULL), pending_read_(NULL), pending_write_(NULL), last_error_(NOERROR), flags_(0) { InitializeCriticalSection(&cs_); } Handle::~Handle() { DeleteCriticalSection(&cs_); } void Handle::Lock() { EnterCriticalSection(&cs_); } void Handle::Unlock() { LeaveCriticalSection(&cs_); } bool Handle::CreateCompletionPort(HANDLE completion_port) { completion_port_ = CreateIoCompletionPort(handle_, completion_port, reinterpret_cast(this), 0); if (completion_port_ == NULL) { Log::PrintErr("Error CreateIoCompletionPort: %d\n", GetLastError()); return false; } return true; } void Handle::Close() { ScopedLock lock(this); 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. ASSERT(handle_ != INVALID_HANDLE_VALUE); MarkClosing(); // Perform handle type specific closing. DoClose(); } } void Handle::DoClose() { CloseHandle(handle_); handle_ = INVALID_HANDLE_VALUE; } bool Handle::HasPendingRead() { ScopedLock lock(this); return pending_read_ != NULL; } bool Handle::HasPendingWrite() { ScopedLock lock(this); return pending_write_ != NULL; } void Handle::ReadComplete(OverlappedBuffer* buffer) { ScopedLock lock(this); // Currently only one outstanding read at the time. ASSERT(pending_read_ == buffer); ASSERT(data_ready_ == NULL); if (!IsClosing() && !buffer->IsEmpty()) { data_ready_ = pending_read_; } else { OverlappedBuffer::DisposeBuffer(buffer); } pending_read_ = NULL; } void Handle::WriteComplete(OverlappedBuffer* buffer) { ScopedLock lock(this); // Currently only one outstanding write at the time. ASSERT(pending_write_ == buffer); OverlappedBuffer::DisposeBuffer(buffer); pending_write_ = NULL; } static unsigned int __stdcall ReadFileThread(void* args) { Handle* handle = reinterpret_cast(args); handle->ReadSyncCompleteAsync(); return 0; } void Handle::ReadSyncCompleteAsync() { ASSERT(pending_read_ != NULL); ASSERT(pending_read_->GetBufferSize() >= kStdOverlappedBufferSize); DWORD buffer_size = pending_read_->GetBufferSize(); if (GetFileType(handle_) == FILE_TYPE_CHAR) { buffer_size = kStdOverlappedBufferSize; } DWORD bytes_read = 0; BOOL ok = ReadFile(handle_, pending_read_->GetBufferStart(), buffer_size, &bytes_read, NULL); if (!ok) { if (GetLastError() != ERROR_BROKEN_PIPE) { Log::PrintErr("ReadFile failed %d\n", GetLastError()); } bytes_read = 0; } OVERLAPPED* overlapped = pending_read_->GetCleanOverlapped(); ok = PostQueuedCompletionStatus(event_handler_->completion_port(), bytes_read, reinterpret_cast(this), overlapped); if (!ok) { FATAL("PostQueuedCompletionStatus failed"); } } bool Handle::IssueRead() { ScopedLock lock(this); ASSERT(type_ != kListenSocket); ASSERT(pending_read_ == NULL); OverlappedBuffer* buffer = OverlappedBuffer::AllocateReadBuffer(kBufferSize); if (SupportsOverlappedIO()) { ASSERT(completion_port_ != INVALID_HANDLE_VALUE); BOOL ok = ReadFile(handle_, buffer->GetBufferStart(), buffer->GetBufferSize(), NULL, buffer->GetCleanOverlapped()); if (ok || GetLastError() == ERROR_IO_PENDING) { // Completing asynchronously. pending_read_ = buffer; return true; } OverlappedBuffer::DisposeBuffer(buffer); HandleIssueError(); return false; } else { // Completing asynchronously through thread. pending_read_ = buffer; uint32_t tid; uintptr_t thread_handle = _beginthreadex(NULL, 32 * 1024, ReadFileThread, this, 0, &tid); if (thread_handle == -1) { FATAL("Failed to start read file thread"); } return true; } } bool Handle::IssueWrite() { ScopedLock lock(this); ASSERT(type_ != kListenSocket); ASSERT(completion_port_ != INVALID_HANDLE_VALUE); ASSERT(pending_write_ != NULL); ASSERT(pending_write_->operation() == OverlappedBuffer::kWrite); OverlappedBuffer* buffer = pending_write_; BOOL ok = WriteFile(handle_, buffer->GetBufferStart(), buffer->GetBufferSize(), NULL, buffer->GetCleanOverlapped()); if (ok || GetLastError() == ERROR_IO_PENDING) { // Completing asynchronously. pending_write_ = buffer; return true; } OverlappedBuffer::DisposeBuffer(buffer); HandleIssueError(); return false; } void Handle::HandleIssueError() { DWORD error = GetLastError(); if (error == ERROR_BROKEN_PIPE) { event_handler_->HandleClosed(this); } else { event_handler_->HandleError(this); } SetLastError(error); } void FileHandle::EnsureInitialized(EventHandlerImplementation* event_handler) { ScopedLock lock(this); event_handler_ = event_handler; if (SupportsOverlappedIO() && completion_port_ == INVALID_HANDLE_VALUE) { CreateCompletionPort(event_handler_->completion_port()); } } bool FileHandle::IsClosed() { return IsClosing() && !HasPendingRead() && !HasPendingWrite(); } void FileHandle::DoClose() { if (GetStdHandle(STD_OUTPUT_HANDLE) == handle_) { int fd = _open("NUL", _O_WRONLY); ASSERT(fd >= 0); _dup2(fd, _fileno(stdout)); close(fd); } else { Handle::DoClose(); } } void DirectoryWatchHandle::EnsureInitialized( EventHandlerImplementation* event_handler) { ScopedLock lock(this); event_handler_ = event_handler; if (completion_port_ == INVALID_HANDLE_VALUE) { CreateCompletionPort(event_handler_->completion_port()); } } bool DirectoryWatchHandle::IsClosed() { return IsClosing() && pending_read_ == NULL; } void DirectoryWatchHandle::DoClose() { Handle::DoClose(); } bool DirectoryWatchHandle::IssueRead() { ScopedLock lock(this); OverlappedBuffer* buffer = OverlappedBuffer::AllocateReadBuffer(kBufferSize); ASSERT(completion_port_ != INVALID_HANDLE_VALUE); BOOL ok = ReadDirectoryChangesW(handle_, buffer->GetBufferStart(), buffer->GetBufferSize(), recursive_, events_, NULL, buffer->GetCleanOverlapped(), NULL); if (ok || GetLastError() == ERROR_IO_PENDING) { // Completing asynchronously. pending_read_ = buffer; return true; } OverlappedBuffer::DisposeBuffer(buffer); return false; } void SocketHandle::HandleIssueError() { int error = WSAGetLastError(); if (error == WSAECONNRESET) { event_handler_->HandleClosed(this); } else { event_handler_->HandleError(this); } WSASetLastError(error); } bool ListenSocket::LoadAcceptEx() { // Load the AcceptEx function into memory using WSAIoctl. GUID guid_accept_ex = WSAID_ACCEPTEX; DWORD bytes; int status = WSAIoctl(socket(), SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_accept_ex, sizeof(guid_accept_ex), &AcceptEx_, sizeof(AcceptEx_), &bytes, NULL, NULL); if (status == SOCKET_ERROR) { Log::PrintErr("Error WSAIoctl failed: %d\n", WSAGetLastError()); return false; } return true; } bool ListenSocket::IssueAccept() { ScopedLock lock(this); // 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 const int kAcceptExAddressAdditionalBytes = 16; static const int kAcceptExAddressStorageSize = sizeof(SOCKADDR_STORAGE) + kAcceptExAddressAdditionalBytes; OverlappedBuffer* buffer = OverlappedBuffer::AllocateAcceptBuffer(2 * kAcceptExAddressStorageSize); DWORD received; BOOL ok; ok = AcceptEx_(socket(), buffer->client(), buffer->GetBufferStart(), 0, // For now don't receive data with accept. kAcceptExAddressStorageSize, kAcceptExAddressStorageSize, &received, buffer->GetCleanOverlapped()); if (!ok) { if (WSAGetLastError() != WSA_IO_PENDING) { Log::PrintErr("AcceptEx failed: %d\n", WSAGetLastError()); closesocket(buffer->client()); OverlappedBuffer::DisposeBuffer(buffer); return false; } } pending_accept_count_++; return true; } void ListenSocket::AcceptComplete(OverlappedBuffer* buffer, HANDLE completion_port) { ScopedLock lock(this); 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(&s), sizeof(s)); if (rc == NO_ERROR) { // Insert the accepted socket into the list. ClientSocket* client_socket = new ClientSocket(buffer->client(), 0); client_socket->CreateCompletionPort(completion_port); if (accepted_head_ == NULL) { accepted_head_ = client_socket; accepted_tail_ = client_socket; } else { ASSERT(accepted_tail_ != NULL); accepted_tail_->set_next(client_socket); accepted_tail_ = client_socket; } } else { Log::PrintErr("setsockopt failed: %d\n", WSAGetLastError()); closesocket(buffer->client()); } } else { // Close the socket, as it's already accepted. closesocket(buffer->client()); } pending_accept_count_--; OverlappedBuffer::DisposeBuffer(buffer); } void ListenSocket::DoClose() { closesocket(socket()); handle_ = INVALID_HANDLE_VALUE; while (CanAccept()) { // Get rid of connections already accepted. ClientSocket *client = Accept(); if (client != NULL) { client->Close(); } else { break; } } } bool ListenSocket::CanAccept() { ScopedLock lock(this); return accepted_head_ != NULL; } ClientSocket* ListenSocket::Accept() { ScopedLock lock(this); if (accepted_head_ == NULL) return NULL; ClientSocket* result = accepted_head_; accepted_head_ = accepted_head_->next(); if (accepted_head_ == NULL) accepted_tail_ = NULL; result->set_next(NULL); return result; } void ListenSocket::EnsureInitialized( EventHandlerImplementation* event_handler) { ScopedLock lock(this); if (AcceptEx_ == NULL) { ASSERT(completion_port_ == INVALID_HANDLE_VALUE); ASSERT(event_handler_ == NULL); event_handler_ = event_handler; CreateCompletionPort(event_handler_->completion_port()); LoadAcceptEx(); } } bool ListenSocket::IsClosed() { return IsClosing() && !HasPendingAccept(); } int Handle::Available() { ScopedLock lock(this); if (data_ready_ == NULL) return 0; ASSERT(!data_ready_->IsEmpty()); return data_ready_->GetRemainingLength(); } int Handle::Read(void* buffer, int num_bytes) { ScopedLock lock(this); if (data_ready_ == NULL) return 0; num_bytes = data_ready_->Read(buffer, num_bytes); if (data_ready_->IsEmpty()) { OverlappedBuffer::DisposeBuffer(data_ready_); data_ready_ = NULL; } return num_bytes; } int Handle::Write(const void* buffer, int num_bytes) { ScopedLock lock(this); if (SupportsOverlappedIO()) { if (pending_write_ != NULL) return 0; if (completion_port_ == INVALID_HANDLE_VALUE) return 0; if (num_bytes > kBufferSize) num_bytes = kBufferSize; pending_write_ = OverlappedBuffer::AllocateWriteBuffer(num_bytes); pending_write_->Write(buffer, num_bytes); if (!IssueWrite()) return -1; return num_bytes; } else { DWORD bytes_written = -1; BOOL ok = WriteFile(handle_, buffer, num_bytes, &bytes_written, NULL); if (!ok) { if (GetLastError() != ERROR_BROKEN_PIPE) { Log::PrintErr("WriteFile failed: %d\n", GetLastError()); } event_handler_->HandleClosed(this); } return bytes_written; } } bool ClientSocket::LoadDisconnectEx() { // Load the DisconnectEx function into memory using WSAIoctl. GUID guid_disconnect_ex = WSAID_DISCONNECTEX; DWORD bytes; int status = WSAIoctl(socket(), SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_disconnect_ex, sizeof(guid_disconnect_ex), &DisconnectEx_, sizeof(DisconnectEx_), &bytes, NULL, NULL); if (status == SOCKET_ERROR) { Log::PrintErr("Error WSAIoctl failed: %d\n", WSAGetLastError()); return false; } return true; } void ClientSocket::Shutdown(int how) { int rc = shutdown(socket(), how); if (how == SD_RECEIVE) MarkClosedRead(); if (how == SD_SEND) MarkClosedWrite(); if (how == SD_BOTH) { MarkClosedRead(); MarkClosedWrite(); } } void ClientSocket::DoClose() { // Always do a shutdown before initiating a disconnect. shutdown(socket(), SD_BOTH); IssueDisconnect(); } bool ClientSocket::IssueRead() { ScopedLock lock(this); ASSERT(completion_port_ != INVALID_HANDLE_VALUE); ASSERT(pending_read_ == NULL); OverlappedBuffer* buffer = OverlappedBuffer::AllocateReadBuffer(1024); DWORD flags; flags = 0; int rc = WSARecv(socket(), buffer->GetWASBUF(), 1, NULL, &flags, buffer->GetCleanOverlapped(), NULL); if (rc == NO_ERROR || WSAGetLastError() == WSA_IO_PENDING) { pending_read_ = buffer; return true; } OverlappedBuffer::DisposeBuffer(buffer); pending_read_ = NULL; HandleIssueError(); return false; } bool ClientSocket::IssueWrite() { ScopedLock lock(this); ASSERT(completion_port_ != INVALID_HANDLE_VALUE); ASSERT(pending_write_ != NULL); ASSERT(pending_write_->operation() == OverlappedBuffer::kWrite); int rc = WSASend(socket(), pending_write_->GetWASBUF(), 1, NULL, 0, pending_write_->GetCleanOverlapped(), NULL); if (rc == NO_ERROR || WSAGetLastError() == WSA_IO_PENDING) { return true; } OverlappedBuffer::DisposeBuffer(pending_write_); pending_write_ = NULL; HandleIssueError(); return false; } void ClientSocket::IssueDisconnect() { Dart_Port p = port(); OverlappedBuffer* buffer = OverlappedBuffer::AllocateDisconnectBuffer(); BOOL ok = DisconnectEx_( socket(), buffer->GetCleanOverlapped(), TF_REUSE_SOCKET, 0); if (!ok && WSAGetLastError() != WSA_IO_PENDING) { DisconnectComplete(buffer); } if (p != ILLEGAL_PORT) DartUtils::PostInt32(p, 1 << kDestroyedEvent); } void ClientSocket::DisconnectComplete(OverlappedBuffer* buffer) { OverlappedBuffer::DisposeBuffer(buffer); closesocket(socket()); if (data_ready_ != NULL) { OverlappedBuffer::DisposeBuffer(data_ready_); } // When disconnect is complete get rid of the object. delete this; } void ClientSocket::EnsureInitialized( EventHandlerImplementation* event_handler) { ScopedLock lock(this); if (completion_port_ == INVALID_HANDLE_VALUE) { ASSERT(event_handler_ == NULL); event_handler_ = event_handler; CreateCompletionPort(event_handler_->completion_port()); } } bool ClientSocket::IsClosed() { return false; } static void DeleteIfClosed(Handle* handle) { if (handle->IsClosed()) { Dart_Port port = handle->port(); delete handle; DartUtils::PostInt32(port, 1 << kDestroyedEvent); } } void EventHandlerImplementation::HandleInterrupt(InterruptMessage* msg) { if (msg->id == kTimeoutId) { // 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 { Handle* handle = reinterpret_cast(msg->id); ASSERT(handle != NULL); if (handle->is_listen_socket()) { ListenSocket* listen_socket = reinterpret_cast(handle); listen_socket->EnsureInitialized(this); listen_socket->SetPortAndMask(msg->dart_port, msg->data); Handle::ScopedLock lock(listen_socket); // If incomming connections are requested make sure to post already // accepted connections. if ((msg->data & (1 << kInEvent)) != 0) { if (listen_socket->CanAccept()) { int event_mask = (1 << kInEvent); handle->set_mask(handle->mask() & ~event_mask); DartUtils::PostInt32(handle->port(), event_mask); } // Always keep 5 outstanding accepts going, to enhance performance. while (listen_socket->pending_accept_count() < 5) { listen_socket->IssueAccept(); } } if ((msg->data & (1 << kCloseCommand)) != 0) { listen_socket->Close(); } } else { handle->EnsureInitialized(this); Handle::ScopedLock lock(handle); if (!handle->IsError()) { if ((msg->data & ((1 << kInEvent) | (1 << kOutEvent))) != 0) { // Only set mask if we turned on kInEvent or kOutEvent. handle->SetPortAndMask(msg->dart_port, msg->data); // If in events (data available events) have been requested, and data // is available, post an in event immediately. Otherwise make sure // that a pending read is issued, unless the socket is already closed // for read. if ((msg->data & (1 << kInEvent)) != 0) { if (handle->Available() > 0) { int event_mask = (1 << kInEvent); handle->set_mask(handle->mask() & ~event_mask); DartUtils::PostInt32(handle->port(), event_mask); } else if (handle->IsClosedRead()) { int event_mask = (1 << kCloseEvent); DartUtils::PostInt32(handle->port(), event_mask); } else if (!handle->HasPendingRead()) { handle->IssueRead(); } } // If out events (can write events) have been requested, and there // are no pending writes, post an out event immediately. if ((msg->data & (1 << kOutEvent)) != 0) { if (!handle->HasPendingWrite()) { int event_mask = (1 << kOutEvent); handle->set_mask(handle->mask() & ~event_mask); DartUtils::PostInt32(handle->port(), event_mask); } } } if (handle->is_client_socket()) { ClientSocket* client_socket = reinterpret_cast(handle); if ((msg->data & (1 << kShutdownReadCommand)) != 0) { client_socket->Shutdown(SD_RECEIVE); } if ((msg->data & (1 << kShutdownWriteCommand)) != 0) { client_socket->Shutdown(SD_SEND); } } } if ((msg->data & (1 << kCloseCommand)) != 0) { handle->SetPortAndMask(msg->dart_port, msg->data); handle->Close(); } } DeleteIfClosed(handle); } } void EventHandlerImplementation::HandleAccept(ListenSocket* listen_socket, OverlappedBuffer* buffer) { listen_socket->AcceptComplete(buffer, completion_port_); if (!listen_socket->IsClosing()) { int event_mask = 1 << kInEvent; if ((listen_socket->mask() & event_mask) != 0) { DartUtils::PostInt32(listen_socket->port(), event_mask); } } DeleteIfClosed(listen_socket); } void EventHandlerImplementation::HandleClosed(Handle* handle) { if (!handle->IsClosing()) { int event_mask = 1 << kCloseEvent; DartUtils::PostInt32(handle->port(), event_mask); } } void EventHandlerImplementation::HandleError(Handle* handle) { handle->set_last_error(WSAGetLastError()); handle->MarkError(); if (!handle->IsClosing()) { int event_mask = 1 << kErrorEvent; DartUtils::PostInt32(handle->port(), event_mask); } } void EventHandlerImplementation::HandleRead(Handle* handle, int bytes, OverlappedBuffer* buffer) { buffer->set_data_length(bytes); handle->ReadComplete(buffer); if (bytes > 0) { if (!handle->IsClosing()) { int event_mask = 1 << kInEvent; if ((handle->mask() & event_mask) != 0) { DartUtils::PostInt32(handle->port(), event_mask); } } } else { handle->MarkClosedRead(); if (bytes == 0) { HandleClosed(handle); } else { HandleError(handle); } } DeleteIfClosed(handle); } void EventHandlerImplementation::HandleWrite(Handle* handle, int bytes, OverlappedBuffer* buffer) { handle->WriteComplete(buffer); if (bytes > 0) { if (!handle->IsError() && !handle->IsClosing()) { int event_mask = 1 << kOutEvent; if ((handle->mask() & event_mask) != 0) { DartUtils::PostInt32(handle->port(), event_mask); } } } else if (bytes == 0) { HandleClosed(handle); } else { HandleError(handle); } DeleteIfClosed(handle); } void EventHandlerImplementation::HandleDisconnect( ClientSocket* client_socket, int bytes, OverlappedBuffer* buffer) { client_socket->DisconnectComplete(buffer); } void EventHandlerImplementation::HandleTimeout() { if (!timeout_queue_.HasTimeout()) return; DartUtils::PostNull(timeout_queue_.CurrentPort()); timeout_queue_.RemoveCurrent(); } void EventHandlerImplementation::HandleIOCompletion(DWORD bytes, ULONG_PTR key, OVERLAPPED* overlapped) { OverlappedBuffer* buffer = OverlappedBuffer::GetFromOverlapped(overlapped); switch (buffer->operation()) { case OverlappedBuffer::kAccept: { ListenSocket* listen_socket = reinterpret_cast(key); HandleAccept(listen_socket, buffer); break; } case OverlappedBuffer::kRead: { Handle* handle = reinterpret_cast(key); HandleRead(handle, bytes, buffer); break; } case OverlappedBuffer::kWrite: { Handle* handle = reinterpret_cast(key); HandleWrite(handle, bytes, buffer); break; } case OverlappedBuffer::kDisconnect: { ClientSocket* client_socket = reinterpret_cast(key); HandleDisconnect(client_socket, bytes, buffer); break; } default: UNREACHABLE(); } } EventHandlerImplementation::EventHandlerImplementation() { intptr_t result; completion_port_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1); if (completion_port_ == NULL) { FATAL("Completion port creation failed"); } shutdown_ = false; } EventHandlerImplementation::~EventHandlerImplementation() { CloseHandle(completion_port_); } int64_t EventHandlerImplementation::GetTimeout() { if (!timeout_queue_.HasTimeout()) { return kInfinityTimeout; } int64_t millis = timeout_queue_.CurrentTimeout() - TimerUtils::GetCurrentTimeMilliseconds(); 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(msg)); if (!ok) { FATAL("PostQueuedCompletionStatus failed"); } } void EventHandlerImplementation::EventHandlerEntry(uword args) { EventHandler* handler = reinterpret_cast(args); EventHandlerImplementation* handler_impl = &handler->delegate_; ASSERT(handler_impl != NULL); while (!handler_impl->shutdown_) { DWORD bytes; ULONG_PTR key; OVERLAPPED* overlapped; int64_t millis = handler_impl->GetTimeout(); ASSERT(millis == kInfinityTimeout || millis >= 0); if (millis > kMaxInt32) millis = kMaxInt32; ASSERT(sizeof(int32_t) == sizeof(DWORD)); BOOL ok = GetQueuedCompletionStatus(handler_impl->completion_port(), &bytes, &key, &overlapped, static_cast(millis)); if (!ok && overlapped == NULL) { if (GetLastError() == ERROR_ABANDONED_WAIT_0) { // The completion port should never be closed. Log::Print("Completion port closed\n"); UNREACHABLE(); } else { // Timeout is signalled by false result and NULL in overlapped. handler_impl->HandleTimeout(); } } else 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); handler_impl->HandleIOCompletion(bytes, key, overlapped); } else { ASSERT(bytes == 0); handler_impl->HandleIOCompletion(-1, key, overlapped); } } else if (key == NULL) { // A key of NULL signals an interrupt message. InterruptMessage* msg = reinterpret_cast(overlapped); handler_impl->HandleInterrupt(msg); delete msg; } else { handler_impl->HandleIOCompletion(bytes, key, overlapped); } } delete handler; } void EventHandlerImplementation::Start(EventHandler* handler) { int result = dart::Thread::Start(EventHandlerEntry, reinterpret_cast(handler)); if (result != 0) { FATAL1("Failed to start event handler thread %d", result); } // Initialize Winsock32 if (!Socket::Initialize()) { FATAL("Failed to initialized Windows sockets"); } } void EventHandlerImplementation::Shutdown() { SendData(kShutdownId, 0, 0); } } // namespace bin } // namespace dart #endif // defined(TARGET_OS_WINDOWS)