blob: 4c57013ddcf5a36b41a6c56cf50de247d377e279 [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.
#include "garnet/bin/ui/scene_manager/util/event_timestamper.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fsl/threading/create_thread.h"
namespace scene_manager {
EventTimestamper::EventTimestamper()
: main_loop_(fsl::MessageLoop::GetCurrent()), task_(0u) {
FXL_DCHECK(main_loop_);
background_loop_.StartThread();
task_.set_handler([](async_t*, zx_status_t) {
zx_thread_set_priority(24 /* HIGH_PRIORITY in LK */);
return ASYNC_TASK_FINISHED;
});
IncreaseBackgroundThreadPriority();
}
EventTimestamper::~EventTimestamper() {
background_loop_.Shutdown();
#ifndef NDEBUG
FXL_CHECK(watch_count_ == 0);
#endif
}
void EventTimestamper::IncreaseBackgroundThreadPriority() {
task_.Post(background_loop_.async());
}
EventTimestamper::Watch::Watch() : wait_(nullptr), timestamper_(nullptr) {}
EventTimestamper::Watch::Watch(EventTimestamper* ts,
zx::event event,
zx_status_t trigger,
Callback callback)
: wait_(new EventTimestamper::Wait(ts->main_loop_->task_runner(),
std::move(event),
trigger,
std::move(callback))),
timestamper_(ts) {
FXL_DCHECK(timestamper_);
#ifndef NDEBUG
++timestamper_->watch_count_;
#endif
}
EventTimestamper::Watch::Watch(Watch&& other)
: wait_(other.wait_), timestamper_(other.timestamper_) {
other.wait_ = nullptr;
other.timestamper_ = nullptr;
}
EventTimestamper::Watch::~Watch() {
if (!wait_) {
// Was moved.
return;
}
#ifndef NDEBUG
--timestamper_->watch_count_;
#endif
switch (wait_->state()) {
case Wait::State::STOPPED:
delete wait_;
break;
case Wait::State::STARTED:
if (ZX_OK ==
wait_->wait().Cancel(timestamper_->background_loop_.async())) {
delete wait_;
} else {
wait_->set_state(Wait::State::ABANDONED);
}
break;
case Wait::State::ABANDONED:
FXL_DCHECK(false) << "internal error.";
break;
}
}
void EventTimestamper::Watch::Start() {
FXL_DCHECK(wait_) << "invalid Watch (was it std::move()d?).";
FXL_DCHECK(wait_->state() == Wait::State::STOPPED)
<< "illegal to call Start() again before callback has been received.";
wait_->set_state(Wait::State::STARTED);
wait_->wait().Begin(timestamper_->background_loop_.async());
}
EventTimestamper::Wait::Wait(const fxl::RefPtr<fxl::TaskRunner>& task_runner,
zx::event event,
zx_status_t trigger,
Callback callback)
: task_runner_(task_runner),
event_(std::move(event)),
callback_(std::move(callback)),
wait_(event_.get(), trigger) {
wait_.set_handler(fbl::BindMember(this, &EventTimestamper::Wait::Handle));
}
EventTimestamper::Wait::~Wait() {
FXL_DCHECK(state_ == State::STOPPED || state_ == State::ABANDONED);
}
async_wait_result_t EventTimestamper::Wait::Handle(
async_t* async,
zx_status_t status,
const zx_packet_signal_t* signal) {
zx_time_t now = zx_time_get(ZX_CLOCK_MONOTONIC);
task_runner_->PostTask([now, this] {
if (state_ == State::ABANDONED) {
// The EventTimestamper::Watch that owned us was destroyed; we must
// immediately destroy ourself or our memory will be leaked.
delete this;
return;
}
FXL_DCHECK(state_ == State::STARTED) << "internal error.";
state_ = State::STOPPED;
callback_(now);
});
return ASYNC_WAIT_FINISHED;
}
} // namespace scene_manager