// 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 RUNTIME_BIN_EVENTHANDLER_WIN_H_ #define RUNTIME_BIN_EVENTHANDLER_WIN_H_ #if !defined(RUNTIME_BIN_EVENTHANDLER_H_) #error Do not include eventhandler_win.h directly; use eventhandler.h instead. #endif #include #include #include #include #include #include "bin/builtin.h" #include "bin/lockers.h" #include "bin/reference_counting.h" #include "bin/socket_base.h" #include "bin/thread.h" namespace dart { namespace bin { // Forward declarations. class ClientSocket; class EventHandlerImplementation; class FileHandle; class Handle; class ListenSocket; class SocketHandle; // 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, kRecvFrom, kWrite, kSendTo, kDisconnect, kConnect }; static std::unique_ptr AllocateAcceptBuffer( Handle* handle, ADDRESS_FAMILY sa_family); static std::unique_ptr AllocateReadBuffer(Handle* handle, int buffer_size); static std::unique_ptr AllocateRecvFromBuffer( Handle* handle, int buffer_size); static std::unique_ptr AllocateWriteBuffer(Handle* handle, int buffer_size); static std::unique_ptr AllocateSendToBuffer( Handle* handle, int buffer_size); static std::unique_ptr AllocateDisconnectBuffer( Handle* handle); static std::unique_ptr AllocateConnectBuffer( Handle* handle); // 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 beginning. 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() const { return operation_; } SOCKET client() const { return client_; } char* GetBufferStart() { return reinterpret_cast(&buffer_data_); } int GetBufferSize() const { return buflen_; } RawAddr* from() const { return from_; } // 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; } void operator delete(void* buffer) { free(buffer); } Handle* StealHandle() { auto handle = handle_; handle_ = nullptr; return handle; } void DetachClient() { client_ = INVALID_SOCKET; } ~OverlappedBuffer(); private: OverlappedBuffer(Handle* handle, int buffer_size, Operation operation); void* operator new(size_t size, int buffer_size) { return malloc(size + buffer_size); } // Allocate an overlapped buffer for thse specified amount of data and // operation. Some operations need additional buffer space, which is // handled by this method. static std::unique_ptr AllocateBuffer(Handle* handle, int buffer_size, Operation operation); OVERLAPPED overlapped_; // OVERLAPPED structure for overlapped IO. SOCKET client_ = INVALID_SOCKET; // Used for AcceptEx client socket. int buflen_; // Length of the buffer. Operation operation_; // Type of operation issued. Handle* handle_ = nullptr; 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. // For the recvfrom operation additional storace is allocated for the // source sockaddr. RawAddr* from_; // 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]; DISALLOW_COPY_AND_ASSIGN(OverlappedBuffer); }; // Abstract super class for holding information on listen and connected // sockets. class Handle : public ReferenceCounted, public DescriptorInfoBase { public: enum Type { kFile, kStd, kDirectoryWatch, kClientSocket, kListenSocket, kDatagramSocket }; enum class SupportsOverlappedIO { kYes, kNo }; // Socket interface exposing normal socket operations. intptr_t Available(); bool DataReady(); intptr_t Read(void* buffer, intptr_t num_bytes); intptr_t RecvFrom(void* buffer, intptr_t num_bytes, RawAddr* from); virtual intptr_t Write(const void* buffer, intptr_t num_bytes); virtual intptr_t SendTo(const void* buffer, intptr_t num_bytes, const RawAddr& addr); // Internal interface used by the event handler. virtual bool IssueReadLocked(MonitorLocker* ml); virtual bool IssueRecvFromLocked(MonitorLocker* ml); bool HasPendingRead(); bool HasPendingWrite(); void ReadComplete(std::unique_ptr buffer); void WriteComplete(std::unique_ptr 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); } HANDLE handle() { return handle_; } void Close(); void CloseLocked(MonitorLocker* ml); virtual void DoCloseLocked(MonitorLocker* ml); virtual bool IsClosed() = 0; bool IsHandleClosed() const { return handle_ == INVALID_HANDLE_VALUE; } Type type() { return type_; } bool is_file() { return type_ == kFile; } bool is_socket() { return type_ == kListenSocket || type_ == kClientSocket || type_ == kDatagramSocket; } bool is_listen_socket() { return type_ == kListenSocket; } bool is_client_socket() { return type_ == kClientSocket; } bool is_datagram_socket() { return type_ == kDatagramSocket; } bool supports_overlapped_io() { 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: // For access to monitor_; friend class EventHandlerImplementation; enum Flags { kClosing = 0, kCloseRead = 1, kCloseWrite = 2, kDoesNotSupportOverlappedIO = 3, kError = 4 }; Handle(intptr_t handle, Type type_, SupportsOverlappedIO supports_overlapped_io); virtual ~Handle(); virtual void HandleIssueError(); Monitor monitor_; const Type type_; HANDLE handle_; std::unique_ptr data_ready_; // Buffer for data ready to be read. OverlappedBuffer* pending_read_ = nullptr; // Buffer for pending read. OverlappedBuffer* pending_write_ = nullptr; // Buffer for pending write DWORD last_error_ = NOERROR; HANDLE read_thread_ = INVALID_HANDLE_VALUE; bool read_thread_starting_ = false; bool read_thread_finished_ = false; private: void WaitForReadThreadStarted(); void NotifyReadThreadStarted(); void WaitForReadThreadFinished(); void NotifyReadThreadFinished(); virtual bool IssueWriteLocked(MonitorLocker* ml, std::unique_ptr buffer); virtual bool IssueSendToLocked(MonitorLocker* ml, std::unique_ptr buffer, const RawAddr& addr); int flags_ = 0; friend class ReferenceCounted; DISALLOW_COPY_AND_ASSIGN(Handle); }; class FileHandle : public DescriptorInfoSingleMixin { public: explicit FileHandle(HANDLE handle) : DescriptorInfoSingleMixin(reinterpret_cast(handle), kFile, SupportsOverlappedIO::kYes) {} virtual bool IsClosed(); protected: FileHandle(HANDLE handle, Type type, SupportsOverlappedIO supports_overlapped_io) : DescriptorInfoSingleMixin(reinterpret_cast(handle), type, supports_overlapped_io) {} private: DISALLOW_COPY_AND_ASSIGN(FileHandle); }; class StdHandle : public FileHandle { public: static StdHandle* Stdin(HANDLE handle); virtual void DoCloseLocked(MonitorLocker* ml); virtual intptr_t Write(const void* buffer, intptr_t num_bytes); void WriteSyncCompleteAsync(); void RunWriteLoop(); #if defined(DEBUG) static StdHandle* StdinPtr() { return stdin_; } #endif private: static Mutex* stdin_mutex_; static StdHandle* stdin_; explicit StdHandle(HANDLE handle) : FileHandle(handle, kStd, SupportsOverlappedIO::kNo) {} HANDLE thread_handle_ = INVALID_HANDLE_VALUE; intptr_t thread_wrote_ = 0; bool write_thread_exists_ = false; bool write_thread_running_ = false; DISALLOW_COPY_AND_ASSIGN(StdHandle); }; class DirectoryWatchHandle : public DescriptorInfoSingleMixin { public: DirectoryWatchHandle(HANDLE handle, int events, bool recursive) : DescriptorInfoSingleMixin(reinterpret_cast(handle), kDirectoryWatch, SupportsOverlappedIO::kYes), events_(events), recursive_(recursive) {} virtual bool IsClosed(); virtual bool IssueReadLocked(MonitorLocker* ml); void Start(); void Stop(); private: int events_; bool recursive_; DISALLOW_COPY_AND_ASSIGN(DirectoryWatchHandle); }; class SocketHandle : public Handle { public: SOCKET socket() const { return socket_; } protected: explicit SocketHandle(intptr_t s, Type type) : Handle(s, type, SupportsOverlappedIO::kYes), socket_(s) {} virtual void HandleIssueError(); private: const SOCKET socket_; DISALLOW_COPY_AND_ASSIGN(SocketHandle); }; // Information on listen sockets. class ListenSocket : public DescriptorInfoMultipleMixin { public: explicit ListenSocket(intptr_t s, ADDRESS_FAMILY sa_family) : DescriptorInfoMultipleMixin(s, kListenSocket), sa_family_(sa_family), pending_accept_count_(0), accepted_head_(nullptr), accepted_tail_(nullptr), accepted_count_(0) {} virtual ~ListenSocket() { ASSERT(!HasPendingAccept()); ASSERT(accepted_head_ == nullptr); ASSERT(accepted_tail_ == nullptr); } // Socket interface exposing normal socket operations. ClientSocket* Accept(); bool StartAccept(); // Internal interface used by the event handler. bool HasPendingAccept() { return pending_accept_count_ > 0; } void AcceptComplete(std::unique_ptr buffer); virtual void DoCloseLocked(MonitorLocker* ml); virtual bool IsClosed(); int pending_accept_count() { return pending_accept_count_; } int accepted_count() { return accepted_count_; } void DispatchCompletedAcceptsLocked(MonitorLocker* ml); private: static constexpr intptr_t kMinIssuedAccepts = 5; bool IssueAcceptLocked(MonitorLocker* ml); ADDRESS_FAMILY sa_family_; // The number of asynchronous `IssueAccept` operations which haven't completed // yet. 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_; // The number of accepted connections which are waiting to be removed from // this queue and processed by dart isolates. int accepted_count_; DISALLOW_COPY_AND_ASSIGN(ListenSocket); }; // Information on connected sockets. class ClientSocket : public DescriptorInfoSingleMixin { public: explicit ClientSocket(intptr_t s, std::unique_ptr&& remote_addr = nullptr) : DescriptorInfoSingleMixin(s, kClientSocket), next_(nullptr), connected_(false), closed_(false), remote_addr_(std::move(remote_addr)) {} virtual ~ClientSocket() { // Don't delete this object until all pending requests have been handled. ASSERT(!HasPendingRead()); ASSERT(!HasPendingWrite()); ASSERT(next_ == nullptr); ASSERT(closed_ == true); } void Shutdown(int how); // Internal interface used by the event handler. virtual bool IssueReadLocked(MonitorLocker* ml); void IssueDisconnectLocked(MonitorLocker* ml); void DisconnectComplete(); void ConnectComplete(); virtual void DoCloseLocked(MonitorLocker* ml); virtual bool IsClosed(); // If `ClientSocket` was constructed with a `remote_addr`, populate `addr` // with that value and return `true`. Otherwise leave `addr` untouched and // return `false`. bool PopulateRemoteAddr(RawAddr& addr); ClientSocket* next() { return next_; } void set_next(ClientSocket* next) { next_ = next; } void mark_connected() { connected_ = true; } bool is_connected() const { return connected_; } void mark_closed() { closed_ = true; } #if defined(DEBUG) static intptr_t disconnecting() { return disconnecting_; } #endif private: virtual bool IssueWriteLocked(MonitorLocker* ml, std::unique_ptr buffer); ClientSocket* next_; bool connected_; bool closed_; std::unique_ptr remote_addr_; #if defined(DEBUG) static intptr_t disconnecting_; #endif DISALLOW_COPY_AND_ASSIGN(ClientSocket); }; class DatagramSocket : public DescriptorInfoSingleMixin { public: explicit DatagramSocket(intptr_t s) : DescriptorInfoSingleMixin(s, kDatagramSocket) {} virtual ~DatagramSocket() { // Don't delete this object until all pending requests have been handled. ASSERT(!HasPendingRead()); ASSERT(!HasPendingWrite()); } // Internal interface used by the event handler. virtual bool IssueRecvFromLocked(MonitorLocker* ml); virtual void DoCloseLocked(MonitorLocker* ml); virtual bool IsClosed(); private: virtual bool IssueSendToLocked(MonitorLocker* ml, std::unique_ptr buffer, const RawAddr& addr); DISALLOW_COPY_AND_ASSIGN(DatagramSocket); }; // Event handler. class EventHandlerImplementation { public: EventHandlerImplementation(); virtual ~EventHandlerImplementation(); void AssociateWithCompletionPort(Handle* handle); 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 HandleIOCompletion(int32_t bytes, ULONG_PTR key, OVERLAPPED* overlapped); void HandleCompletionOrInterrupt(BOOL ok, DWORD bytes, ULONG_PTR key, OVERLAPPED* overlapped); HANDLE completion_port() { return completion_port_; } LPFN_ACCEPTEX accept_ex() { InitializeSocketExtensions(); return accept_ex_; } LPFN_CONNECTEX connect_ex() { InitializeSocketExtensions(); return connect_ex_; } LPFN_DISCONNECTEX disconnect_ex() { InitializeSocketExtensions(); return disconnect_ex_; } LPFN_GETACCEPTEXSOCKADDRS get_accept_ex_sockaddrs() { InitializeSocketExtensions(); return get_accept_ex_sockaddrs_; } private: void InitializeSocketExtensions(); void HandleAccept(ListenSocket* listen_socket, std::unique_ptr buffer); void HandleRead(Handle* handle, int bytes, std::unique_ptr buffer); void HandleRecvFrom(Handle* handle, int bytes, std::unique_ptr buffer); void HandleWrite(Handle* handle, int bytes, std::unique_ptr buffer); void HandleDisconnect(ClientSocket* client_socket, int bytes, std::unique_ptr buffer); void HandleConnect(ClientSocket* client_socket, int bytes, std::unique_ptr buffer); Monitor monitor_; HANDLE handler_thread_ = INVALID_HANDLE_VALUE; TimeoutQueue timeout_queue_; // Time for next timeout. bool shutdown_ = false; HANDLE completion_port_ = INVALID_HANDLE_VALUE; std::atomic socket_extensions_initialized_{false}; LPFN_ACCEPTEX accept_ex_ = nullptr; LPFN_CONNECTEX connect_ex_ = nullptr; LPFN_DISCONNECTEX disconnect_ex_ = nullptr; LPFN_GETACCEPTEXSOCKADDRS get_accept_ex_sockaddrs_ = nullptr; DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation); }; } // namespace bin } // namespace dart #endif // RUNTIME_BIN_EVENTHANDLER_WIN_H_