| /* |
| * |
| * Copyright 2015 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include "src/core/lib/iomgr/port.h" |
| |
| #ifdef GRPC_WINSOCK_SOCKET |
| |
| #include <winsock2.h> |
| #include <limits> |
| |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/log_windows.h> |
| |
| #include "src/core/lib/debug/stats.h" |
| #include "src/core/lib/gprpp/thd.h" |
| #include "src/core/lib/iomgr/iocp_windows.h" |
| #include "src/core/lib/iomgr/iomgr_internal.h" |
| #include "src/core/lib/iomgr/socket_windows.h" |
| #include "src/core/lib/iomgr/timer.h" |
| |
| static ULONG g_iocp_kick_token; |
| static OVERLAPPED g_iocp_custom_overlap; |
| |
| static gpr_atm g_custom_events = 0; |
| |
| static HANDLE g_iocp; |
| |
| static DWORD deadline_to_millis_timeout(grpc_millis deadline) { |
| if (deadline == GRPC_MILLIS_INF_FUTURE) { |
| return INFINITE; |
| } |
| grpc_millis now = grpc_core::ExecCtx::Get()->Now(); |
| if (deadline < now) return 0; |
| grpc_millis timeout = deadline - now; |
| if (timeout > std::numeric_limits<DWORD>::max()) return INFINITE; |
| return static_cast<DWORD>(deadline - now); |
| } |
| |
| grpc_iocp_work_status grpc_iocp_work(grpc_millis deadline) { |
| BOOL success; |
| DWORD bytes = 0; |
| DWORD flags = 0; |
| ULONG_PTR completion_key; |
| LPOVERLAPPED overlapped; |
| grpc_winsocket* socket; |
| grpc_winsocket_callback_info* info; |
| GRPC_STATS_INC_SYSCALL_POLL(); |
| success = |
| GetQueuedCompletionStatus(g_iocp, &bytes, &completion_key, &overlapped, |
| deadline_to_millis_timeout(deadline)); |
| grpc_core::ExecCtx::Get()->InvalidateNow(); |
| if (success == 0 && overlapped == NULL) { |
| return GRPC_IOCP_WORK_TIMEOUT; |
| } |
| GPR_ASSERT(completion_key && overlapped); |
| if (overlapped == &g_iocp_custom_overlap) { |
| gpr_atm_full_fetch_add(&g_custom_events, -1); |
| if (completion_key == (ULONG_PTR)&g_iocp_kick_token) { |
| /* We were awoken from a kick. */ |
| return GRPC_IOCP_WORK_KICK; |
| } |
| gpr_log(GPR_ERROR, "Unknown custom completion key."); |
| abort(); |
| } |
| |
| socket = (grpc_winsocket*)completion_key; |
| if (overlapped == &socket->write_info.overlapped) { |
| info = &socket->write_info; |
| } else if (overlapped == &socket->read_info.overlapped) { |
| info = &socket->read_info; |
| } else { |
| abort(); |
| } |
| if (socket->shutdown_called) { |
| info->bytes_transfered = 0; |
| info->wsa_error = WSA_OPERATION_ABORTED; |
| } else { |
| success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, |
| FALSE, &flags); |
| info->bytes_transfered = bytes; |
| info->wsa_error = success ? 0 : WSAGetLastError(); |
| } |
| GPR_ASSERT(overlapped == &info->overlapped); |
| grpc_socket_become_ready(socket, info); |
| return GRPC_IOCP_WORK_WORK; |
| } |
| |
| void grpc_iocp_init(void) { |
| g_iocp = |
| CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); |
| GPR_ASSERT(g_iocp); |
| } |
| |
| void grpc_iocp_kick(void) { |
| BOOL success; |
| |
| gpr_atm_full_fetch_add(&g_custom_events, 1); |
| success = PostQueuedCompletionStatus(g_iocp, 0, (ULONG_PTR)&g_iocp_kick_token, |
| &g_iocp_custom_overlap); |
| GPR_ASSERT(success); |
| } |
| |
| void grpc_iocp_flush(void) { |
| grpc_core::ExecCtx exec_ctx; |
| grpc_iocp_work_status work_status; |
| |
| do { |
| work_status = grpc_iocp_work(GRPC_MILLIS_INF_PAST); |
| } while (work_status == GRPC_IOCP_WORK_KICK || |
| grpc_core::ExecCtx::Get()->Flush()); |
| } |
| |
| void grpc_iocp_shutdown(void) { |
| grpc_core::ExecCtx exec_ctx; |
| while (gpr_atm_acq_load(&g_custom_events)) { |
| grpc_iocp_work(GRPC_MILLIS_INF_FUTURE); |
| grpc_core::ExecCtx::Get()->Flush(); |
| } |
| |
| GPR_ASSERT(CloseHandle(g_iocp)); |
| } |
| |
| void grpc_iocp_add_socket(grpc_winsocket* socket) { |
| HANDLE ret; |
| if (socket->added_to_iocp) return; |
| ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp, |
| (uintptr_t)socket, 0); |
| if (!ret) { |
| char* utf8_message = gpr_format_message(WSAGetLastError()); |
| gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message); |
| gpr_free(utf8_message); |
| __debugbreak(); |
| abort(); |
| } |
| socket->added_to_iocp = 1; |
| GPR_ASSERT(ret == g_iocp); |
| } |
| |
| #endif /* GRPC_WINSOCK_SOCKET */ |