brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.3 KiB · 5e5888a Raw
278 lines · cpp
1//===-- MainLoopWindows.cpp -----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/windows/MainLoopWindows.h"10#include "lldb/Host/Config.h"11#include "lldb/Host/Socket.h"12#include "lldb/Host/windows/windows.h"13#include "lldb/Utility/Status.h"14#include "llvm/Config/llvm-config.h"15#include "llvm/Support/WindowsError.h"16#include <algorithm>17#include <atomic>18#include <cassert>19#include <ctime>20#include <io.h>21#include <synchapi.h>22#include <thread>23#include <vector>24#include <winbase.h>25#include <winerror.h>26#include <winsock2.h>27 28using namespace lldb;29using namespace lldb_private;30 31static DWORD ToTimeout(std::optional<MainLoopWindows::TimePoint> point) {32  using namespace std::chrono;33 34  if (!point)35    return WSA_INFINITE;36 37  nanoseconds dur = (std::max)(*point - steady_clock::now(), nanoseconds(0));38  return ceil<milliseconds>(dur).count();39}40 41namespace {42 43class PipeEvent : public MainLoopWindows::IOEvent {44public:45  explicit PipeEvent(HANDLE handle)46      : IOEvent(CreateEventW(NULL, /*bManualReset=*/TRUE,47                             /*bInitialState=*/FALSE, NULL)),48        m_handle(handle), m_ready(CreateEventW(NULL, /*bManualReset=*/TRUE,49                                               /*bInitialState=*/FALSE, NULL)) {50    assert(m_event && m_ready);51    m_monitor_thread = std::thread(&PipeEvent::Monitor, this);52  }53 54  ~PipeEvent() override {55    if (m_monitor_thread.joinable()) {56      m_stopped = true;57      SetEvent(m_ready);58      CancelIoEx(m_handle, /*lpOverlapped=*/NULL);59      m_monitor_thread.join();60    }61    CloseHandle(m_event);62    CloseHandle(m_ready);63  }64 65  void WillPoll() override {66    if (WaitForSingleObject(m_event, /*dwMilliseconds=*/0) != WAIT_TIMEOUT) {67      // The thread has already signalled that the data is available. No need68      // for further polling until we consume that event.69      return;70    }71    if (WaitForSingleObject(m_ready, /*dwMilliseconds=*/0) != WAIT_TIMEOUT) {72      // The thread is already waiting for data to become available.73      return;74    }75    // Start waiting.76    SetEvent(m_ready);77  }78 79  void Disarm() override { ResetEvent(m_event); }80 81  /// Monitors the handle performing a zero byte read to determine when data is82  /// avaiable.83  void Monitor() {84    // Wait until the MainLoop tells us to start.85    WaitForSingleObject(m_ready, INFINITE);86 87    do {88      char buf[1];89      DWORD bytes_read = 0;90      OVERLAPPED ov;91      ZeroMemory(&ov, sizeof(ov));92      // Block on a 0-byte read; this will only resume when data is93      // available in the pipe. The pipe must be PIPE_WAIT or this thread94      // will spin.95      BOOL success =96          ReadFile(m_handle, buf, /*nNumberOfBytesToRead=*/0, &bytes_read, &ov);97      DWORD bytes_available = 0;98      DWORD err = GetLastError();99      if (!success && err == ERROR_IO_PENDING) {100        success = GetOverlappedResult(m_handle, &ov, &bytes_read,101                                      /*bWait=*/TRUE);102        err = GetLastError();103      }104      if (success) {105        success =106            PeekNamedPipe(m_handle, NULL, 0, NULL, &bytes_available, NULL);107        err = GetLastError();108      }109      if (success) {110        if (bytes_available == 0) {111          // This can happen with a zero-byte write. Try again.112          continue;113        }114      } else if (err == ERROR_NO_DATA) {115        // The pipe is nonblocking. Try again.116        Sleep(0);117        continue;118      } else if (err == ERROR_OPERATION_ABORTED) {119        // Read may have been cancelled, try again.120        continue;121      }122 123      // Notify that data is available on the pipe. It's important to set this124      // before clearing m_ready to avoid a race with WillPoll.125      SetEvent(m_event);126      // Stop polling until we're told to resume.127      ResetEvent(m_ready);128 129      // Wait until the current read is consumed before doing the next read.130      WaitForSingleObject(m_ready, INFINITE);131    } while (!m_stopped);132  }133 134private:135  HANDLE m_handle;136  HANDLE m_ready;137  std::thread m_monitor_thread;138  std::atomic<bool> m_stopped = false;139};140 141class SocketEvent : public MainLoopWindows::IOEvent {142public:143  explicit SocketEvent(SOCKET socket)144      : IOEvent(WSACreateEvent()), m_socket(socket) {145    assert(m_event != WSA_INVALID_EVENT);146  }147 148  ~SocketEvent() override { WSACloseEvent(m_event); }149 150  void WillPoll() override {151    int result =152        WSAEventSelect(m_socket, m_event, FD_READ | FD_ACCEPT | FD_CLOSE);153    assert(result == 0);154    UNUSED_IF_ASSERT_DISABLED(result);155  }156 157  void DidPoll() override {158    int result = WSAEventSelect(m_socket, WSA_INVALID_EVENT, 0);159    assert(result == 0);160    UNUSED_IF_ASSERT_DISABLED(result);161  }162 163  void Disarm() override { WSAResetEvent(m_event); }164 165  SOCKET m_socket;166};167 168} // namespace169 170MainLoopWindows::MainLoopWindows() {171  m_interrupt_event = WSACreateEvent();172  assert(m_interrupt_event != WSA_INVALID_EVENT);173}174 175MainLoopWindows::~MainLoopWindows() {176  assert(m_read_fds.empty());177  BOOL result = WSACloseEvent(m_interrupt_event);178  assert(result == TRUE);179  UNUSED_IF_ASSERT_DISABLED(result);180}181 182llvm::Expected<size_t> MainLoopWindows::Poll() {183  std::vector<HANDLE> events;184  events.reserve(m_read_fds.size() + 1);185  for (auto &[_, fd_info] : m_read_fds) {186    fd_info.event->WillPoll();187    events.push_back(fd_info.event->GetHandle());188  }189  events.push_back(m_interrupt_event);190 191  DWORD result =192      WSAWaitForMultipleEvents(events.size(), events.data(), FALSE,193                               ToTimeout(GetNextWakeupTime()), FALSE);194 195  for (auto &[_, fd_info] : m_read_fds)196    fd_info.event->DidPoll();197 198  if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + events.size())199    return result - WSA_WAIT_EVENT_0;200 201  // A timeout is treated as a (premature) signalization of the interrupt event.202  if (result == WSA_WAIT_TIMEOUT)203    return events.size() - 1;204 205  return llvm::createStringError(llvm::inconvertibleErrorCode(),206                                 "WSAWaitForMultipleEvents failed");207}208 209MainLoopWindows::ReadHandleUP210MainLoopWindows::RegisterReadObject(const IOObjectSP &object_sp,211                                    const Callback &callback, Status &error) {212  if (!object_sp || !object_sp->IsValid()) {213    error = Status::FromErrorString("IO object is not valid.");214    return nullptr;215  }216 217  IOObject::WaitableHandle waitable_handle = object_sp->GetWaitableHandle();218  assert(waitable_handle != IOObject::kInvalidHandleValue);219 220  if (m_read_fds.find(waitable_handle) != m_read_fds.end()) {221    error = Status::FromErrorStringWithFormat(222        "File descriptor %p already monitored.", waitable_handle);223    return nullptr;224  }225 226  if (object_sp->GetFdType() == IOObject::eFDTypeSocket) {227    m_read_fds[waitable_handle] = {228        std::make_unique<SocketEvent>(229            reinterpret_cast<SOCKET>(waitable_handle)),230        callback};231  } else {232    DWORD file_type = GetFileType(waitable_handle);233    if (file_type != FILE_TYPE_PIPE) {234      error = Status::FromErrorStringWithFormat("Unsupported file type %ld",235                                                file_type);236      return nullptr;237    }238 239    m_read_fds[waitable_handle] = {std::make_unique<PipeEvent>(waitable_handle),240                                   callback};241  }242 243  return CreateReadHandle(object_sp);244}245 246void MainLoopWindows::UnregisterReadObject(IOObject::WaitableHandle handle) {247  auto it = m_read_fds.find(handle);248  assert(it != m_read_fds.end());249  m_read_fds.erase(it);250}251 252Status MainLoopWindows::Run() {253  m_terminate_request = false;254 255  Status error;256 257  while (!m_terminate_request) {258    llvm::Expected<size_t> signaled_event = Poll();259    if (!signaled_event)260      return Status::FromError(signaled_event.takeError());261 262    if (*signaled_event < m_read_fds.size()) {263      auto &KV = *std::next(m_read_fds.begin(), *signaled_event);264      KV.second.event->Disarm();265      KV.second.callback(*this); // Do the work.266    } else {267      assert(*signaled_event == m_read_fds.size());268      WSAResetEvent(m_interrupt_event);269    }270    ProcessCallbacks();271  }272  return Status();273}274 275bool MainLoopWindows::Interrupt() {276  return WSASetEvent(m_interrupt_event);277}278