blob: 01ceafb208d321ea45ee87679d11dd6dab06dacd [file] [log] [blame]
// Copyright 2017 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.
#ifndef GARNET_LIB_UI_GFX_UTIL_EVENT_TIMESTAMPER_H_
#define GARNET_LIB_UI_GFX_UTIL_EVENT_TIMESTAMPER_H_
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/wait.h>
#include <lib/fit/function.h>
#include <lib/zx/event.h>
#include "lib/fxl/logging.h"
#include "lib/fxl/macros.h"
namespace scenic_impl {
namespace gfx {
// EventTimestamper uses a background thread to watch for signals specified
// by EventTimestamper::Watch objects. When a signal is observed, a task is
// posted on the main loop to invoke a callback that is provided by the client.
//
// A program typically needs/wants a single EventTimestamper, which is shared
// by everyone who needs event-timestamps.
class EventTimestamper {
private:
class Waiter;
public:
using Callback = fit::function<void(zx_time_t timestamp)>;
EventTimestamper();
~EventTimestamper();
// When the Start() method is called, a Watch object begins to watch its event
// for the specified trigger signal. When the event occurs, the callback will
// be invoked, once. To watch for subsequent signals, Start() must be called
// again. It is illegal to call Start() again before the previous callback
// has been received. It is safe to destroy the Watch object even if Start()
// has been called; in this case, it is guaranteed that the callback will not
// be invoked.
class Watch {
public:
Watch();
Watch(EventTimestamper* ts, zx::event event, zx_status_t trigger,
Callback callback);
Watch(Watch&& rhs);
Watch& operator=(Watch&& rhs);
~Watch();
// Start watching for the event to be signaled. It is illegal to call
// Start() again before the callback has been invoked (it is safe to invoke
// Start() again from within the callback).
void Start();
// Return the watched event (or a null handle, if this Watch was moved).
const zx::event& event() const;
private:
Waiter* waiter_;
EventTimestamper* timestamper_;
FXL_DISALLOW_COPY_AND_ASSIGN(Watch);
};
private:
// Helper object that stores state corresponding to a single Watch object.
// Invariants:
// - |state_| only changes on the main thread.
// - instances of Waiter are only destroyed on the main thread.
class Waiter {
public:
enum class State { STARTED, STOPPED, ABANDONED };
Waiter(async_dispatcher_t* dispatcher, zx::event event, zx_status_t trigger,
Callback callback);
~Waiter();
void set_state(State state) { state_ = state; }
State state() const { return state_; }
async::WaitBase& wait() { return wait_; }
const zx::event& event() const { return event_; }
private:
void Handle(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal);
async_dispatcher_t* const dispatcher_;
zx::event event_;
Callback callback_;
State state_ = State::STOPPED;
async::WaitMethod<Waiter, &Waiter::Handle> wait_{this};
};
// Posts this EventTimestamper as a task on the background thread; when the
// task is run it will bump the thread priority.
// TODO(MZ-257): Avoid using a high-priority thread. This would probably
// entail not using a background thread at all, but instead relying on new
// kernel functionality to add a timestamp to a port message and/or a signaled
// event. When addressing this, be sure to stop inheriting from async::Task.
// Also see MG-940 and MG-1032.
void IncreaseBackgroundThreadPriority();
async_dispatcher_t* const main_dispatcher_;
async::Loop background_loop_;
async::TaskClosure task_;
#ifndef NDEBUG
size_t watch_count_ = 0;
#endif
FXL_DISALLOW_COPY_AND_ASSIGN(EventTimestamper);
};
} // namespace gfx
} // namespace scenic_impl
#endif // GARNET_LIB_UI_GFX_UTIL_EVENT_TIMESTAMPER_H_