brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.1 KiB · 8a24810 Raw
599 lines · cpp
1//===-- MainLoopTest.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/MainLoop.h"10#include "TestingSupport/SubsystemRAII.h"11#include "lldb/Host/ConnectionFileDescriptor.h"12#include "lldb/Host/FileSystem.h"13#include "lldb/Host/MainLoopBase.h"14#include "lldb/Host/PseudoTerminal.h"15#include "lldb/Host/common/TCPSocket.h"16#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX17#include "llvm/Testing/Support/Error.h"18#include "gtest/gtest.h"19#include <chrono>20#include <future>21#include <thread>22 23using namespace lldb_private;24 25namespace {26class MainLoopTest : public testing::Test {27public:28  SubsystemRAII<FileSystem, Socket> subsystems;29 30  void SetUp() override {31    Status error;32    auto listen_socket_up = std::make_unique<TCPSocket>(true);33    ASSERT_TRUE(error.Success());34    error = listen_socket_up->Listen("localhost:0", 5);35    ASSERT_TRUE(error.Success());36 37    Socket *accept_socket;38    auto connect_socket_up = std::make_unique<TCPSocket>(true);39    error = connect_socket_up->Connect(40        llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber())41            .str());42    ASSERT_TRUE(error.Success());43    ASSERT_TRUE(listen_socket_up->Accept(std::chrono::seconds(1), accept_socket)44                    .Success());45 46    callback_count = 0;47    socketpair[0] = std::move(connect_socket_up);48    socketpair[1].reset(accept_socket);49  }50 51  void TearDown() override {52    socketpair[0].reset();53    socketpair[1].reset();54  }55 56protected:57  MainLoop::Callback make_callback() {58    return [&](MainLoopBase &loop) {59      ++callback_count;60      loop.RequestTermination();61    };62  }63  std::shared_ptr<Socket> socketpair[2];64  unsigned callback_count;65};66} // namespace67 68TEST_F(MainLoopTest, ReadSocketObject) {69  char X = 'X';70  size_t len = sizeof(X);71  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());72 73  MainLoop loop;74 75  Status error;76  auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error);77  ASSERT_TRUE(error.Success());78  ASSERT_TRUE(handle);79  ASSERT_TRUE(loop.Run().Success());80  ASSERT_EQ(1u, callback_count);81}82 83// Flakey, see https://github.com/llvm/llvm-project/issues/152677.84#ifndef _WIN3285TEST_F(MainLoopTest, ReadPipeObject) {86  Pipe pipe;87 88  ASSERT_TRUE(pipe.CreateNew().Success());89 90  MainLoop loop;91 92  char X = 'X';93  size_t len = sizeof(X);94  ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));95 96  Status error;97  auto handle = loop.RegisterReadObject(98      std::make_shared<NativeFile>(pipe.GetReadFileDescriptor(),99                                   File::eOpenOptionReadOnly, false),100      make_callback(), error);101  ASSERT_TRUE(error.Success());102  ASSERT_TRUE(handle);103  ASSERT_TRUE(loop.Run().Success());104  ASSERT_EQ(1u, callback_count);105}106 107TEST_F(MainLoopTest, MultipleReadsPipeObject) {108  Pipe pipe;109 110  ASSERT_TRUE(pipe.CreateNew().Success());111 112  MainLoop loop;113 114  std::future<void> async_writer = std::async(std::launch::async, [&] {115    for (int i = 0; i < 5; ++i) {116      std::this_thread::sleep_for(std::chrono::milliseconds(500));117      char X = 'X';118      size_t len = sizeof(X);119      ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));120    }121  });122 123  Status error;124  lldb::FileSP file = std::make_shared<NativeFile>(125      pipe.GetReadFileDescriptor(), File::eOpenOptionReadOnly, false);126  auto handle = loop.RegisterReadObject(127      file,128      [&](MainLoopBase &loop) {129        callback_count++;130        if (callback_count == 5)131          loop.RequestTermination();132 133        // Read some data to ensure the handle is not in a readable state.134        char buf[1024] = {0};135        size_t len = sizeof(buf);136        ASSERT_THAT_ERROR(file->Read(buf, len).ToError(), llvm::Succeeded());137        EXPECT_EQ(len, 1U);138        EXPECT_EQ(buf[0], 'X');139      },140      error);141  ASSERT_TRUE(error.Success());142  ASSERT_TRUE(handle);143  ASSERT_TRUE(loop.Run().Success());144  ASSERT_EQ(5u, callback_count);145  async_writer.wait();146}147#endif148 149TEST_F(MainLoopTest, PipeDelayBetweenRegisterAndRun) {150  Pipe pipe;151 152  ASSERT_TRUE(pipe.CreateNew().Success());153 154  MainLoop loop;155 156  Status error;157  lldb::FileSP file = std::make_shared<NativeFile>(158      pipe.GetReadFileDescriptor(), File::eOpenOptionReadOnly, false);159  auto handle = loop.RegisterReadObject(160      file,161      [&](MainLoopBase &loop) {162        callback_count++;163 164        // Read some data to ensure the handle is not in a readable state.165        char buf[1024] = {0};166        size_t len = sizeof(buf);167        ASSERT_THAT_ERROR(file->Read(buf, len).ToError(), llvm::Succeeded());168        EXPECT_EQ(len, 2U);169        EXPECT_EQ(buf[0], 'X');170        EXPECT_EQ(buf[1], 'X');171      },172      error);173  auto cb = [&](MainLoopBase &) {174    callback_count++;175    char X = 'X';176    size_t len = sizeof(X);177    // Write twice and ensure we coalesce into a single read.178    ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));179    ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));180  };181  // Add a write that triggers a read events.182  bool addition_succeeded =183      loop.AddCallback(cb, std::chrono::milliseconds(500));184  ASSERT_TRUE(addition_succeeded);185  addition_succeeded =186      loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },187                       std::chrono::milliseconds(1000));188  ASSERT_TRUE(addition_succeeded);189  ASSERT_TRUE(error.Success());190  ASSERT_TRUE(handle);191 192  // Write between RegisterReadObject / Run should NOT invoke the callback.193  cb(loop);194  ASSERT_EQ(1u, callback_count);195 196  ASSERT_TRUE(loop.Run().Success());197  ASSERT_EQ(4u, callback_count);198}199 200TEST_F(MainLoopTest, NoSelfTriggersDuringPipeHandler) {201  Pipe pipe;202 203  ASSERT_TRUE(pipe.CreateNew().Success());204 205  MainLoop loop;206 207  Status error;208  lldb::FileSP file = std::make_shared<NativeFile>(209      pipe.GetReadFileDescriptor(), File::eOpenOptionReadOnly, false);210  auto handle = loop.RegisterReadObject(211      file,212      [&](MainLoopBase &lop) {213        callback_count++;214 215        char X = 'Y';216        size_t len = sizeof(X);217        // writes / reads during the handler callback should NOT trigger itself.218        ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));219 220        char buf[1024] = {0};221        len = sizeof(buf);222        ASSERT_THAT_ERROR(file->Read(buf, len).ToError(), llvm::Succeeded());223        EXPECT_EQ(len, 2U);224        EXPECT_EQ(buf[0], 'X');225        EXPECT_EQ(buf[1], 'Y');226 227        if (callback_count == 2)228          loop.RequestTermination();229      },230      error);231  // Add a write that triggers a read event.232  loop.AddPendingCallback([&](MainLoopBase &) {233    char X = 'X';234    size_t len = sizeof(X);235    ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));236  });237  loop.AddCallback(238      [&](MainLoopBase &) {239        char X = 'X';240        size_t len = sizeof(X);241        ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));242      },243      std::chrono::milliseconds(500));244  ASSERT_TRUE(error.Success());245  ASSERT_TRUE(handle);246  ASSERT_TRUE(loop.Run().Success());247  ASSERT_EQ(2u, callback_count);248}249 250TEST_F(MainLoopTest, NoSpuriousPipeReads) {251  Pipe pipe;252 253  ASSERT_TRUE(pipe.CreateNew().Success());254 255  char X = 'X';256  size_t len = sizeof(X);257  ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::Succeeded());258 259  lldb::IOObjectSP r = std::make_shared<NativeFile>(260      pipe.GetReadFileDescriptor(), File::eOpenOptionReadOnly, false);261 262  MainLoop loop;263 264  Status error;265  auto handle = loop.RegisterReadObject(266      r,267      [&](MainLoopBase &) {268        if (callback_count == 0) {269          // Read the byte back the first time we're called. After that, the270          // pipe is empty, and we should not be called anymore.271          char X;272          size_t len = sizeof(X);273          ASSERT_THAT_ERROR(r->Read(&X, len).ToError(), llvm::Succeeded());274          EXPECT_EQ(len, sizeof(X));275          EXPECT_EQ(X, 'X');276        }277        ++callback_count;278      },279      error);280  ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());281  // Terminate the loop after one second.282  loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },283                   std::chrono::seconds(1));284  ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded());285 286  // Make sure the callback was called only once.287  ASSERT_EQ(1u, callback_count);288}289 290TEST_F(MainLoopTest, NoSpuriousSocketReads) {291  // Write one byte into the socket.292  char X = 'X';293  size_t len = sizeof(X);294  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());295 296  MainLoop loop;297 298  Status error;299  auto handle = loop.RegisterReadObject(300      socketpair[1],301      [this](MainLoopBase &) {302        if (callback_count == 0) {303          // Read the byte back the first time we're called. After that, the304          // socket is empty, and we should not be called anymore.305          char X;306          size_t len = sizeof(X);307          EXPECT_THAT_ERROR(socketpair[1]->Read(&X, len).ToError(),308                            llvm::Succeeded());309          EXPECT_EQ(len, sizeof(X));310          EXPECT_EQ(X, 'X');311        }312        ++callback_count;313      },314      error);315  ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());316  // Terminate the loop after one second.317  bool addition_succeeded =318      loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },319                       std::chrono::seconds(1));320  ASSERT_TRUE(addition_succeeded);321  ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded());322 323  // Make sure the callback was called only once.324  ASSERT_EQ(1u, callback_count);325}326 327TEST_F(MainLoopTest, TerminatesImmediately) {328  char X = 'X';329  size_t len = sizeof(X);330  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());331  ASSERT_TRUE(socketpair[1]->Write(&X, len).Success());332 333  MainLoop loop;334  Status error;335  auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error);336  ASSERT_TRUE(error.Success());337  auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error);338  ASSERT_TRUE(error.Success());339 340  ASSERT_TRUE(loop.Run().Success());341  ASSERT_EQ(1u, callback_count);342}343 344TEST_F(MainLoopTest, PendingCallback) {345  char X = 'X';346  size_t len = sizeof(X);347  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());348 349  MainLoop loop;350  Status error;351  auto handle = loop.RegisterReadObject(352      socketpair[1],353      [&](MainLoopBase &loop) {354        // Both callbacks should be called before the loop terminates.355        loop.AddPendingCallback(make_callback());356        loop.AddPendingCallback(make_callback());357        loop.RequestTermination();358      },359      error);360  ASSERT_TRUE(error.Success());361  ASSERT_TRUE(handle);362  ASSERT_TRUE(loop.Run().Success());363  ASSERT_EQ(2u, callback_count);364}365 366TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) {367  char X = 'X';368  size_t len = sizeof(X);369  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());370 371  MainLoop loop;372  Status error;373  auto handle = loop.RegisterReadObject(374      socketpair[1],375      [&](MainLoopBase &loop) {376        // Add one pending callback on the first iteration.377        if (callback_count == 0) {378          loop.AddPendingCallback(379              [&](MainLoopBase &loop) { callback_count++; });380        }381        // Terminate the loop on second iteration.382        if (callback_count++ >= 1)383          loop.RequestTermination();384      },385      error);386  ASSERT_TRUE(error.Success());387  ASSERT_TRUE(handle);388  ASSERT_TRUE(loop.Run().Success());389  // 2 iterations of read callback + 1 call of pending callback.390  ASSERT_EQ(3u, callback_count);391}392 393TEST_F(MainLoopTest, PendingCallbackTrigger) {394  MainLoop loop;395  std::promise<void> add_callback2;396  bool callback1_called = false;397  bool addition_succeeded = loop.AddPendingCallback([&](MainLoopBase &loop) {398    callback1_called = true;399    add_callback2.set_value();400  });401  EXPECT_TRUE(addition_succeeded);402  Status error;403  ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());404  bool callback2_called = false;405  std::thread callback2_adder([&]() {406    add_callback2.get_future().get();407    loop.AddPendingCallback([&](MainLoopBase &loop) {408      callback2_called = true;409      loop.RequestTermination();410    });411  });412  ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded());413  callback2_adder.join();414  ASSERT_TRUE(callback1_called);415  ASSERT_TRUE(callback2_called);416}417 418TEST_F(MainLoopTest, ManyPendingCallbacks) {419  MainLoop loop;420  Status error;421  // Try to fill up the pipe buffer and make sure bad things don't happen. This422  // is a regression test for the case where writing to the interrupt pipe423  // caused a deadlock when the pipe filled up (either because the main loop was424  // not running, because it was slow, or because it was busy/blocked doing425  // something else).426  for (int i = 0; i < 65536; ++i) {427    bool addition_succeeded = loop.AddPendingCallback(428        [&](MainLoopBase &loop) { loop.RequestTermination(); });429    EXPECT_TRUE(addition_succeeded);430  }431  ASSERT_TRUE(loop.Run().Success());432}433 434TEST_F(MainLoopTest, CallbackWithTimeout) {435  MainLoop loop;436  auto start = std::chrono::steady_clock::now();437  loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },438                   std::chrono::seconds(2));439  ASSERT_THAT_ERROR(loop.Run().takeError(), llvm::Succeeded());440  EXPECT_GE(std::chrono::steady_clock::now() - start, std::chrono::seconds(2));441}442 443TEST_F(MainLoopTest, TimedCallbacksRunInOrder) {444  MainLoop loop;445  auto start = std::chrono::steady_clock::now();446  std::chrono::milliseconds epsilon(10);447  std::vector<int> order;448  auto add_cb = [&](int id) {449    loop.AddCallback([&order, id](MainLoopBase &) { order.push_back(id); },450                     start + id * epsilon);451  };452  add_cb(3);453  add_cb(2);454  add_cb(4);455  add_cb(1);456  bool addition_succeeded =457      loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },458                       start + 5 * epsilon);459  EXPECT_TRUE(addition_succeeded);460  ASSERT_THAT_ERROR(loop.Run().takeError(), llvm::Succeeded());461  EXPECT_GE(std::chrono::steady_clock::now() - start, 5 * epsilon);462  ASSERT_THAT(order, testing::ElementsAre(1, 2, 3, 4));463}464 465TEST_F(MainLoopTest, TimedCallbackShortensSleep) {466  MainLoop loop;467  auto start = std::chrono::steady_clock::now();468  bool long_callback_called = false;469  bool addition_succeeded = loop.AddCallback(470      [&](MainLoopBase &loop) {471        long_callback_called = true;472        loop.RequestTermination();473      },474      std::chrono::seconds(30));475  EXPECT_TRUE(addition_succeeded);476  std::future<Status> async_run =477      std::async(std::launch::async, &MainLoop::Run, std::ref(loop));478  std::this_thread::sleep_for(std::chrono::milliseconds(100));479  bool short_callback_called = false;480  addition_succeeded = loop.AddCallback(481      [&](MainLoopBase &loop) {482        short_callback_called = true;483        loop.RequestTermination();484      },485      std::chrono::seconds(1));486  EXPECT_TRUE(addition_succeeded);487  ASSERT_THAT_ERROR(async_run.get().takeError(), llvm::Succeeded());488  EXPECT_LT(std::chrono::steady_clock::now() - start, std::chrono::seconds(10));489  EXPECT_TRUE(short_callback_called);490  EXPECT_FALSE(long_callback_called);491}492 493#ifdef LLVM_ON_UNIX494TEST_F(MainLoopTest, DetectsEOF) {495 496  PseudoTerminal term;497  ASSERT_THAT_ERROR(term.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded());498  ASSERT_THAT_ERROR(term.OpenSecondary(O_RDWR | O_NOCTTY), llvm::Succeeded());499  auto conn = std::make_unique<ConnectionFileDescriptor>(500      term.ReleasePrimaryFileDescriptor(), true);501 502  Status error;503  MainLoop loop;504  auto handle =505      loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error);506  ASSERT_TRUE(error.Success());507  term.CloseSecondaryFileDescriptor();508 509  ASSERT_TRUE(loop.Run().Success());510  ASSERT_EQ(1u, callback_count);511}512 513TEST_F(MainLoopTest, Signal) {514  MainLoop loop;515  Status error;516 517  auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);518  ASSERT_TRUE(error.Success());519  kill(getpid(), SIGUSR1);520  ASSERT_TRUE(loop.Run().Success());521  ASSERT_EQ(1u, callback_count);522}523 524TEST_F(MainLoopTest, SignalOnOtherThread) {525  MainLoop loop;526  Status error;527 528  auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);529  ASSERT_TRUE(error.Success());530  std::thread([] { pthread_kill(pthread_self(), SIGUSR1); }).join();531  ASSERT_TRUE(loop.Run().Success());532  ASSERT_EQ(1u, callback_count);533}534 535// Test that a signal which is not monitored by the MainLoop does not536// cause a premature exit.537TEST_F(MainLoopTest, UnmonitoredSignal) {538  MainLoop loop;539  Status error;540  struct sigaction sa;541  sa.sa_sigaction = [](int, siginfo_t *, void *) {};542  sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART543  sigemptyset(&sa.sa_mask);544  ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr));545 546  auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);547  ASSERT_TRUE(error.Success());548  kill(getpid(), SIGUSR2);549  kill(getpid(), SIGUSR1);550  ASSERT_TRUE(loop.Run().Success());551  ASSERT_EQ(1u, callback_count);552}553 554// Test that two callbacks can be registered for the same signal555// and unregistered independently.556TEST_F(MainLoopTest, TwoSignalCallbacks) {557  MainLoop loop;558  Status error;559  unsigned callback2_count = 0;560  unsigned callback3_count = 0;561 562  auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);563  ASSERT_TRUE(error.Success());564 565  {566    // Run a single iteration with two callbacks enabled.567    auto handle2 = loop.RegisterSignal(568        SIGUSR1, [&](MainLoopBase &loop) { ++callback2_count; }, error);569    ASSERT_TRUE(error.Success());570 571    kill(getpid(), SIGUSR1);572    ASSERT_TRUE(loop.Run().Success());573    ASSERT_EQ(1u, callback_count);574    ASSERT_EQ(1u, callback2_count);575    ASSERT_EQ(0u, callback3_count);576  }577 578  {579    // Make sure that remove + add new works.580    auto handle3 = loop.RegisterSignal(581        SIGUSR1, [&](MainLoopBase &loop) { ++callback3_count; }, error);582    ASSERT_TRUE(error.Success());583 584    kill(getpid(), SIGUSR1);585    ASSERT_TRUE(loop.Run().Success());586    ASSERT_EQ(2u, callback_count);587    ASSERT_EQ(1u, callback2_count);588    ASSERT_EQ(1u, callback3_count);589  }590 591  // Both extra callbacks should be unregistered now.592  kill(getpid(), SIGUSR1);593  ASSERT_TRUE(loop.Run().Success());594  ASSERT_EQ(3u, callback_count);595  ASSERT_EQ(1u, callback2_count);596  ASSERT_EQ(1u, callback3_count);597}598#endif599