Skip to content

Commit bc06bf5

Browse files
committed
Support coalescing keys in the shared renderer
1 parent 9c7b0f5 commit bc06bf5

20 files changed

Lines changed: 293 additions & 19 deletions

‎packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ class EventDispatcher {
5151

5252
/*
5353
* Dispatches a raw event with asynchronous batched priority. Before the
54-
* dispatch we make sure that no other RawEvent of same type and same target
55-
* is on the queue.
54+
* dispatch we make sure that no other RawEvent of same type, same target,
55+
* and same coalescing key is on the queue.
5656
*/
5757
void dispatchUniqueEvent(RawEvent &&rawEvent) const;
5858

‎packages/react-native/ReactCommon/react/renderer/core/EventEmitter.cpp‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ void EventEmitter::dispatchUniqueEvent(
9292
eventTimestamp);
9393
}
9494

95+
void EventEmitter::dispatchUniqueEvent(
96+
std::string type,
97+
folly::dynamic&& payload,
98+
HighResTimeStamp eventTimestamp,
99+
RawEvent::CoalescingKey coalescingKey) const {
100+
dispatchUniqueEvent(
101+
std::move(type),
102+
DynamicEventPayload::create(std::move(payload)),
103+
eventTimestamp,
104+
coalescingKey);
105+
}
106+
95107
void EventEmitter::dispatchEvent(
96108
std::string type,
97109
const ValueFactory& payloadFactory,
@@ -181,6 +193,18 @@ void EventEmitter::dispatchUniqueEvent(
181193
std::string type,
182194
SharedEventPayload payload,
183195
HighResTimeStamp eventTimestamp) const {
196+
dispatchUniqueEvent(
197+
std::move(type),
198+
std::move(payload),
199+
eventTimestamp,
200+
RawEvent::DefaultCoalescingKey);
201+
}
202+
203+
void EventEmitter::dispatchUniqueEvent(
204+
std::string type,
205+
SharedEventPayload payload,
206+
HighResTimeStamp eventTimestamp,
207+
RawEvent::CoalescingKey coalescingKey) const {
184208
TraceSection s("EventEmitter::dispatchUniqueEvent");
185209

186210
auto eventDispatcher = eventDispatcher_.lock();
@@ -203,7 +227,8 @@ void EventEmitter::dispatchUniqueEvent(
203227
std::move(shadowNodeFamily),
204228
RawEvent::Category::Continuous,
205229
true,
206-
eventTimestamp));
230+
eventTimestamp,
231+
coalescingKey));
207232
}
208233

