| // 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/lib/ui/gfx/util/event_timestamper.h" |
| |
| #include <fuchsia/scheduler/cpp/fidl.h> |
| #include <lib/async/default.h> |
| #include <lib/async/time.h> |
| |
| namespace scenic_impl { |
| namespace gfx { |
| |
| EventTimestamper::EventTimestamper(sys::ComponentContext* component_context) |
| : main_dispatcher_(async_get_default_dispatcher()), |
| background_loop_(&kAsyncLoopConfigNoAttachToThread), |
| task_([component_context] { |
| auto profile_provider = |
| component_context->svc() |
| ->Connect<fuchsia::scheduler::ProfileProvider>(); |
| profile_provider->GetProfile( |
| 24 /* HIGH_PRIORITY in LK */, |
| "garnet/lib/ui/gfx/util/event_timestamper", |
| [](zx_status_t fidl_status, zx::profile profile) { |
| FXL_DCHECK(fidl_status == ZX_OK); |
| if (fidl_status == ZX_OK) { |
| zx_status_t status = |
| zx_object_set_profile(zx_thread_self(), profile.get(), 0); |
| FXL_DCHECK(status == ZX_OK); |
| } |
| }); |
| }) { |
| FXL_DCHECK(main_dispatcher_); |
| background_loop_.StartThread(); |
| |
| IncreaseBackgroundThreadPriority(); |
| } |
| |
| EventTimestamper::~EventTimestamper() { |
| background_loop_.Shutdown(); |
| #ifndef NDEBUG |
| FXL_CHECK(watch_count_ == 0); |
| #endif |
| } |
| |
| void EventTimestamper::IncreaseBackgroundThreadPriority() { |
| task_.Post(background_loop_.dispatcher()); |
| } |
| |
| EventTimestamper::Watch::Watch() : waiter_(nullptr), timestamper_(nullptr) {} |
| |
| EventTimestamper::Watch::Watch(EventTimestamper* ts, zx::event event, |
| zx_status_t trigger, Callback callback) |
| : waiter_(new EventTimestamper::Waiter(ts->main_dispatcher_, |
| std::move(event), trigger, |
| std::move(callback))), |
| timestamper_(ts) { |
| FXL_DCHECK(timestamper_); |
| #ifndef NDEBUG |
| ++timestamper_->watch_count_; |
| #endif |
| } |
| |
| EventTimestamper::Watch::Watch(Watch&& rhs) |
| : waiter_(rhs.waiter_), timestamper_(rhs.timestamper_) { |
| rhs.waiter_ = nullptr; |
| rhs.timestamper_ = nullptr; |
| } |
| |
| EventTimestamper::Watch& EventTimestamper::Watch::operator=( |
| EventTimestamper::Watch&& rhs) { |
| FXL_DCHECK(!waiter_ && !timestamper_); |
| waiter_ = rhs.waiter_; |
| timestamper_ = rhs.timestamper_; |
| rhs.waiter_ = nullptr; |
| rhs.timestamper_ = nullptr; |
| return *this; |
| } |
| |
| EventTimestamper::Watch::~Watch() { |
| if (!waiter_) { |
| // Was moved. |
| return; |
| } |
| #ifndef NDEBUG |
| --timestamper_->watch_count_; |
| #endif |
| |
| switch (waiter_->state()) { |
| case Waiter::State::STOPPED: |
| delete waiter_; |
| break; |
| case Waiter::State::STARTED: |
| waiter_->set_state(Waiter::State::ABANDONED); |
| if (ZX_OK == waiter_->wait().Cancel()) { |
| // We successfully cancelled the async::Wait, so we can/must delete the |
| // Waiter ourselves. Otherwise we must not delete it, because it will |
| // delete itself when it sees that it has been abandoned. |
| delete waiter_; |
| } |
| break; |
| case Waiter::State::ABANDONED: |
| FXL_DCHECK(false) << "internal error."; |
| break; |
| } |
| } |
| |
| void EventTimestamper::Watch::Start() { |
| FXL_DCHECK(waiter_) << "invalid Watch (was it std::move()d?)."; |
| FXL_DCHECK(waiter_->state() == Waiter::State::STOPPED) |
| << "illegal to call Start() again before callback has been received."; |
| waiter_->set_state(Waiter::State::STARTED); |
| waiter_->wait().Begin(timestamper_->background_loop_.dispatcher()); |
| } |
| |
| // Return the watched event (or a null handle, if this Watch was moved). |
| const zx::event& EventTimestamper::Watch::event() const { |
| static const zx::event null_handle; |
| return waiter_ ? waiter_->event() : null_handle; |
| } |
| |
| bool EventTimestamper::Watch::IsWatching() const { |
| FXL_DCHECK(waiter_) << "invalid Watch (was it std::move()d?)."; |
| return waiter_->state() == Waiter::State::STARTED; |
| } |
| |
| EventTimestamper::Waiter::Waiter(async_dispatcher_t* dispatcher, |
| zx::event event, zx_status_t trigger, |
| Callback callback) |
| : dispatcher_(dispatcher), |
| event_(std::move(event)), |
| callback_(std::move(callback)), |
| wait_(this, event_.get(), trigger) {} |
| |
| EventTimestamper::Waiter::~Waiter() { |
| FXL_DCHECK(state_ == State::STOPPED || state_ == State::ABANDONED); |
| } |
| |
| void EventTimestamper::Waiter::Handle(async_dispatcher_t* dispatcher, |
| async::WaitBase* wait, zx_status_t status, |
| const zx_packet_signal_t* signal) { |
| FXL_DCHECK(dispatcher); |
| zx_time_t now = async_now(dispatcher); |
| async::PostTask(dispatcher_, [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); |
| }); |
| } |
| |
| } // namespace gfx |
| } // namespace scenic_impl |