Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bab0146d authored by Zach Johnson's avatar Zach Johnson
Browse files

Add Reactor.WaitForIdle

Given fuzzing is deterministic (any more data or work to handle will be posted
by existing work) we can wait for the reactor to become idle.

Add support for this and begin using in the fuzz tests.

Test: fuzz/run --host bluetooth_gd_hci_fuzz_test
Change-Id: Ib6acaf159348f886e6dc7c081a1a081df8f460bd
parent 01ea436c
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "hci/fuzz/dev_null_hci.h"
#include "hci/hci_layer.h"
#include "module.h"
#include "os/log.h"

#include <fuzzer/FuzzedDataProvider.h>

@@ -49,9 +50,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    }
  }

  // TODO replace with something more general in thread/reactor
  fuzzHal->waitForHandler();

  if (!moduleRegistry.GetTestThread().GetReactor()->WaitForIdle(std::chrono::milliseconds(100))) {
    LOG_ERROR("idle timed out");
  }
  moduleRegistry.StopAll();
  return 0;
}
+36 −5
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ namespace {

// Use at most sizeof(epoll_event) * kEpollMaxEvents kernel memory
constexpr int kEpollMaxEvents = 64;
constexpr uint64_t kStopReactor = 1 << 0;
constexpr uint64_t kWaitForIdle = 1 << 1;

}  // namespace

@@ -82,6 +84,8 @@ void Reactor::Run() {
  bool already_running = is_running_.exchange(true);
  ASSERT(!already_running);

  int timeout_ms = -1;
  bool waiting_for_idle = false;
  for (;;) {
    {
      std::unique_lock<std::mutex> lock(mutex_);
@@ -89,8 +93,14 @@ void Reactor::Run() {
    }
    epoll_event events[kEpollMaxEvents];
    int count;
    RUN_NO_INTR(count = epoll_wait(epoll_fd_, events, kEpollMaxEvents, -1));
    RUN_NO_INTR(count = epoll_wait(epoll_fd_, events, kEpollMaxEvents, timeout_ms));
    ASSERT(count != -1);
    if (waiting_for_idle && count == 0) {
      timeout_ms = -1;
      waiting_for_idle = false;
      idle_promise_->set_value();
      idle_promise_ = nullptr;
    }

    for (int i = 0; i < count; ++i) {
      auto event = events[i];
@@ -100,8 +110,14 @@ void Reactor::Run() {
      if (event.data.ptr == nullptr) {
        uint64_t value;
        eventfd_read(control_fd_, &value);
        if ((value & kStopReactor) != 0) {
          is_running_ = false;
          return;
        } else if ((value & kWaitForIdle) != 0) {
          timeout_ms = 30;
          waiting_for_idle = true;
          continue;
        }
      }
      auto* reactable = static_cast<Reactor::Reactable*>(event.data.ptr);
      std::unique_lock<std::mutex> lock(mutex_);
@@ -138,7 +154,7 @@ void Reactor::Stop() {
  if (!is_running_) {
    LOG_WARN("not running, will stop once it's started");
  }
  auto control = eventfd_write(control_fd_, 1);
  auto control = eventfd_write(control_fd_, kStopReactor);
  ASSERT(control != -1);
}

@@ -183,7 +199,7 @@ void Reactor::Unregister(Reactor::Reactable* reactable) {
    if (reactable->is_executing_) {
      reactable->removed_ = true;
      reactable->finished_promise_ = std::make_unique<std::promise<void>>();
      executing_reactable_finished_ = std::make_unique<std::future<void>>(reactable->finished_promise_->get_future());
      executing_reactable_finished_ = std::make_shared<std::future<void>>(reactable->finished_promise_->get_future());
      delaying_delete_until_callback_finished = true;
    }
  }
@@ -205,6 +221,21 @@ bool Reactor::WaitForUnregisteredReactable(std::chrono::milliseconds timeout) {
  return stop_status == std::future_status::ready;
}

bool Reactor::WaitForIdle(std::chrono::milliseconds timeout) {
  auto promise = std::make_shared<std::promise<void>>();
  auto future = std::make_unique<std::future<void>>(promise->get_future());
  {
    std::lock_guard<std::mutex> lock(mutex_);
    idle_promise_ = promise;
  }

  auto control = eventfd_write(control_fd_, kWaitForIdle);
  ASSERT(control != -1);

  auto idle_status = future->wait_for(timeout);
  return idle_status == std::future_status::ready;
}

void Reactor::ModifyRegistration(Reactor::Reactable* reactable, Closure on_read_ready, Closure on_write_ready) {
  ASSERT(reactable != nullptr);

+4 −0
Original line number Diff line number Diff line
@@ -69,6 +69,9 @@ class Reactor {
  // Wait for up to timeout milliseconds, and return true if the reactable finished executing.
  bool WaitForUnregisteredReactable(std::chrono::milliseconds timeout);

  // Wait for up to timeout milliseconds, and return true if we reached idle.
  bool WaitForIdle(std::chrono::milliseconds timeout);

  // Modify the registration for a reactable with given reactable
  void ModifyRegistration(Reactable* reactable, Closure on_read_ready, Closure on_write_ready);

@@ -79,6 +82,7 @@ class Reactor {
  std::atomic<bool> is_running_;
  std::list<Reactable*> invalidation_list_;
  std::shared_ptr<std::future<void>> executing_reactable_finished_;
  std::shared_ptr<std::promise<void>> idle_promise_;
};

}  // namespace os