209234
void EventEmitter::setEnabled(bool enabled) {

‎packages/react-native/ReactCommon/react/renderer/core/EventEmitter.h‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,20 @@ class EventEmitter {
128128

129129
void dispatchUniqueEvent(std::string type, folly::dynamic &&payload, HighResTimeStamp eventTimestamp) const;
130130

131+
void dispatchUniqueEvent(
132+
std::string type,
133+
folly::dynamic &&payload,
134+
HighResTimeStamp eventTimestamp,
135+
RawEvent::CoalescingKey coalescingKey) const;
136+
131137
void dispatchUniqueEvent(std::string type, SharedEventPayload payload, HighResTimeStamp eventTimestamp) const;
132138

139+
void dispatchUniqueEvent(
140+
std::string type,
141+
SharedEventPayload payload,
142+
HighResTimeStamp eventTimestamp,
143+
RawEvent::CoalescingKey coalescingKey) const;
144+
133145
private:
134146
friend class UIManagerBinding;
135147

‎packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ void EventQueue::enqueueEvent(RawEvent&& rawEvent) const {
3535
// for the same target. If the same target has event types A1, B1
3636
// in the event queue and event A2 occurs. A1 has to stay in the
3737
// queue.
38-
if (it->isUnique && it->type == rawEvent.type) {
38+
if (it->isUnique && it->type == rawEvent.type &&
39+
it->coalescingKey == rawEvent.coalescingKey) {
3940
repeatedEvent = it;
4041
}
4142

‎packages/react-native/ReactCommon/react/renderer/core/EventQueue.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class EventQueue {
5050

5151
/*
5252
* Enqueues and (probably later) dispatches a given event.
53-
* Deletes last RawEvent from the queue if it has the same type and target.
53+
* Deletes last RawEvent from the queue if it has the same type, target, and
54+
* coalescing key.
5455
* Can be called on any thread.
5556
*/
5657
void enqueueUniqueEvent(RawEvent &&rawEvent) const;

‎packages/react-native/ReactCommon/react/renderer/core/RawEvent.cpp‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ RawEvent::RawEvent(
1616
std::weak_ptr<const ShadowNodeFamily> shadowNodeFamily,
1717
Category category,
1818
bool isUnique,
19-
HighResTimeStamp eventStartTimeStamp)
19+
HighResTimeStamp eventStartTimeStamp,
20+
CoalescingKey coalescingKey)
2021
: type(std::move(type)),
2122
eventPayload(std::move(eventPayload)),
2223
eventTarget(std::move(eventTarget)),
2324
shadowNodeFamily(std::move(shadowNodeFamily)),
2425
category(category),
2526
isUnique(isUnique),
27+
coalescingKey(coalescingKey),
2628
eventStartTimeStamp(eventStartTimeStamp) {}
2729

2830
} // namespace facebook::react

‎packages/react-native/ReactCommon/react/renderer/core/RawEvent.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class ShadowNodeFamily;
2525
* Represents ready-to-dispatch event object.
2626
*/
2727
struct RawEvent {
28+
using CoalescingKey = int;
29+
static constexpr CoalescingKey DefaultCoalescingKey = 0;
30+
2831
/*
2932
* Defines category of a native platform event. This is used to deduce types
3033
* of events for Concurrent Mode.
@@ -76,7 +79,8 @@ struct RawEvent {
7679
std::weak_ptr<const ShadowNodeFamily> shadowNodeFamily,
7780
Category category = Category::Unspecified,
7881
bool isUnique = false,
79-
HighResTimeStamp eventStartTimeStamp = HighResTimeStamp::now());
82+
HighResTimeStamp eventStartTimeStamp = HighResTimeStamp::now(),
83+
CoalescingKey coalescingKey = DefaultCoalescingKey);
8084

8185
std::string type;
8286
SharedEventPayload eventPayload;
@@ -85,6 +89,7 @@ struct RawEvent {
8589
Category category;
8690
EventTag loggingTag{0};
8791
bool isUnique{false};
92+
CoalescingKey coalescingKey{DefaultCoalescingKey};
8893

8994
// The timestamp for the event start time. This defaults to the current
9095
// time if not specified by the client (e.g., when MotionEvent was triggered

‎packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp‎

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
#include <jsi/jsi.h>
1111
#include <react/renderer/core/EventLogger.h>
1212
#include <react/renderer/core/EventPipe.h>
13+
#include <react/renderer/core/EventQueue.h>
1314
#include <react/renderer/core/EventQueueProcessor.h>
1415
#include <react/renderer/core/EventTarget.h>
1516
#include <react/renderer/core/InstanceHandle.h>
1617
#include <react/renderer/core/ShadowNodeFamily.h>
1718
#include <react/renderer/core/StatePipe.h>
1819
#include <react/renderer/core/ValueFactoryEventPayload.h>
20+
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
1921

2022
#include <memory>
2123
#include <stdexcept>
@@ -65,6 +67,81 @@ class EventQueueProcessorTest : public testing::Test {
6567
ValueFactory dummyValueFactory_;
6668
};
6769

70+
class NoopEventBeat final : public EventBeat {
71+
public:
72+
explicit NoopEventBeat(RuntimeScheduler& runtimeScheduler)
73+
: EventBeat(std::make_shared<EventBeat::OwnerBox>(), runtimeScheduler) {}
74+
75+
void request() const override {}
76+
void requestSynchronous(Tag /*tag*/) const override {}
77+
};
78+
79+
class TestEventQueue final : public EventQueue {
80+
public:
81+
using EventQueue::EventQueue;
82+
83+
void flush(jsi::Runtime& runtime) const {
84+
flushEvents(runtime);
85+
}
86+
};
87+
88+
class EventQueueCoalescingTest : public testing::Test {
89+
protected:
90+
void SetUp() override {
91+
runtime_ = facebook::hermes::makeHermesRuntime();
92+
93+
RuntimeExecutor runtimeExecutor =
94+
[](std::function<void(facebook::jsi::Runtime&)>&& /*callback*/) {};
95+
runtimeScheduler_ = std::make_unique<RuntimeScheduler>(runtimeExecutor);
96+
}
97+
98+
std::unique_ptr<TestEventQueue> createEventQueue() {
99+
auto eventPipe = [this](
100+
jsi::Runtime& /*runtime*/,
101+
const EventTarget* /*eventTarget*/,
102+
const std::string& type,
103+
ReactEventPriority /*priority*/,
104+
const EventPayload& /*payload*/,
105+
HighResTimeStamp eventTimestamp) {
106+
eventTypes_.push_back(type);
107+
eventTimestamps_.push_back(eventTimestamp);
108+
};
109+
110+
auto dummyEventPipeConclusion = [](jsi::Runtime&) {};
111+
auto dummyStatePipe = [](const StateUpdate&) {};
112+
auto mockEventLogger = std::make_shared<MockEventLogger>();
113+
114+
return std::make_unique<TestEventQueue>(
115+
EventQueueProcessor(
116+
eventPipe,
117+
dummyEventPipeConclusion,
118+
dummyStatePipe,
119+
mockEventLogger),
120+
std::make_unique<NoopEventBeat>(*runtimeScheduler_));
121+
}
122+
123+
RawEvent createRawEvent(
124+
std::string type,
125+
RawEvent::CoalescingKey coalescingKey,
126+
HighResTimeStamp eventTimestamp) {
127+
return RawEvent(
128+
std::move(type),
129+
std::make_shared<ValueFactoryEventPayload>(dummyValueFactory_),
130+
nullptr,
131+
{},
132+
RawEvent::Category::Continuous,
133+
false,
134+
eventTimestamp,
135+
coalescingKey);
136+
}
137+
138+
std::unique_ptr<facebook::hermes::HermesRuntime> runtime_;
139+
std::unique_ptr<RuntimeScheduler> runtimeScheduler_;
140+
std::vector<std::string> eventTypes_;
141+
std::vector<HighResTimeStamp> eventTimestamps_;
142+
ValueFactory dummyValueFactory_;
143+
};
144+
68145
TEST_F(EventQueueProcessorTest, singleUnspecifiedEvent) {
69146
eventProcessor_->flushEvents(
70147
*runtime_,
@@ -205,4 +282,95 @@ TEST_F(EventQueueProcessorTest, releasesEventTargetsWhenDispatchThrows) {
205282
EXPECT_TRUE(eventTarget->getInstanceHandle(*runtime_).isNull());
206283
}
207284

285+
TEST_F(EventQueueCoalescingTest, uniqueEventsWithSameCoalescingKeyCoalesce) {
286+
auto eventQueue = createEventQueue();
287+
auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1);
288+
auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2);
289+
290+
eventQueue->enqueueUniqueEvent(
291+
createRawEvent("topChange", 3, firstTimestamp));
292+
eventQueue->enqueueUniqueEvent(
293+
createRawEvent("topChange", 3, secondTimestamp));
294+
295+
eventQueue->flush(*runtime_);
296+
297+
ASSERT_EQ(eventTypes_.size(), 1);
298+
EXPECT_EQ(eventTypes_[0], "topChange");
299+
EXPECT_EQ(eventTimestamps_[0], secondTimestamp);
300+
}
301+
302+
TEST_F(
303+
EventQueueCoalescingTest,
304+
defaultCoalescingKeysPreserveExistingBehavior) {
305+
auto eventQueue = createEventQueue();
306+
auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1);
307+
auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2);
308+
309+
eventQueue->enqueueUniqueEvent(createRawEvent(
310+
"topChange", RawEvent::DefaultCoalescingKey, firstTimestamp));
311+
eventQueue->enqueueUniqueEvent(createRawEvent(
312+
"topChange", RawEvent::DefaultCoalescingKey, secondTimestamp));
313+
eventQueue->flush(*runtime_);
314+
315+
ASSERT_EQ(eventTimestamps_.size(), 1);
316+
EXPECT_EQ(eventTimestamps_[0], secondTimestamp);
317+
}
318+
319+
TEST_F(EventQueueCoalescingTest, defaultCoalescingKeyDoesNotMatchCustomKey) {
320+
auto eventQueue = createEventQueue();
321+
auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1);
322+
auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2);
323+
324+
eventQueue->enqueueUniqueEvent(createRawEvent(
325+
"topChange", RawEvent::DefaultCoalescingKey, firstTimestamp));
326+
eventQueue->enqueueUniqueEvent(
327+
createRawEvent("topChange", 3, secondTimestamp));
328+
eventQueue->flush(*runtime_);
329+
330+
ASSERT_EQ(eventTimestamps_.size(), 2);
331+
EXPECT_EQ(eventTimestamps_[0], firstTimestamp);
332+
EXPECT_EQ(eventTimestamps_[1], secondTimestamp);
333+
}
334+
335+
TEST_F(EventQueueCoalescingTest, differentKeysPreserveEventOrder) {
336+
auto eventQueue = createEventQueue();
337+
auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1);
338+
auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2);
339+
auto thirdTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(3);
340+
341+
eventQueue->enqueueUniqueEvent(
342+
createRawEvent("topChange", 3, firstTimestamp));
343+
eventQueue->enqueueUniqueEvent(
344+
createRawEvent("topChange", 4, secondTimestamp));
345+
eventQueue->enqueueUniqueEvent(
346+
createRawEvent("topChange", 3, thirdTimestamp));
347+
eventQueue->flush(*runtime_);
348+
349+
ASSERT_EQ(eventTimestamps_.size(), 3);
350+
EXPECT_EQ(eventTimestamps_[0], firstTimestamp);
351+
EXPECT_EQ(eventTimestamps_[1], secondTimestamp);
352+
EXPECT_EQ(eventTimestamps_[2], thirdTimestamp);
353+
}
354+
355+
TEST_F(
356+
EventQueueCoalescingTest,
357+
uniqueEventsWithDifferentCoalescingKeysDoNotCoalesce) {
358+
auto eventQueue = createEventQueue();
359+
auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1);
360+
auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2);
361+
362+
eventQueue->enqueueUniqueEvent(
363+
createRawEvent("topChange", 3, firstTimestamp));
364+
eventQueue->enqueueUniqueEvent(
365+
createRawEvent("topChange", 4, secondTimestamp));
366+
367+
eventQueue->flush(*runtime_);
368+
369+
ASSERT_EQ(eventTypes_.size(), 2);
370+
EXPECT_EQ(eventTypes_[0], "topChange");
371+
EXPECT_EQ(eventTypes_[1], "topChange");
372+
EXPECT_EQ(eventTimestamps_[0], firstTimestamp);
373+
EXPECT_EQ(eventTimestamps_[1], secondTimestamp);
374+
}
375+
208376
} // namespace facebook::react

