| // 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 <mswsock.h> | |
| #include <winsock2.h> | |
| #include <ws2tcpip.h> | |
| #include <memory> | |
| #include <utility> | |
| #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<OverlappedBuffer> AllocateAcceptBuffer( | |
| Handle* handle, | |
| ADDRESS_FAMILY sa_family); | |
| static std::unique_ptr<OverlappedBuffer> AllocateReadBuffer(Handle* handle, | |
| int buffer_size); | |
| static std::unique_ptr<OverlappedBuffer> AllocateRecvFromBuffer( | |
| Handle* handle, | |
| int buffer_size); | |
| static std::unique_ptr<OverlappedBuffer> AllocateWriteBuffer(Handle* handle, | |
| int buffer_size); | |
| static std::unique_ptr<OverlappedBuffer> AllocateSendToBuffer( | |
| Handle* handle, | |
| int buffer_size); | |
| static std::unique_ptr<OverlappedBuffer> AllocateDisconnectBuffer( | |
| Handle* handle); | |
| static std::unique_ptr<OverlappedBuffer> 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<char*>(&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<OverlappedBuffer> 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<Handle>, 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<OverlappedBuffer> buffer); | |
| void WriteComplete(std::unique_ptr<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); } | |
| 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<OverlappedBuffer> | |
| 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<OverlappedBuffer> buffer); | |
| virtual bool IssueSendToLocked(MonitorLocker* ml, | |
| std::unique_ptr<OverlappedBuffer> buffer, | |
| const RawAddr& addr); | |
| int flags_ = 0; | |
| friend class ReferenceCounted<Handle>; | |
| DISALLOW_COPY_AND_ASSIGN(Handle); | |
| }; | |
| class FileHandle : public DescriptorInfoSingleMixin<Handle> { | |
| public: | |
| explicit FileHandle(HANDLE handle) | |
| : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(handle), | |
| kFile, | |
| SupportsOverlappedIO::kYes) {} | |
| virtual bool IsClosed(); | |
| protected: | |
| FileHandle(HANDLE handle, | |
| Type type, | |
| SupportsOverlappedIO supports_overlapped_io) | |
| : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(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<Handle> { | |
| public: | |
| DirectoryWatchHandle(HANDLE handle, int events, bool recursive) | |
| : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(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<SocketHandle> { | |
| 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<OverlappedBuffer> 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<SocketHandle> { | |
| public: | |
| explicit ClientSocket(intptr_t s, | |
| std::unique_ptr<RawAddr>&& 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<OverlappedBuffer> buffer); | |
| ClientSocket* next_; | |
| bool connected_; | |
| bool closed_; | |
| std::unique_ptr<RawAddr> remote_addr_; | |
| #if defined(DEBUG) | |
| static intptr_t disconnecting_; | |
| #endif | |
| DISALLOW_COPY_AND_ASSIGN(ClientSocket); | |
| }; | |
| class DatagramSocket : public DescriptorInfoSingleMixin<SocketHandle> { | |
| 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<OverlappedBuffer> 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<OverlappedBuffer> buffer); | |
| void HandleRead(Handle* handle, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer); | |
| void HandleRecvFrom(Handle* handle, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer); | |
| void HandleWrite(Handle* handle, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer); | |
| void HandleDisconnect(ClientSocket* client_socket, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> buffer); | |
| void HandleConnect(ClientSocket* client_socket, | |
| int bytes, | |
| std::unique_ptr<OverlappedBuffer> 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<bool> 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_ |