// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_ #define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_ #include <map> #include <queue> #include "mojo/public/cpp/bindings/callback.h" #include "mojo/public/cpp/system/core.h" namespace mojo { class RunLoopHandler; class RunLoop { public: RunLoop(); ~RunLoop(); // Sets up state needed for RunLoop. This must be invoked before creating a // RunLoop. static void SetUp(); // Cleans state created by Setup(). static void TearDown(); // Returns the RunLoop for the current thread. Returns NULL if not yet // created. static RunLoop* current(); // Registers a RunLoopHandler for the specified handle. Only one handler can // be registered for a specified handle. void AddHandler(RunLoopHandler* handler, const Handle& handle, MojoHandleSignals handle_signals, MojoDeadline deadline); void RemoveHandler(const Handle& handle); bool HasHandler(const Handle& handle) const; // Runs the loop servicing handles and tasks as they are ready. This returns // when Quit() is invoked, or there are no more handles nor tasks. void Run(); // Runs the loop servicing any handles and tasks that are ready. Does not wait // for handles or tasks to become ready before returning. Returns early if // Quit() is invoked. void RunUntilIdle(); void Quit(); // Adds a task to be performed after delay has elapsed. Must be posted to the // current thread's RunLoop. void PostDelayedTask(const Closure& task, MojoTimeTicks delay); private: struct RunState; struct WaitState; // Contains the data needed to track a request to AddHandler(). struct HandlerData { HandlerData() : handler(NULL), handle_signals(MOJO_HANDLE_SIGNAL_NONE), deadline(0), id(0) {} RunLoopHandler* handler; MojoHandleSignals handle_signals; MojoTimeTicks deadline; // See description of |RunLoop::next_handler_id_| for details. int id; }; typedef std::map<Handle, HandlerData> HandleToHandlerData; // Used for NotifyHandlers to specify whether HandlerData's |deadline| // should be checked prior to notifying. enum CheckDeadline { CHECK_DEADLINE, IGNORE_DEADLINE }; // Mode of operation of the run loop. enum RunMode { UNTIL_EMPTY, UNTIL_IDLE }; // Runs the loop servicing any handles and tasks that are ready. If // |run_mode| is |UNTIL_IDLE|, does not wait for handles or tasks to become // ready before returning. Returns early if Quit() is invoked. void RunInternal(RunMode run_mode); // Do one unit of delayed work, if eligible. Returns true is a task was run. bool DoDelayedWork(); // Waits for a handle to be ready or until the next task must be run. Returns // after servicing at least one handle (or there are no more handles) unless // a task must be run or |non_blocking| is true, in which case it will also // return if no task is registered and servicing at least one handle would // require blocking. Returns true if a RunLoopHandler was notified. bool Wait(bool non_blocking); // Notifies handlers of |error|. If |check| == CHECK_DEADLINE, this will // only notify handlers whose deadline has expired and skips the rest. // Returns true if a RunLoopHandler was notified. bool NotifyHandlers(MojoResult error, CheckDeadline check); // Removes the first invalid handle. This is called if MojoWaitMany() finds an // invalid handle. Returns true if a RunLoopHandler was notified. bool RemoveFirstInvalidHandle(const WaitState& wait_state); // Returns the state needed to pass to WaitMany(). WaitState GetWaitState(bool non_blocking) const; HandleToHandlerData handler_data_; // If non-NULL we're running (inside Run()). Member references a value on the // stack. RunState* run_state_; // An ever increasing value assigned to each HandlerData::id. Used to detect // uniqueness while notifying. That is, while notifying expired timers we copy // |handler_data_| and only notify handlers whose id match. If the id does not // match it means the handler was removed then added so that we shouldn't // notify it. int next_handler_id_; struct PendingTask { PendingTask(const Closure& task, MojoTimeTicks runtime, uint64_t sequence_number); ~PendingTask(); bool operator<(const PendingTask& other) const; Closure task; MojoTimeTicks run_time; uint64_t sequence_number; }; // An ever increasing sequence number attached to each pending task in order // to preserve relative order of tasks posted at the 'same' time. uint64_t next_sequence_number_; typedef std::priority_queue<PendingTask> DelayedTaskQueue; DelayedTaskQueue delayed_tasks_; MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoop); }; } // namespace mojo #endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_