‎scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1159,8 +1159,10 @@ class facebook::react::EventEmitter {
11591159
public void dispatchUniqueEvent(std::string type, const facebook::react::ValueFactory& payloadFactory, facebook::react::HighResTimeStamp eventTimestamp) const;
11601160
public void dispatchUniqueEvent(std::string type, facebook::react::SharedEventPayload payload) const;
11611161
public void dispatchUniqueEvent(std::string type, facebook::react::SharedEventPayload payload, facebook::react::HighResTimeStamp eventTimestamp) const;
1162+
public void dispatchUniqueEvent(std::string type, facebook::react::SharedEventPayload payload, facebook::react::HighResTimeStamp eventTimestamp, facebook::react::RawEvent::CoalescingKey coalescingKey) const;
11621163
public void dispatchUniqueEvent(std::string type, folly::dynamic&& payload) const;
11631164
public void dispatchUniqueEvent(std::string type, folly::dynamic&& payload, facebook::react::HighResTimeStamp eventTimestamp) const;
1165+
public void dispatchUniqueEvent(std::string type, folly::dynamic&& payload, facebook::react::HighResTimeStamp eventTimestamp, facebook::react::RawEvent::CoalescingKey coalescingKey) const;
11641166
public void setEnabled(bool enabled);
11651167
public void setShadowNodeFamily(std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily);
11661168
}
@@ -3512,15 +3514,18 @@ struct facebook::react::RadialGradientSize::Dimensions {
35123514
}
35133515

