blob: 91f77bc3777bb5558c35f8770920947bb72f340f [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 <map>
#include <mutex>
#include "garnet/lib/debug_ipc/helper/file_line_function.h"
#include "lib/fxl/macros.h"
#if defined(__Fuchsia__)
#include <zircon/compiler.h>
#else
// The macros for thread annotations aren't set up for non-Fuchsia builds.
#define __TA_REQUIRES(arg)
#endif
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 {
public:
enum class WatchMode { kRead, kWrite, kReadWrite };
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).
MessageLoop();
virtual ~MessageLoop();
// These must be called on the same thread as Run().
virtual void Init();
virtual void Cleanup();
// 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);
// Exits the message loop immediately, not running pending functions. This
// must be called only on the MessageLoop thread.
void QuitNow();
// 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;
bool debug_mode() const { return debug_mode_; }
void set_debug_mode(bool active) { debug_mode_ = active; }
protected:
virtual void RunImpl() = 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_; }
// 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_;
private:
friend WatchHandle;
struct Task {
FileLineFunction file_line;
std::function<void()> task_fn;
};
std::deque<Task> task_queue_;
bool should_quit_ = false;
// Whether the message loop should output debug information.
bool debug_mode_ = false;
FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
// 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 {
public:
// Constructs a WatchHandle not watching anything.
WatchHandle();
// Constructor used by MessageLoop to make one that watches something.
WatchHandle(MessageLoop* msg_loop, int id);
WatchHandle(WatchHandle&&);
// Stops watching.
~WatchHandle();
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; }
private:
friend MessageLoop;
MessageLoop* msg_loop_ = nullptr;
int id_ = 0;
};
} // namespace debug_ipc