blob: 2301d9e4e4fd835ce800244f7810f8d8338e09f4 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS 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 WINDOW_MANAGER_EVENT_LOOP_H_
#define WINDOW_MANAGER_EVENT_LOOP_H_
#include <map>
#include <set>
#include <tr1/memory>
#include <vector>
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "window_manager/callback.h"
namespace window_manager {
// EventLoop provides an interface for fetching X events and setting
// timeouts.
class EventLoop {
public:
EventLoop();
~EventLoop();
// Get the number of current-registered timeouts. Used for testing.
int num_timeouts() const { return timeout_fds_.size(); }
// Loop until Exit() is called, waiting for FDs to become readable or
// timeouts to fire.
void Run();
// Exit the loop the next time we're about to wait for FDs or timeouts.
void Exit() { exit_requested_ = true; }
// Start watching a file descriptor, invoking a callback when it becomes
// readable. Takes ownership of |cb|, which must be a repeatable
// (non-self-deleting) callback.
void AddFileDescriptor(int fd, Closure* cb);
// Stop watching a file descriptor.
void RemoveFileDescriptor(int fd);
// Register a callback that will always be invoked before we wait for
// changes to file descriptors. This is needed for e.g. Xlib, which can
// sidestep us and read its own FD at inopportune times to add events to
// its internal queue. For example, a callback can send a request to the
// X server that generates a response. While reading from the FD to find
// the response, Xlib will store any intervening events in its queue. We
// need to make sure that those events are handled before we wait on the
// (now non-readable) Xlib FD our next time through the loop.
void AddPrePollCallback(Closure* cb);
// Run |cb| in |initial_timeout_ms| milliseconds, returning a
// non-negative ID that can be used to refer the timeout later. A
// timeout of 0 will result in the callback being invoked in the next
// iteration of the event loop.
//
// Takes ownership of |cb|, which must be a repeatable
// (non-self-deleting) callback. If |recurring_timeout_ms| is non-zero,
// the timeout will be repeated every |recurring_timeout_ms| milliseconds
// after the initial run; otherwise it will only be run once. Note that
// even non-recurring timeouts must be removed using RemoveTimeout() for
// their resources to be freed.
int AddTimeout(Closure* cb, int initial_timeout_ms, int recurring_timeout_ms);
// Remove a timeout. It is safe to call this from within the callback of
// the timeout that's being removed. Crashes if the timeout doesn't exist.
void RemoveTimeout(int id);
// If the variable pointed at by |id| contains a timeout, remove the timeout
// and set the variable to -1.
void RemoveTimeoutIfSet(int* id) {
if (*id >= 0) {
RemoveTimeout(*id);
*id = -1;
}
}
// Run |cb| once immediately after control is returned to the event loop.
//
// Takes ownership of |cb|, which must be a repeatable
// (non-self-deleting) callback. Note that other not-yet-run tasks
// previously posted via PostTask() will be run before this one.
void PostTask(Closure* cb);
// Suspend a previously-registered timeout. Use ResetTimeout() to
// unsuspend it.
void SuspendTimeout(int fd);
// Modify a previously-registered timeout. The timeout arguments are
// interpreted in the same manner as in AddTimeout().
void ResetTimeout(int id, int initial_timeout_ms, int recurring_timeout_ms);
// Does the system that we're currently running on support the latest
// timerfd interface (the one with timerfd_create())? This was
// introduced in Linux 2.6.25 and glibc 2.8. This is static so that
// EventLoopTest can skip out early on older systems.
static bool IsTimerFdSupported();
// Run an already-registered timeout. This should only be used by
// testing code that wants to manually run a timeout's callback.
void RunTimeoutForTesting(int id);
private:
typedef std::vector<std::tr1::shared_ptr<Closure> > CallbackVector;
typedef std::map<int, std::tr1::shared_ptr<Closure> > FdCallbackMap;
// Run all callbacks from |posted_tasks_| and clear the vector.
// If the existing callbacks post additional tasks, they will be run as
// well.
void RunAllPostedTasks();
// Should we exit the loop?
bool exit_requested_;
// File descriptor that we're using for epoll_wait().
int epoll_fd_;
// Map from file descriptors to the corresponding callbacks.
FdCallbackMap callbacks_;
// Callbacks that get called before we poll. See AddPrePollCallback()
// for details.
CallbackVector pre_poll_callbacks_;
// Callbacks that have been posted via PostTask() to be run immediately
// after control is returned to the event loop, in the order in which
// they'll be run.
CallbackVector posted_tasks_;
// timerfd file descriptors that we've created (a subset of the keys in
// |callbacks_|.
std::set<int> timeout_fds_;
// Does the kernel support timerfd? If it doesn't, timeout-related calls
// are no-ops, and we'll crash if Run() is ever called.
bool timerfd_supported_;
// Callbacks that have been scheduled to run during the current poll
// cycle. If two timeouts A and B fire during the same cycle and A's
// callback happens to get executed first and removes B, we want avoid
// running B's callback afterwards. We store the set here so that
// RemoveFileDescriptor() can remove items from it.
std::set<std::tr1::shared_ptr<Closure> > callbacks_to_run_;
DISALLOW_COPY_AND_ASSIGN(EventLoop);
};
} // namespace window_manager
#endif // WINDOW_MANAGER_EVENT_LOOP_H_