35143516
struct facebook::react::RawEvent {
3515-
public RawEvent(std::string type, facebook::react::SharedEventPayload eventPayload, facebook::react::SharedEventTarget eventTarget, std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily, facebook::react::RawEvent::Category category = facebook::react::RawEvent::Category::Unspecified, bool isUnique = false, facebook::react::HighResTimeStamp eventStartTimeStamp = facebook::react::HighResTimeStamp::now());
3517+
public RawEvent(std::string type, facebook::react::SharedEventPayload eventPayload, facebook::react::SharedEventTarget eventTarget, std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily, facebook::react::RawEvent::Category category = facebook::react::RawEvent::Category::Unspecified, bool isUnique = false, facebook::react::HighResTimeStamp eventStartTimeStamp = facebook::react::HighResTimeStamp::now(), facebook::react::RawEvent::CoalescingKey coalescingKey = facebook::react::RawEvent::DefaultCoalescingKey);
35163518
public bool isUnique;
35173519
public facebook::react::EventTag loggingTag;
35183520
public facebook::react::HighResTimeStamp eventStartTimeStamp;
35193521
public facebook::react::RawEvent::Category category;
3522+
public facebook::react::RawEvent::CoalescingKey coalescingKey;
35203523
public facebook::react::SharedEventPayload eventPayload;
35213524
public facebook::react::SharedEventTarget eventTarget;
3525+
public static constexpr facebook::react::RawEvent::CoalescingKey DefaultCoalescingKey;
35223526
public std::string type;
35233527
public std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily;
3528+
public using CoalescingKey = int;
35243529
}
35253530

