blob: 694139fa86be3fa831efd91a9c3529ace3afaaef [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <deque>
#include <functional>
#include <limits>
#include <map>
#include <mutex>
#include <vector>
#include "src/developer/debug/shared/logging/file_line_function.h"
#include "src/lib/fxl/macros.h"
#if defined(__Fuchsia__)
#include <zircon/compiler.h>
// The macros for thread annotations aren't set up for non-Fuchsia builds.
#define __TA_REQUIRES(arg)
namespace debug_ipc {
class FDWatcher;
// Message loop implementation. Unlike the one in FXL, this will run on the
// host in addition to a Zircon target.
class MessageLoop {
enum class WatchMode {
class WatchHandle;
// There can be only one active MessageLoop in scope per thread at a time.
// A message loop is active between Init() and Cleanup(). During this
// period, Current() will return the message loop.
// Init() / Cleanup() is a separate phase so a message loop can be created
// and managed on one thread and sent to another thread to actually run (to
// help with cross-thread task posting).
virtual ~MessageLoop();
// These must be called on the same thread as Run().
virtual void Init();
virtual void Cleanup();
// Exits the message loop immediately, not running pending functions. This
// must be called only on the MessageLoop thread.
virtual void QuitNow();
// Returns the current message loop or null if there isn't one.
static MessageLoop* Current();
// Runs the message loop.
void Run();
void PostTask(FileLineFunction file_line, std::function<void()> fn);
// Set a task to run after a certain number of miliseconds have elapsed.
// Granularity is hard to guarantee but the timer shouldn't fire earlier than
// expected.
void PostTimer(FileLineFunction file_line, uint64_t delta_ms,
std::function<void()> fn);
// Starts watching the given file descriptor in the given mode. Returns
// a WatchHandle that scopes the watch operation (when the handle is
// destroyed the watcher is unregistered).
// This function must only be called on the message loop thread.
// The watcher pointer must outlive the returned WatchHandle. Typically
// the class implementing the FDWatcher would keep the WatchHandle as a
// member. Must only be called on the message loop thread.
// You can only watch a handle once. Note that stdin/stdout/stderr can be
// the same underlying OS handle, so the caller can only watch one of them.
virtual WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) = 0;
static constexpr uint64_t kMaxDelay = std::numeric_limits<uint64_t>::max();
virtual void RunImpl() = 0;
// Get the value of a monotonic clock in nanoseconds.
virtual uint64_t GetMonotonicNowNS() const = 0;
// Used by WatchHandle to unregister a watch. Can be called from any thread
// without the lock held.
virtual void StopWatching(int id) = 0;
// Indicates there are tasks to process. Can be called from any thread
// and will be called without the lock held.
virtual void SetHasTasks() = 0;
// Processes one pending task, returning true if there was work to do, or
// false if there was nothing. The mutex_ must be held during the call. It
// will be unlocked during task processing, so the platform implementation
// that calls it must not assume state did not change across the call.
bool ProcessPendingTask() __TA_REQUIRES(mutex_);
// The platform implementation should check should_quit() after every
// task execution and exit if true.
bool should_quit() const { return should_quit_; }
// How much time we should wait before waking up again to process timers.
uint64_t DelayNS() const;
// Style guide says this should be private and we should have a protected
// getter, but that makes the thread annotations much more complicated.
std::mutex mutex_;
friend WatchHandle;
struct Task {
FileLineFunction file_line;
std::function<void()> task_fn;
std::deque<Task> task_queue_;
struct Timer {
Task task;
// Expiration time in nanoseconds. The time is absolute and compares to
// GetMonotonicNowNS.
uint64_t expiry;
std::vector<Timer> timers_;
static bool CompareTimers(const Timer& a, const Timer& b) {
return a.expiry >= b.expiry;
// Expiration time of the timer which will expire soonest. Returns an upper
// bound if there are no timers set.
uint64_t NextExpiryNS() const;
bool should_quit_ = false;
// Scopes watching a file handle. When the WatchHandle is destroyed, the
// MessageLoop will stop watching the handle. Must only be destroyed on the
// thread where the MessageLoop is.
// Invalid watch handles will have watching() return false.
class MessageLoop::WatchHandle {
// Constructs a WatchHandle not watching anything.
// Constructor used by MessageLoop to make one that watches something.
WatchHandle(MessageLoop* msg_loop, int id);
// Stops watching.
WatchHandle& operator=(WatchHandle&& other);
// Stops watching from the message loop.
// If the handle is not watching, this doesn't do anything.
void StopWatching();
bool watching() const { return id_ > 0; }
friend MessageLoop;
MessageLoop* msg_loop_ = nullptr;
int id_ = 0;
} // namespace debug_ipc