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