| // 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 |