blob: 43562cd1d0f95aac8e82666c20f8701f72156f40 [file] [log] [blame]
// Copyright (c) 2012, 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.
#ifndef BIN_EVENTHANDLER_WIN_H_
#define BIN_EVENTHANDLER_WIN_H_
#if !defined(BIN_EVENTHANDLER_H_)
#error Do not include eventhandler_win.h directly; use eventhandler.h instead.
#endif
#include <winsock2.h>
#include <mswsock.h>
#include "bin/builtin.h"
#include "platform/thread.h"
namespace dart {
namespace bin {
// Forward declarations.
class EventHandlerImplementation;
class Handle;
class FileHandle;
class SocketHandle;
class ClientSocket;
class ListenSocket;
struct InterruptMessage {
intptr_t id;
Dart_Port dart_port;
int64_t data;
};
// An OverlappedBuffer encapsulates the OVERLAPPED structure and the
// associated data buffer. For accept it also contains the pre-created
// socket for the client.
class OverlappedBuffer {
public:
enum Operation { kAccept, kRead, kWrite, kDisconnect };
static OverlappedBuffer* AllocateAcceptBuffer(int buffer_size);
static OverlappedBuffer* AllocateReadBuffer(int buffer_size);
static OverlappedBuffer* AllocateWriteBuffer(int buffer_size);
static OverlappedBuffer* AllocateDisconnectBuffer();
static void DisposeBuffer(OverlappedBuffer* buffer);
// Find the IO buffer from the OVERLAPPED address.
static OverlappedBuffer* GetFromOverlapped(OVERLAPPED* overlapped);
// Read data from a buffer which has been received. It will read up
// to num_bytes bytes of data returning the actual number of bytes
// read. This will update the index of the next byte in the buffer
// so calling Read several times will keep returning new data from
// the buffer until all data have been read.
int Read(void* buffer, int num_bytes);
// Write data to a buffer before sending it. Returns the number of bytes
// actually written to the buffer. Calls to Write will always write to
// the buffer from the begining.
int Write(const void* buffer, int num_bytes);
// Check the amount of data in a read buffer which has not been read yet.
int GetRemainingLength();
bool IsEmpty() { return GetRemainingLength() == 0; }
Operation operation() { return operation_; }
SOCKET client() { return client_; }
char* GetBufferStart() { return reinterpret_cast<char*>(&buffer_data_); }
int GetBufferSize() { return buflen_; }
// Returns the address of the OVERLAPPED structure with all fields
// initialized to zero.
OVERLAPPED* GetCleanOverlapped() {
memset(&overlapped_, 0, sizeof(overlapped_));
return &overlapped_;
}
// Returns a WASBUF structure initialized with the data in this IO buffer.
WSABUF* GetWASBUF() {
wbuf_.buf = GetBufferStart();
wbuf_.len = GetBufferSize();
return &wbuf_;
};
void set_data_length(int data_length) { data_length_ = data_length; }
private:
OverlappedBuffer(int buffer_size, Operation operation)
: operation_(operation), buflen_(buffer_size) {
memset(GetBufferStart(), 0, GetBufferSize());
index_ = 0;
data_length_ = 0;
if (operation_ == kAccept) {
client_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
}
void* operator new(size_t size, int buffer_size) {
return malloc(size + buffer_size);
}
void operator delete(void* buffer) {
free(buffer);
}
static OverlappedBuffer* AllocateBuffer(int buffer_size,
Operation operation);
OVERLAPPED overlapped_; // OVERLAPPED structure for overlapped IO.
SOCKET client_; // Used for AcceptEx client socket.
int buflen_; // Length of the buffer.
Operation operation_; // Type of operation issued.
int index_; // Index for next read from read buffer.
int data_length_; // Length of the actual data in the buffer.
WSABUF wbuf_; // Structure for passing buffer to WSA functions.
// Buffer for recv/send/AcceptEx. This must be at the end of the
// object as the object is allocated larger than it's definition
// indicate to extend this array.
uint8_t buffer_data_[1];
};
// Abstract super class for holding information on listen and connected
// sockets.
class Handle {
public:
enum Type {
kFile,
kStd,
kDirectoryWatch,
kClientSocket,
kListenSocket
};
class ScopedLock {
public:
explicit ScopedLock(Handle* handle)
: handle_(handle) {
handle_->Lock();
}
~ScopedLock() {
handle_->Unlock();
}
private:
Handle* handle_;
};
virtual ~Handle();
// Socket interface exposing normal socket operations.
int Available();
int Read(void* buffer, int num_bytes);
virtual int Write(const void* buffer, int num_bytes);
// Internal interface used by the event handler.
virtual bool IssueRead();
virtual bool IssueWrite();
bool HasPendingRead();
bool HasPendingWrite();
void ReadComplete(OverlappedBuffer* buffer);
void WriteComplete(OverlappedBuffer* buffer);
bool IsClosing() { return (flags_ & (1 << kClosing)) != 0; }
bool IsClosedRead() { return (flags_ & (1 << kCloseRead)) != 0; }
bool IsClosedWrite() { return (flags_ & (1 << kCloseWrite)) != 0; }
bool IsError() { return (flags_ & (1 << kError)) != 0; }
void MarkClosing() { flags_ |= (1 << kClosing); }
void MarkClosedRead() { flags_ |= (1 << kCloseRead); }
void MarkClosedWrite() { flags_ |= (1 << kCloseWrite); }
void MarkError() { flags_ |= (1 << kError); }
virtual void EnsureInitialized(
EventHandlerImplementation* event_handler) = 0;
HANDLE handle() { return handle_; }
Dart_Port port() { return port_; }
EventHandlerImplementation* event_handler() { return event_handler_; }
void Lock();
void Unlock();
bool CreateCompletionPort(HANDLE completion_port);
void Close();
virtual void DoClose();
virtual bool IsClosed() = 0;
void SetPortAndMask(Dart_Port port, intptr_t mask) {
port_ = port;
mask_ = mask;
}
Type type() { return type_; }
bool is_file() { return type_ == kFile; }
bool is_socket() { return type_ == kListenSocket || type_ == kClientSocket; }
bool is_listen_socket() { return type_ == kListenSocket; }
bool is_client_socket() { return type_ == kClientSocket; }
void set_mask(intptr_t mask) { mask_ = mask; }
intptr_t mask() { return mask_; }
void MarkDoesNotSupportOverlappedIO() {
flags_ |= (1 << kDoesNotSupportOverlappedIO);
}
bool SupportsOverlappedIO() {
return (flags_ & (1 << kDoesNotSupportOverlappedIO)) == 0;
}
void ReadSyncCompleteAsync();
DWORD last_error() { return last_error_; }
void set_last_error(DWORD last_error) { last_error_ = last_error; }
protected:
enum Flags {
kClosing = 0,
kCloseRead = 1,
kCloseWrite = 2,
kDoesNotSupportOverlappedIO = 3,
kError = 4
};
explicit Handle(HANDLE handle);
Handle(HANDLE handle, Dart_Port port);
virtual void HandleIssueError();
Type type_;
HANDLE handle_;
Dart_Port port_; // Dart port to communicate events for this socket.
intptr_t mask_; // Mask of events to report through the port.
HANDLE completion_port_;
EventHandlerImplementation* event_handler_;
OverlappedBuffer* data_ready_; // Buffer for data ready to be read.
OverlappedBuffer* pending_read_; // Buffer for pending read.
OverlappedBuffer* pending_write_; // Buffer for pending write
DWORD last_error_;
private:
int flags_;
CRITICAL_SECTION cs_; // Critical section protecting this object.
};
class FileHandle : public Handle {
public:
explicit FileHandle(HANDLE handle)
: Handle(handle) { type_ = kFile; }
FileHandle(HANDLE handle, Dart_Port port)
: Handle(handle, port) { type_ = kFile; }
virtual void EnsureInitialized(EventHandlerImplementation* event_handler);
virtual bool IsClosed();
};
class StdHandle : public FileHandle {
public:
explicit StdHandle(HANDLE handle)
: FileHandle(handle),
thread_wrote_(0),
write_thread_exists_(false),
write_thread_running_(false),
write_monitor_(new Monitor()) {
type_ = kStd;
}
~StdHandle() {
delete write_monitor_;
}
virtual void DoClose();
virtual int Write(const void* buffer, int num_bytes);
void WriteSyncCompleteAsync();
void RunWriteLoop();
private:
DWORD thread_wrote_;
bool write_thread_exists_;
bool write_thread_running_;
dart::Monitor* write_monitor_;
};
class DirectoryWatchHandle : public Handle {
public:
DirectoryWatchHandle(HANDLE handle, int events, bool recursive)
: Handle(handle),
events_(events),
recursive_(recursive) {
type_ = kDirectoryWatch;
}
virtual void EnsureInitialized(EventHandlerImplementation* event_handler);
virtual bool IsClosed();
virtual bool IssueRead();
private:
int events_;
bool recursive_;
};
class SocketHandle : public Handle {
public:
SOCKET socket() { return reinterpret_cast<SOCKET>(handle_); }
protected:
explicit SocketHandle(SOCKET s) : Handle(reinterpret_cast<HANDLE>(s)) {}
SocketHandle(SOCKET s, Dart_Port port)
: Handle(reinterpret_cast<HANDLE>(s), port) {}
virtual void HandleIssueError();
};
// Information on listen sockets.
class ListenSocket : public SocketHandle {
public:
explicit ListenSocket(SOCKET s) : SocketHandle(s),
AcceptEx_(NULL),
pending_accept_count_(0),
accepted_head_(NULL),
accepted_tail_(NULL) {
type_ = kListenSocket;
}
virtual ~ListenSocket() {
ASSERT(!HasPendingAccept());
ASSERT(accepted_head_ == NULL);
ASSERT(accepted_tail_ == NULL);
};
// Socket interface exposing normal socket operations.
ClientSocket* Accept();
bool CanAccept();
// Internal interface used by the event handler.
bool HasPendingAccept() { return pending_accept_count_ > 0; }
bool IssueAccept();
void AcceptComplete(OverlappedBuffer* buffer, HANDLE completion_port);
virtual void EnsureInitialized(
EventHandlerImplementation* event_handler);
virtual void DoClose();
virtual bool IsClosed();
int pending_accept_count() { return pending_accept_count_; }
private:
bool LoadAcceptEx();
LPFN_ACCEPTEX AcceptEx_;
int pending_accept_count_;
// Linked list of accepted connections provided by completion code. Ready to
// be handed over through accept.
ClientSocket* accepted_head_;
ClientSocket* accepted_tail_;
};
// Information on connected sockets.
class ClientSocket : public SocketHandle {
public:
explicit ClientSocket(SOCKET s) : SocketHandle(s),
DisconnectEx_(NULL),
next_(NULL) {
LoadDisconnectEx();
type_ = kClientSocket;
}
ClientSocket(SOCKET s, Dart_Port port) : SocketHandle(s, port),
DisconnectEx_(NULL),
next_(NULL) {
LoadDisconnectEx();
type_ = kClientSocket;
}
virtual ~ClientSocket() {
// Don't delete this object until all pending requests have been handled.
ASSERT(!HasPendingRead());
ASSERT(!HasPendingWrite());
ASSERT(next_ == NULL);
};
void Shutdown(int how);
// Internal interface used by the event handler.
virtual bool IssueRead();
virtual bool IssueWrite();
void IssueDisconnect();
void DisconnectComplete(OverlappedBuffer* buffer);
virtual void EnsureInitialized(
EventHandlerImplementation* event_handler);
virtual void DoClose();
virtual bool IsClosed();
ClientSocket* next() { return next_; }
void set_next(ClientSocket* next) { next_ = next; }
private:
bool LoadDisconnectEx();
LPFN_DISCONNECTEX DisconnectEx_;
ClientSocket* next_;
};
// Event handler.
class EventHandlerImplementation {
public:
EventHandlerImplementation();
virtual ~EventHandlerImplementation();
void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
void Start(EventHandler* handler);
void Shutdown();
static void EventHandlerEntry(uword args);
int64_t GetTimeout();
void HandleInterrupt(InterruptMessage* msg);
void HandleTimeout();
void HandleAccept(ListenSocket* listen_socket, OverlappedBuffer* buffer);
void HandleClosed(Handle* handle);
void HandleError(Handle* handle);
void HandleRead(Handle* handle, int bytes, OverlappedBuffer* buffer);
void HandleWrite(Handle* handle, int bytes, OverlappedBuffer* buffer);
void HandleDisconnect(ClientSocket* client_socket,
int bytes,
OverlappedBuffer* buffer);
void HandleIOCompletion(DWORD bytes, ULONG_PTR key, OVERLAPPED* overlapped);
HANDLE completion_port() { return completion_port_; }
private:
ClientSocket* client_sockets_head_;
TimeoutQueue timeout_queue_; // Time for next timeout.
bool shutdown_;
HANDLE completion_port_;
};
} // namespace bin
} // namespace dart
#endif // BIN_EVENTHANDLER_WIN_H_