35263531
enum facebook::react::RawEvent::Category {

‎scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1866,8 +1866,10 @@ class facebook::react::EventEmitter {
18661866
public void dispatchUniqueEvent(std::string type, const facebook::react::ValueFactory& payloadFactory, facebook::react::HighResTimeStamp eventTimestamp) const;
18671867
public void dispatchUniqueEvent(std::string type, facebook::react::SharedEventPayload payload) const;
18681868
public void dispatchUniqueEvent(std::string type, facebook::react::SharedEventPayload payload, facebook::react::HighResTimeStamp eventTimestamp) const;
1869+
public void dispatchUniqueEvent(std::string type, facebook::react::SharedEventPayload payload, facebook::react::HighResTimeStamp eventTimestamp, facebook::react::RawEvent::CoalescingKey coalescingKey) const;
18691870
public void dispatchUniqueEvent(std::string type, folly::dynamic&& payload) const;
18701871
public void dispatchUniqueEvent(std::string type, folly::dynamic&& payload, facebook::react::HighResTimeStamp eventTimestamp) const;
1872+
public void dispatchUniqueEvent(std::string type, folly::dynamic&& payload, facebook::react::HighResTimeStamp eventTimestamp, facebook::react::RawEvent::CoalescingKey coalescingKey) const;
18711873
public void setEnabled(bool enabled);
18721874
public void setShadowNodeFamily(std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily);
18731875
}
@@ -6432,15 +6434,18 @@ struct facebook::react::RadialGradientSize::Dimensions {
64326434
}
64336435

64346436
struct facebook::react::RawEvent {
6435-
public RawEvent(std::string type, facebook::react::SharedEventPayload eventPayload, facebook::react::SharedEventTarget eventTarget, std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily, facebook::react::RawEvent::Category category = facebook::react::RawEvent::Category::Unspecified, bool isUnique = false, facebook::react::HighResTimeStamp eventStartTimeStamp = facebook::react::HighResTimeStamp::now());
6437+
public RawEvent(std::string type, facebook::react::SharedEventPayload eventPayload, facebook::react::SharedEventTarget eventTarget, std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily, facebook::react::RawEvent::Category category = facebook::react::RawEvent::Category::Unspecified, bool isUnique = false, facebook::react::HighResTimeStamp eventStartTimeStamp = facebook::react::HighResTimeStamp::now(), facebook::react::RawEvent::CoalescingKey coalescingKey = facebook::react::RawEvent::DefaultCoalescingKey);
64366438
public bool isUnique;
64376439
public facebook::react::EventTag loggingTag;
64386440
public facebook::react::HighResTimeStamp eventStartTimeStamp;
64396441
public facebook::react::RawEvent::Category category;
6442+
public facebook::react::RawEvent::CoalescingKey coalescingKey;
64406443
public facebook::react::SharedEventPayload eventPayload;
64416444
public facebook::react::SharedEventTarget eventTarget;
6445+
public static constexpr facebook::react::RawEvent::CoalescingKey DefaultCoalescingKey;
64426446
public std::string type;
64436447
public std::weak_ptr<const facebook::react::ShadowNodeFamily> shadowNodeFamily;
6448+
public using CoalescingKey = int;
64446449
}
64456450

64466451
enum facebook::react::RawEvent::Category {

0 commit comments

Comments
 (0)