8779bc8d7f
If the propagated type allows, eliminate the check for numbers at identical operations in optimized code. The previous optimizaton was done before full type propagation was run and was therefore not very effective. * Made flow-graph printing having no side-effects: calling Type() lazily initializes the type_ and reaching_type_ fields of Value and Definition. Access fields directly when printing instead. Also, fixed two random spelling mistakes. R=srdjan@google.com Review URL: https://codereview.chromium.org//27727002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@28851 260f80e4-7a28-3924-810f-c04153c831b5
1142 lines
32 KiB
C++
1142 lines
32 KiB
C++
// 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 <winsock2.h> // NOLINT
|
|
#include <ws2tcpip.h> // NOLINT
|
|
#include <mswsock.h> // NOLINT
|
|
#include <io.h> // NOLINT
|
|
#include <fcntl.h> // NOLINT
|
|
|
|
#include "bin/builtin.h"
|
|
#include "bin/dartutils.h"
|
|
#include "bin/log.h"
|
|
#include "bin/socket.h"
|
|
#include "bin/utils.h"
|
|
#include "vm/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();
|
|
}
|
|
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);
|
|
return data_length_ - index_;
|
|
}
|
|
|
|
|
|
Handle::Handle(HANDLE handle)
|
|
: handle_(reinterpret_cast<HANDLE>(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>(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<ULONG_PTR>(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 void ReadFileThread(uword args) {
|
|
Handle* handle = reinterpret_cast<Handle*>(args);
|
|
handle->ReadSyncCompleteAsync();
|
|
}
|
|
|
|
|
|
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<ULONG_PTR>(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;
|
|
int result = dart::Thread::Start(ReadFileThread,
|
|
reinterpret_cast<uword>(this));
|
|
if (result != 0) {
|
|
FATAL1("Failed to start read file thread %d", result);
|
|
}
|
|
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 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;
|
|
}
|
|
|
|
|
|
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<char*>(&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 (pending_write_ != NULL) return 0;
|
|
if (num_bytes > kBufferSize) num_bytes = kBufferSize;
|
|
ASSERT(SupportsOverlappedIO());
|
|
if (completion_port_ == INVALID_HANDLE_VALUE) return 0;
|
|
pending_write_ = OverlappedBuffer::AllocateWriteBuffer(num_bytes);
|
|
pending_write_->Write(buffer, num_bytes);
|
|
if (!IssueWrite()) return -1;
|
|
return num_bytes;
|
|
}
|
|
|
|
|
|
static void WriteFileThread(uword args) {
|
|
StdHandle* handle = reinterpret_cast<StdHandle*>(args);
|
|
handle->RunWriteLoop();
|
|
}
|
|
|
|
|
|
void StdHandle::RunWriteLoop() {
|
|
write_monitor_->Enter();
|
|
write_thread_running_ = true;
|
|
// Notify we have started.
|
|
write_monitor_->Notify();
|
|
|
|
while (write_thread_running_) {
|
|
write_monitor_->Wait(Monitor::kNoTimeout);
|
|
if (pending_write_ != NULL) {
|
|
// We woke up and had a pending write. Execute it.
|
|
WriteSyncCompleteAsync();
|
|
}
|
|
}
|
|
|
|
write_thread_exists_ = false;
|
|
write_monitor_->Notify();
|
|
write_monitor_->Exit();
|
|
}
|
|
|
|
|
|
void StdHandle::WriteSyncCompleteAsync() {
|
|
ASSERT(pending_write_ != NULL);
|
|
|
|
DWORD bytes_written = -1;
|
|
BOOL ok = WriteFile(handle_,
|
|
pending_write_->GetBufferStart(),
|
|
pending_write_->GetBufferSize(),
|
|
&bytes_written,
|
|
NULL);
|
|
if (!ok) {
|
|
if (GetLastError() != ERROR_BROKEN_PIPE) {
|
|
Log::PrintErr("WriteFile failed %d\n", GetLastError());
|
|
}
|
|
bytes_written = 0;
|
|
}
|
|
thread_wrote_ += bytes_written;
|
|
OVERLAPPED* overlapped = pending_write_->GetCleanOverlapped();
|
|
ok = PostQueuedCompletionStatus(event_handler_->completion_port(),
|
|
bytes_written,
|
|
reinterpret_cast<ULONG_PTR>(this),
|
|
overlapped);
|
|
if (!ok) {
|
|
FATAL("PostQueuedCompletionStatus failed");
|
|
}
|
|
}
|
|
|
|
int StdHandle::Write(const void* buffer, int num_bytes) {
|
|
ScopedLock lock(this);
|
|
if (pending_write_ != NULL) 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.
|
|
MonitorLocker locker(write_monitor_);
|
|
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;
|
|
int result = dart::Thread::Start(WriteFileThread,
|
|
reinterpret_cast<uword>(this));
|
|
if (result != 0) {
|
|
FATAL1("Failed to start write file thread %d", result);
|
|
}
|
|
while (!write_thread_running_) {
|
|
// Wait until we the thread is running.
|
|
locker.Wait(Monitor::kNoTimeout);
|
|
}
|
|
}
|
|
// Create buffer and notify thread about the new handle.
|
|
pending_write_ = OverlappedBuffer::AllocateWriteBuffer(num_bytes);
|
|
pending_write_->Write(buffer, num_bytes);
|
|
locker.Notify();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void StdHandle::DoClose() {
|
|
MonitorLocker locker(write_monitor_);
|
|
if (write_thread_exists_) {
|
|
write_thread_running_ = false;
|
|
locker.Notify();
|
|
while (write_thread_exists_) {
|
|
locker.Wait(Monitor::kNoTimeout);
|
|
}
|
|
}
|
|
if (handle_ == GetStdHandle(STD_OUTPUT_HANDLE)) {
|
|
int fd = _open("NUL", _O_WRONLY);
|
|
ASSERT(fd >= 0);
|
|
_dup2(fd, _fileno(stdout));
|
|
close(fd);
|
|
} else {
|
|
Handle::DoClose();
|
|
}
|
|
}
|
|
|
|
|
|
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<Handle*>(msg->id);
|
|
ASSERT(handle != NULL);
|
|
if (handle->is_listen_socket()) {
|
|
ListenSocket* listen_socket =
|
|
reinterpret_cast<ListenSocket*>(handle);
|
|
listen_socket->EnsureInitialized(this);
|
|
listen_socket->SetPortAndMask(msg->dart_port, msg->data);
|
|
|
|
Handle::ScopedLock lock(listen_socket);
|
|
|
|
// If incoming 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<ClientSocket*>(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<ListenSocket*>(key);
|
|
HandleAccept(listen_socket, buffer);
|
|
break;
|
|
}
|
|
case OverlappedBuffer::kRead: {
|
|
Handle* handle = reinterpret_cast<Handle*>(key);
|
|
HandleRead(handle, bytes, buffer);
|
|
break;
|
|
}
|
|
case OverlappedBuffer::kWrite: {
|
|
Handle* handle = reinterpret_cast<Handle*>(key);
|
|
HandleWrite(handle, bytes, buffer);
|
|
break;
|
|
}
|
|
case OverlappedBuffer::kDisconnect: {
|
|
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(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<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 != 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<DWORD>(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<InterruptMessage*>(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<uword>(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)
|