1 // Copyright 2022 gRPC authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 #ifndef GRPC_SRC_CORE_LIB_EVENT_ENGINE_WINDOWS_WIN_SOCKET_H 15 #define GRPC_SRC_CORE_LIB_EVENT_ENGINE_WINDOWS_WIN_SOCKET_H 16 17 #include <grpc/support/port_platform.h> 18 19 #ifdef GPR_WINDOWS 20 21 #include <grpc/event_engine/event_engine.h> 22 23 #include "absl/base/thread_annotations.h" 24 #include "absl/functional/any_invocable.h" 25 #include "src/core/lib/event_engine/thread_pool/thread_pool.h" 26 #include "src/core/util/debug_location.h" 27 #include "src/core/util/sync.h" 28 29 namespace grpc_event_engine { 30 namespace experimental { 31 32 class WinSocket { 33 public: 34 struct OverlappedResult { 35 int wsa_error; 36 DWORD bytes_transferred; 37 absl::Status error_status; 38 }; 39 40 // State related to a Read or Write socket operation 41 class OpState { 42 public: 43 explicit OpState(WinSocket* win_socket) noexcept; 44 // Signal a result has returned 45 // If a callback is already primed for notification, it will be executed via 46 // the WinSocket's ThreadPool. Otherwise, a "pending iocp" flag will 47 // be set. 48 void SetReady(); 49 // Set WSA result for a completed op. 50 // If the error is non-zero, bytes will be overridden to 0. 51 void SetResult(int wsa_error, DWORD bytes, absl::string_view context); 52 // Set error results for a completed op. 53 // This is a manual override, meant to ignore any WSA status code. 54 void SetErrorStatus(absl::Status error_status); 55 // Retrieve the results of an overlapped operation (via Winsock API) and 56 // store them locally. 57 void GetOverlappedResult(); 58 // Retrieve the results of an overlapped operation (via Winsock API) and 59 // store them locally. This overload allows acceptance of connections on new 60 // sockets. 61 void GetOverlappedResult(SOCKET sock); 62 // Retrieve the cached result from GetOverlappedResult result()63 const OverlappedResult& result() const { return result_; } 64 // OVERLAPPED, needed for Winsock API calls overlapped()65 LPOVERLAPPED overlapped() { return &overlapped_; } 66 67 private: 68 friend class WinSocket; 69 70 OVERLAPPED overlapped_; 71 WinSocket* win_socket_ = nullptr; 72 EventEngine::Closure* closure_ = nullptr; 73 OverlappedResult result_; 74 }; 75 76 WinSocket(SOCKET socket, ThreadPool* thread_pool) noexcept; 77 ~WinSocket(); 78 // Provide a closure that will be called when an IOCP completion has occurred. 79 // 80 // Notification callbacks *must be registered* before any WSASend or WSARecv 81 // operations are started. Only one closure can be registered at a time for 82 // each read or send operation. 83 void NotifyOnRead(EventEngine::Closure* on_read); 84 void NotifyOnWrite(EventEngine::Closure* on_write); 85 // Remove the notification callback for read/write events. 86 // 87 // This method should only be called if no IOCP event is pending for the 88 // socket. It is UB if an IOCP event comes through and a notification is not 89 // registered. 90 void UnregisterReadCallback(); 91 void UnregisterWriteCallback(); 92 93 bool IsShutdown(); 94 // Shutdown socket operations, but do not delete the WinSocket. 95 // Connections will be disconnected, and the socket will be closed. 96 // If the socket is managed by a shared_ptr (most should be), then the 97 // WinSocket will be deleted when the last outstanding overlapped event comes 98 // back. 99 void Shutdown(); 100 void Shutdown(const grpc_core::DebugLocation& location, 101 absl::string_view reason); 102 103 // Return the appropriate OpState for a given OVERLAPPED 104 // Returns nullptr if the overlapped does not match either read or write ops. 105 OpState* GetOpInfoForOverlapped(OVERLAPPED* overlapped); 106 // Getters for the operation state data. read_info()107 OpState* read_info() { return &read_info_; } write_info()108 OpState* write_info() { return &write_info_; } 109 // Accessor method for underlying socket 110 SOCKET raw_socket(); 111 112 private: 113 void NotifyOnReady(OpState& info, EventEngine::Closure* closure); 114 115 SOCKET socket_; 116 std::atomic<bool> is_shutdown_{false}; 117 ThreadPool* thread_pool_; 118 // These OpStates are effectively synchronized using their respective 119 // OVERLAPPED structures and the Overlapped I/O APIs. For example, OpState 120 // users should not attempt to read their bytes_transeferred until 121 // GetOverlappedResult has returned, to ensure there are no two threads 122 // reading and writing the same values concurrently. 123 // 124 // Callers must also ensure that at most one read and write operation are 125 // occurring at a time. Attempting to do multiple concurrent reads/writes will 126 // have undefined behavior. 127 OpState read_info_; 128 OpState write_info_; 129 }; 130 131 // Attempt to configure default socket settings 132 absl::Status PrepareSocket(SOCKET sock); 133 134 // Set non block option for socket. 135 absl::Status SetSocketNonBlock(SOCKET sock); 136 137 } // namespace experimental 138 } // namespace grpc_event_engine 139 140 #endif 141 142 #endif // GRPC_SRC_CORE_LIB_EVENT_ENGINE_WINDOWS_WIN_SOCKET_H 143