Files
react-native/ReactCommon/react/renderer/core/EventQueue.cpp
T
Samuel Susla 0ee8e292a4 Fix for ScrollView race condition between C++ state update and onScroll
Summary:
Changelog: [internal]

There is a possibility of race between updating scrollview's state and virtualised list asking for layout of individual cells.
To make sure the race doesn't happen, state must be updated before dispatching onScroll event.

Android has implemented a different mechanism to tackle this issue in D28558380 (https://github.com/facebook/react-native/commit/b161241db2ef74d2e4bff36d4972f5f0312dcc44).

Reviewed By: JoshuaGross

Differential Revision: D28642737

fbshipit-source-id: 33874beac69fc5a66eeb7f459fd89cd0b00dafcf
2021-05-26 03:32:03 -07:00

120 lines
2.9 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "EventQueue.h"
#include "EventEmitter.h"
#include "ShadowNodeFamily.h"
namespace facebook {
namespace react {
EventQueue::EventQueue(
EventQueueProcessor eventProcessor,
std::unique_ptr<EventBeat> eventBeat)
: eventProcessor_(std::move(eventProcessor)),
eventBeat_(std::move(eventBeat)) {
eventBeat_->setBeatCallback(
std::bind(&EventQueue::onBeat, this, std::placeholders::_1));
}
void EventQueue::enqueueEvent(RawEvent &&rawEvent) const {
{
std::lock_guard<std::mutex> lock(queueMutex_);
eventQueue_.push_back(std::move(rawEvent));
}
onEnqueue();
}
void EventQueue::enqueueUniqueEvent(RawEvent &&rawEvent) const {
{
std::lock_guard<std::mutex> lock(queueMutex_);
auto repeatedEvent = eventQueue_.rend();
for (auto it = eventQueue_.rbegin(); it != eventQueue_.rend(); ++it) {
if (it->type == rawEvent.type &&
it->eventTarget == rawEvent.eventTarget) {
repeatedEvent = it;
break;
} else if (it->eventTarget == rawEvent.eventTarget) {
// It is necessary to maintain order of different event types
// for the same target. If the same target has event types A1, B1
// in the event queue and event A2 occurs. A1 has to stay in the
// queue.
break;
}
}
if (repeatedEvent == eventQueue_.rend()) {
eventQueue_.push_back(std::move(rawEvent));
} else {
*repeatedEvent = std::move(rawEvent);
}
}
onEnqueue();
}
void EventQueue::enqueueStateUpdate(StateUpdate &&stateUpdate) const {
{
std::lock_guard<std::mutex> lock(queueMutex_);
if (!stateUpdateQueue_.empty()) {
auto const position = stateUpdateQueue_.back();
if (stateUpdate.family == position.family) {
stateUpdateQueue_.pop_back();
}
}
stateUpdateQueue_.push_back(std::move(stateUpdate));
}
onEnqueue();
}
void EventQueue::onBeat(jsi::Runtime &runtime) const {
flushStateUpdates();
flushEvents(runtime);
}
void EventQueue::flushEvents(jsi::Runtime &runtime) const {
std::vector<RawEvent> queue;
{
std::lock_guard<std::mutex> lock(queueMutex_);
if (eventQueue_.size() == 0) {
return;
}
queue = std::move(eventQueue_);
eventQueue_.clear();
}
eventProcessor_.flushEvents(runtime, std::move(queue));
}
void EventQueue::flushStateUpdates() const {
std::vector<StateUpdate> stateUpdateQueue;
{
std::lock_guard<std::mutex> lock(queueMutex_);
if (stateUpdateQueue_.empty()) {
return;
}
stateUpdateQueue = std::move(stateUpdateQueue_);
stateUpdateQueue_.clear();
}
eventProcessor_.flushStateUpdates(std::move(stateUpdateQueue));
}
} // namespace react
} // namespace facebook