Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1244)

Unified Diff: content/renderer/input/main_thread_event_queue.cc

Issue 2765583002: Teach main thread event queue about closures. (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/input/main_thread_event_queue.cc
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc
index 39dd5e56c1ba7750f63e2ae8a4acb73dc5a38ea1..225c76b4411552b5d10a73090c8e9f4ba28b03de 100644
--- a/content/renderer/input/main_thread_event_queue.cc
+++ b/content/renderer/input/main_thread_event_queue.cc
@@ -14,24 +14,200 @@ namespace content {
namespace {
-// Time interval at which touchmove events will be skipped during rAF signal.
-const base::TimeDelta kAsyncTouchMoveInterval =
- base::TimeDelta::FromMilliseconds(200);
-
const size_t kTenSeconds = 10 * 1000 * 1000;
-bool IsContinuousEvent(const std::unique_ptr<EventWithDispatchType>& event) {
- switch (event->event().type()) {
- case blink::WebInputEvent::MouseMove:
- case blink::WebInputEvent::MouseWheel:
- case blink::WebInputEvent::TouchMove:
- return true;
- default:
+class QueuedClosure : public QueuedItem {
+ public:
+ QueuedClosure(const base::Closure& closure) : closure_(closure) {}
+
+ ~QueuedClosure() override {}
+
+ bool CanCoalesceWith(const QueuedItem&) override { return false; }
+
+ void CoalesceWith(const QueuedItem&) override { NOTREACHED(); }
+
+ bool IsSameEventClass(const QueuedItem&) const override {
+ // Say they are the same event class as an optimization to
+ // because coalescing will always fail.
+ return true;
+ }
+
+ bool IsEvent() const override { return false; }
mustaq 2017/03/24 15:31:18 I think it would be unwise to consider QueuedClosu
dtapuska 2017/03/24 20:16:31 How about a QueuedClosure and a QueuedWebInputEven
+
+ void Dispatch(int routing_id, MainThreadEventQueueClient*) override {
+ closure_.Run();
+ }
+
+ void EventHandled(int routing_id,
+ blink::scheduler::RendererScheduler* renderer_scheduler,
+ MainThreadEventQueueClient* client,
+ blink::WebInputEvent::Type type,
+ blink::WebInputEventResult result,
+ InputEventAckState ack_result) override {}
+
+ private:
+ base::Closure closure_;
+};
+
+class EventWithDispatchType : public ScopedWebInputEventWithLatencyInfo,
+ public QueuedItem {
+ public:
+ EventWithDispatchType(ui::WebScopedInputEvent event,
+ const ui::LatencyInfo& latency,
+ InputEventDispatchType dispatch_type,
+ bool originally_cancelable)
+ : ScopedWebInputEventWithLatencyInfo(std::move(event), latency),
+ dispatch_type_(dispatch_type),
+ non_blocking_coalesced_count_(0),
+ creation_timestamp_(base::TimeTicks::Now()),
+ last_coalesced_timestamp_(creation_timestamp_),
+ originally_cancelable_(originally_cancelable) {}
+
+ ~EventWithDispatchType() override {}
+
+ bool CanCoalesceWith(const QueuedItem& other_item) override {
+ if (!other_item.IsEvent())
return false;
+ const EventWithDispatchType& other_event =
+ static_cast<const EventWithDispatchType&>(other_item);
+ return ScopedWebInputEventWithLatencyInfo::CanCoalesceWith(other_event);
+ }
+
+ void CoalesceWith(const QueuedItem& other_item) override {
+ const EventWithDispatchType& other_event =
+ static_cast<const EventWithDispatchType&>(other_item);
+ // If this event was blocking push the event id to the blocking
+ // list before updating the dispatch_type of this event.
+ if (dispatch_type_ == DISPATCH_TYPE_BLOCKING) {
+ blocking_coalesced_event_ids_.push_back(
+ ui::WebInputEventTraits::GetUniqueTouchEventId(event()));
+ } else {
+ non_blocking_coalesced_count_++;
+ }
+ ScopedWebInputEventWithLatencyInfo::CoalesceWith(other_event);
+ dispatch_type_ = other_event.dispatch_type_;
+ last_coalesced_timestamp_ = base::TimeTicks::Now();
+ originally_cancelable_ = other_event.originally_cancelable_;
+ }
+
+ bool IsSameEventClass(const QueuedItem& other_item) const override {
+ if (!other_item.IsEvent())
+ return false;
+ const EventWithDispatchType& other_event =
+ static_cast<const EventWithDispatchType&>(other_item);
+ return event().isSameEventClass(other_event.event());
+ }
+
+ bool IsEvent() const override { return true; }
+
+ void Dispatch(int routing_id, MainThreadEventQueueClient* client) override {
+ // Report the coalesced count only for continuous events; otherwise
+ // the zero value would be dominated by non-continuous events.
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (IsContinuousEvent()) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Event.MainThreadEventQueue.Continuous.QueueingTime",
+ (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50);
+
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Event.MainThreadEventQueue.Continuous.FreshnessTime",
+ (now - lastCoalescedTimestamp()).InMicroseconds(), 1, kTenSeconds,
+ 50);
+
+ UMA_HISTOGRAM_COUNTS_1000("Event.MainThreadEventQueue.CoalescedCount",
+ coalescedCount());
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Event.MainThreadEventQueue.NonContinuous.QueueingTime",
+ (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50);
+ }
+
+ InputEventDispatchType dispatch_type = dispatchType();
+ if (!blockingCoalescedEventIds().empty()) {
+ switch (dispatch_type) {
+ case DISPATCH_TYPE_BLOCKING:
+ dispatch_type = DISPATCH_TYPE_BLOCKING_NOTIFY_MAIN;
+ break;
+ case DISPATCH_TYPE_NON_BLOCKING:
+ dispatch_type = DISPATCH_TYPE_NON_BLOCKING_NOTIFY_MAIN;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ client->HandleEventOnMainThread(routing_id, &coalesced_event(),
+ latencyInfo(), dispatch_type);
+ }
+
+ void EventHandled(int routing_id,
+ blink::scheduler::RendererScheduler* renderer_scheduler,
+ MainThreadEventQueueClient* client,
+ blink::WebInputEvent::Type type,
+ blink::WebInputEventResult result,
+ InputEventAckState ack_result) override {
+ for (const auto id : blockingCoalescedEventIds()) {
+ client->SendInputEventAck(routing_id, type, ack_result, id);
+ if (renderer_scheduler) {
+ renderer_scheduler->DidHandleInputEventOnMainThread(event(), result);
+ }
+ }
+ }
+
+ bool originallyCancelable() const { return originally_cancelable_; }
+
+ private:
+ const std::deque<uint32_t>& blockingCoalescedEventIds() const {
+ return blocking_coalesced_event_ids_;
+ }
+ InputEventDispatchType dispatchType() const { return dispatch_type_; }
+ base::TimeTicks creationTimestamp() const { return creation_timestamp_; }
+ base::TimeTicks lastCoalescedTimestamp() const {
+ return last_coalesced_timestamp_;
+ }
+
+ size_t coalescedCount() const {
+ return non_blocking_coalesced_count_ + blocking_coalesced_event_ids_.size();
}
-}
-bool IsAsyncTouchMove(const std::unique_ptr<EventWithDispatchType>& event) {
+ bool IsContinuousEvent() const {
+ switch (event().type()) {
+ case blink::WebInputEvent::MouseMove:
+ case blink::WebInputEvent::MouseWheel:
+ case blink::WebInputEvent::TouchMove:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ InputEventDispatchType dispatch_type_;
+
+ // Contains the unique touch event ids to be acked. If
+ // the events are not TouchEvents the values will be 0. More importantly for
+ // those cases the deque ends up containing how many additional ACKs
+ // need to be sent.
+ std::deque<uint32_t> blocking_coalesced_event_ids_;
+ // Contains the number of non-blocking events coalesced.
+ size_t non_blocking_coalesced_count_;
+ base::TimeTicks creation_timestamp_;
+ base::TimeTicks last_coalesced_timestamp_;
+
+ // Whether the received event was originally cancelable or not. The compositor
+ // input handler can change the event based on presence of event handlers so
+ // this is the state at which the renderer received the event from the
+ // browser.
+ bool originally_cancelable_;
+};
+
+// Time interval at which touchmove events will be skipped during rAF signal.
+const base::TimeDelta kAsyncTouchMoveInterval =
+ base::TimeDelta::FromMilliseconds(200);
+
+bool IsAsyncTouchMove(const std::unique_ptr<QueuedItem>& queued_item) {
+ if (!queued_item->IsEvent())
+ return false;
+ const EventWithDispatchType* event =
+ static_cast<const EventWithDispatchType*>(queued_item.get());
if (event->event().type() != blink::WebInputEvent::TouchMove)
return false;
const blink::WebTouchEvent& touch_event =
@@ -41,37 +217,8 @@ bool IsAsyncTouchMove(const std::unique_ptr<EventWithDispatchType>& event) {
} // namespace
-EventWithDispatchType::EventWithDispatchType(
- ui::WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
- InputEventDispatchType dispatch_type,
- bool originally_cancelable)
- : ScopedWebInputEventWithLatencyInfo(std::move(event), latency),
- dispatch_type_(dispatch_type),
- non_blocking_coalesced_count_(0),
- creation_timestamp_(base::TimeTicks::Now()),
- last_coalesced_timestamp_(creation_timestamp_),
- originally_cancelable_(originally_cancelable) {}
-
-EventWithDispatchType::~EventWithDispatchType() {}
-
-void EventWithDispatchType::CoalesceWith(const EventWithDispatchType& other) {
- // If this event was blocking push the event id to the blocking
- // list before updating the dispatch_type of this event.
- if (dispatch_type_ == DISPATCH_TYPE_BLOCKING) {
- blocking_coalesced_event_ids_.push_back(
- ui::WebInputEventTraits::GetUniqueTouchEventId(event()));
- } else {
- non_blocking_coalesced_count_++;
- }
- ScopedWebInputEventWithLatencyInfo::CoalesceWith(other);
- dispatch_type_ = other.dispatch_type_;
- last_coalesced_timestamp_ = base::TimeTicks::Now();
- originally_cancelable_ = other.originally_cancelable_;
-}
-
MainThreadEventQueue::SharedState::SharedState()
- : sent_main_frame_request_(false) {}
+ : sent_main_frame_request_(false), sent_post_task_(false) {}
MainThreadEventQueue::SharedState::~SharedState() {}
@@ -206,48 +353,23 @@ bool MainThreadEventQueue::HandleEvent(
return non_blocking;
}
-void MainThreadEventQueue::DispatchInFlightEvent() {
- if (in_flight_event_) {
- // Report the coalesced count only for continuous events; otherwise
- // the zero value would be dominated by non-continuous events.
- base::TimeTicks now = base::TimeTicks::Now();
- if (IsContinuousEvent(in_flight_event_)) {
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Event.MainThreadEventQueue.Continuous.QueueingTime",
- (now - in_flight_event_->creationTimestamp()).InMicroseconds(), 1,
- kTenSeconds, 50);
-
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Event.MainThreadEventQueue.Continuous.FreshnessTime",
- (now - in_flight_event_->lastCoalescedTimestamp()).InMicroseconds(),
- 1, kTenSeconds, 50);
+void MainThreadEventQueue::QueueClosure(const base::Closure& closure) {
+ bool needs_post_task = false;
+ std::unique_ptr<QueuedClosure> item(new QueuedClosure(closure));
+ {
+ base::AutoLock lock(shared_state_lock_);
+ shared_state_.events_.Queue(std::move(item));
+ needs_post_task = !shared_state_.sent_post_task_;
+ shared_state_.sent_post_task_ = true;
+ }
- UMA_HISTOGRAM_COUNTS_1000("Event.MainThreadEventQueue.CoalescedCount",
- in_flight_event_->coalescedCount());
- } else {
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Event.MainThreadEventQueue.NonContinuous.QueueingTime",
- (now - in_flight_event_->creationTimestamp()).InMicroseconds(), 1,
- kTenSeconds, 50);
- }
+ if (needs_post_task)
+ SendEventNotificationToMainThread();
+}
- InputEventDispatchType dispatch_type = in_flight_event_->dispatchType();
- if (!in_flight_event_->blockingCoalescedEventIds().empty()) {
- switch (dispatch_type) {
- case DISPATCH_TYPE_BLOCKING:
- dispatch_type = DISPATCH_TYPE_BLOCKING_NOTIFY_MAIN;
- break;
- case DISPATCH_TYPE_NON_BLOCKING:
- dispatch_type = DISPATCH_TYPE_NON_BLOCKING_NOTIFY_MAIN;
- break;
- default:
- NOTREACHED();
- }
- }
- client_->HandleEventOnMainThread(
- routing_id_, &in_flight_event_->coalesced_event(),
- in_flight_event_->latencyInfo(), dispatch_type);
- }
+void MainThreadEventQueue::DispatchInFlightEvent() {
+ if (in_flight_event_)
+ in_flight_event_->Dispatch(routing_id_, client_);
in_flight_event_.reset();
}
@@ -260,7 +382,7 @@ void MainThreadEventQueue::PossiblyScheduleMainFrame() {
base::AutoLock lock(shared_state_lock_);
if (!shared_state_.sent_main_frame_request_ &&
!shared_state_.events_.empty() &&
- IsRafAlignedEvent(shared_state_.events_.front()->event())) {
+ IsRafAlignedEvent(shared_state_.events_.front())) {
needs_main_frame = !shared_state_.sent_main_frame_request_;
shared_state_.sent_main_frame_request_ = false;
}
@@ -269,15 +391,28 @@ void MainThreadEventQueue::PossiblyScheduleMainFrame() {
client_->NeedsMainFrame(routing_id_);
}
-void MainThreadEventQueue::DispatchSingleEvent() {
+void MainThreadEventQueue::DispatchEvents() {
+ std::deque<std::unique_ptr<QueuedItem>> events_to_process;
{
base::AutoLock lock(shared_state_lock_);
- if (shared_state_.events_.empty())
- return;
+ shared_state_.sent_post_task_ = false;
- in_flight_event_ = shared_state_.events_.Pop();
+ shared_state_.events_.swap(&events_to_process);
+
+ // Now take any raf aligned events that are at the tail of the queue
+ // and put them back.
+ while (!events_to_process.empty()) {
+ if (!IsRafAlignedEvent(events_to_process.back()))
+ break;
+ shared_state_.events_.emplace_front(std::move(events_to_process.back()));
+ events_to_process.pop_back();
+ }
+ }
+ while (!events_to_process.empty()) {
+ in_flight_event_ = std::move(events_to_process.front());
+ events_to_process.pop_front();
+ DispatchInFlightEvent();
}
- DispatchInFlightEvent();
PossiblyScheduleMainFrame();
}
@@ -285,13 +420,8 @@ void MainThreadEventQueue::EventHandled(blink::WebInputEvent::Type type,
blink::WebInputEventResult result,
InputEventAckState ack_result) {
if (in_flight_event_) {
- for (const auto id : in_flight_event_->blockingCoalescedEventIds()) {
- client_->SendInputEventAck(routing_id_, type, ack_result, id);
- if (renderer_scheduler_) {
- renderer_scheduler_->DidHandleInputEventOnMainThread(
- in_flight_event_->event(), result);
- }
- }
+ in_flight_event_->EventHandled(routing_id_, renderer_scheduler_, client_,
+ type, result, ack_result);
}
}
@@ -299,24 +429,23 @@ void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) {
if (IsRafAlignedInputDisabled())
return;
- std::deque<std::unique_ptr<EventWithDispatchType>> events_to_process;
+ std::deque<std::unique_ptr<QueuedItem>> events_to_process;
{
base::AutoLock lock(shared_state_lock_);
shared_state_.sent_main_frame_request_ = false;
while (!shared_state_.events_.empty()) {
- if (!IsRafAlignedEvent(shared_state_.events_.front()->event()))
- break;
-
- // Throttle touchmoves that are async.
- if (handle_raf_aligned_touch_input_ &&
- IsAsyncTouchMove(shared_state_.events_.front())) {
- if (shared_state_.events_.size() == 1 &&
- frame_time < shared_state_.last_async_touch_move_timestamp_ +
- kAsyncTouchMoveInterval) {
- break;
+ if (IsRafAlignedEvent(shared_state_.events_.front())) {
+ // Throttle touchmoves that are async.
+ if (handle_raf_aligned_touch_input_ &&
+ IsAsyncTouchMove(shared_state_.events_.front())) {
+ if (shared_state_.events_.size() == 1 &&
+ frame_time < shared_state_.last_async_touch_move_timestamp_ +
+ kAsyncTouchMoveInterval) {
+ break;
+ }
+ shared_state_.last_async_touch_move_timestamp_ = frame_time;
}
- shared_state_.last_async_touch_move_timestamp_ = frame_time;
}
events_to_process.emplace_back(shared_state_.events_.Pop());
}
@@ -332,14 +461,13 @@ void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) {
void MainThreadEventQueue::SendEventNotificationToMainThread() {
mustaq 2017/03/24 15:31:18 Since there is really no notification here, may be
dtapuska 2017/03/24 20:16:33 That would mean that we are somehow transferring t
main_task_runner_->PostTask(
- FROM_HERE, base::Bind(&MainThreadEventQueue::DispatchSingleEvent, this));
+ FROM_HERE, base::Bind(&MainThreadEventQueue::DispatchEvents, this));
}
-void MainThreadEventQueue::QueueEvent(
- std::unique_ptr<EventWithDispatchType> event) {
- bool is_raf_aligned = IsRafAlignedEvent(event->event());
- size_t send_notification_count = 0;
+void MainThreadEventQueue::QueueEvent(std::unique_ptr<QueuedItem> event) {
+ bool is_raf_aligned = IsRafAlignedEvent(event);
bool needs_main_frame = false;
+ bool needs_post_task = false;
{
mustaq 2017/03/24 15:31:18 I will take a look at this block again.
dtapuska 2017/03/24 20:16:31 Acknowledged.
base::AutoLock lock(shared_state_lock_);
size_t size_before = shared_state_.events_.size();
@@ -348,24 +476,15 @@ void MainThreadEventQueue::QueueEvent(
bool was_raf_aligned = false;
if (size_before > 0) {
was_raf_aligned =
- IsRafAlignedEvent(shared_state_.events_.at(size_before - 1)->event());
+ IsRafAlignedEvent(shared_state_.events_.at(size_before - 1));
}
shared_state_.events_.Queue(std::move(event));
size_t size_after = shared_state_.events_.size();
if (size_before != size_after) {
- if (IsRafAlignedInputDisabled()) {
- send_notification_count = 1;
- } else if (!is_raf_aligned) {
- send_notification_count = 1;
- // If we had just enqueued a non-rAF input event we will send a series
- // of normal post messages to ensure they are all handled right away.
- for (size_t pos = size_after - 1; pos >= 1; --pos) {
- if (IsRafAlignedEvent(shared_state_.events_.at(pos - 1)->event()))
- send_notification_count++;
- else
- break;
- }
+ if (IsRafAlignedInputDisabled() || !is_raf_aligned) {
+ needs_post_task = !shared_state_.sent_post_task_;
+ shared_state_.sent_post_task_ = true;
} else {
needs_main_frame = !shared_state_.sent_main_frame_request_;
shared_state_.sent_main_frame_request_ = true;
@@ -375,25 +494,31 @@ void MainThreadEventQueue::QueueEvent(
// the rAF alignment of the event may have and we need to schedule
// a notification.
bool is_coalesced_raf_aligned =
- IsRafAlignedEvent(shared_state_.events_.at(size_before - 1)->event());
- if (was_raf_aligned != is_coalesced_raf_aligned)
- send_notification_count = 1;
+ IsRafAlignedEvent(shared_state_.events_.at(size_before - 1));
+ if (was_raf_aligned != is_coalesced_raf_aligned) {
+ needs_post_task = !shared_state_.sent_post_task_;
+ shared_state_.sent_post_task_ = true;
+ }
}
}
- for (size_t i = 0; i < send_notification_count; ++i)
+ if (needs_post_task)
SendEventNotificationToMainThread();
if (needs_main_frame)
client_->NeedsMainFrame(routing_id_);
}
-bool MainThreadEventQueue::IsRafAlignedInputDisabled() {
+bool MainThreadEventQueue::IsRafAlignedInputDisabled() const {
return !handle_raf_aligned_mouse_input_ && !handle_raf_aligned_touch_input_;
}
bool MainThreadEventQueue::IsRafAlignedEvent(
- const blink::WebInputEvent& event) {
- switch (event.type()) {
+ const std::unique_ptr<QueuedItem>& item) const {
+ if (!item->IsEvent())
+ return false;
+ const EventWithDispatchType* event =
+ static_cast<const EventWithDispatchType*>(item.get());
+ switch (event->event().type()) {
case blink::WebInputEvent::MouseMove:
case blink::WebInputEvent::MouseWheel:
return handle_raf_aligned_mouse_input_;

Powered by Google App Engine
This is Rietveld 408576698