1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 #include "ABI49_0_0Scheduler.h"
9
10 #include <glog/logging.h>
11 #include <ABI49_0_0jsi/ABI49_0_0jsi.h>
12
13 #include <ABI49_0_0React/debug/ABI49_0_0React_native_assert.h>
14 #include <ABI49_0_0React/renderer/componentregistry/ABI49_0_0ComponentDescriptorRegistry.h>
15 #include <ABI49_0_0React/renderer/core/ABI49_0_0EventQueueProcessor.h>
16 #include <ABI49_0_0React/renderer/core/ABI49_0_0LayoutContext.h>
17 #include <ABI49_0_0React/renderer/debug/ABI49_0_0SystraceSection.h>
18 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0MountingOverrideDelegate.h>
19 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0ShadowViewMutation.h>
20 #include <ABI49_0_0React/renderer/runtimescheduler/ABI49_0_0RuntimeScheduler.h>
21 #include <ABI49_0_0React/renderer/templateprocessor/ABI49_0_0UITemplateProcessor.h>
22 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0UIManager.h>
23 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0UIManagerBinding.h>
24
25 #ifdef ABI49_0_0RN_SHADOW_TREE_INTROSPECTION
26 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0stubs.h>
27 #include <iostream>
28 #endif
29
30 namespace ABI49_0_0facebook::ABI49_0_0React {
31
Scheduler(SchedulerToolbox const & schedulerToolbox,UIManagerAnimationDelegate * animationDelegate,SchedulerDelegate * delegate)32 Scheduler::Scheduler(
33 SchedulerToolbox const &schedulerToolbox,
34 UIManagerAnimationDelegate *animationDelegate,
35 SchedulerDelegate *delegate) {
36 runtimeExecutor_ = schedulerToolbox.runtimeExecutor;
37 contextContainer_ = schedulerToolbox.contextContainer;
38
39 ABI49_0_0ReactNativeConfig_ =
40 contextContainer_->at<std::shared_ptr<const ABI49_0_0ReactNativeConfig>>(
41 "ABI49_0_0ReactNativeConfig");
42
43 // Creating a container for future `EventDispatcher` instance.
44 eventDispatcher_ = std::make_shared<std::optional<EventDispatcher const>>();
45
46 auto uiManager = std::make_shared<UIManager>(
47 runtimeExecutor_, schedulerToolbox.backgroundExecutor, contextContainer_);
48 auto eventOwnerBox = std::make_shared<EventBeat::OwnerBox>();
49 eventOwnerBox->owner = eventDispatcher_;
50
51 auto weakRuntimeScheduler =
52 contextContainer_->find<std::weak_ptr<RuntimeScheduler>>(
53 "RuntimeScheduler");
54 auto runtimeScheduler = weakRuntimeScheduler.has_value()
55 ? weakRuntimeScheduler.value().lock()
56 : nullptr;
57
58 auto eventPipe = [uiManager, runtimeScheduler = runtimeScheduler.get()](
59 jsi::Runtime &runtime,
60 const EventTarget *eventTarget,
61 const std::string &type,
62 ABI49_0_0ReactEventPriority priority,
63 const ValueFactory &payloadFactory) {
64 uiManager->visitBinding(
65 [&](UIManagerBinding const &uiManagerBinding) {
66 uiManagerBinding.dispatchEvent(
67 runtime, eventTarget, type, priority, payloadFactory);
68 },
69 runtime);
70 if (runtimeScheduler != nullptr) {
71 runtimeScheduler->callExpiredTasks(runtime);
72 }
73 };
74
75 auto statePipe = [uiManager](StateUpdate const &stateUpdate) {
76 uiManager->updateState(stateUpdate);
77 };
78
79 // Creating an `EventDispatcher` instance inside the already allocated
80 // container (inside the optional).
81 eventDispatcher_->emplace(
82 EventQueueProcessor(eventPipe, statePipe),
83 schedulerToolbox.synchronousEventBeatFactory,
84 schedulerToolbox.asynchronousEventBeatFactory,
85 eventOwnerBox);
86
87 // Casting to `std::shared_ptr<EventDispatcher const>`.
88 auto eventDispatcher =
89 EventDispatcher::Shared{eventDispatcher_, &eventDispatcher_->value()};
90
91 componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory(
92 eventDispatcher, contextContainer_);
93
94 uiManager->setDelegate(this);
95 uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_);
96
97 auto bindingsExecutor =
98 schedulerToolbox.bridgelessBindingsExecutor.has_value()
99 ? schedulerToolbox.bridgelessBindingsExecutor.value()
100 : runtimeExecutor_;
101 bindingsExecutor([uiManager](jsi::Runtime &runtime) {
102 UIManagerBinding::createAndInstallIfNeeded(runtime, uiManager);
103 });
104
105 auto componentDescriptorRegistryKey =
106 "ComponentDescriptorRegistry_DO_NOT_USE_PRETTY_PLEASE";
107 contextContainer_->erase(componentDescriptorRegistryKey);
108 contextContainer_->insert(
109 componentDescriptorRegistryKey,
110 std::weak_ptr<ComponentDescriptorRegistry const>(
111 componentDescriptorRegistry_));
112
113 delegate_ = delegate;
114 commitHooks_ = schedulerToolbox.commitHooks;
115 uiManager_ = uiManager;
116
117 for (auto const &commitHook : commitHooks_) {
118 uiManager->registerCommitHook(*commitHook);
119 }
120
121 #ifdef ANDROID
122 removeOutstandingSurfacesOnDestruction_ = true;
123 reduceDeleteCreateMutationLayoutAnimation_ = ABI49_0_0ReactNativeConfig_->getBool(
124 "ABI49_0_0React_fabric:reduce_delete_create_mutation_layout_animation_android");
125 #else
126 removeOutstandingSurfacesOnDestruction_ = ABI49_0_0ReactNativeConfig_->getBool(
127 "ABI49_0_0React_fabric:remove_outstanding_surfaces_on_destruction_ios");
128 reduceDeleteCreateMutationLayoutAnimation_ = true;
129 #endif
130
131 CoreFeatures::blockPaintForUseLayoutEffect = ABI49_0_0ReactNativeConfig_->getBool(
132 "ABI49_0_0React_fabric:block_paint_for_use_layout_effect");
133
134 if (animationDelegate != nullptr) {
135 animationDelegate->setComponentDescriptorRegistry(
136 componentDescriptorRegistry_);
137 animationDelegate->setReduceDeleteCreateMutation(
138 reduceDeleteCreateMutationLayoutAnimation_);
139 }
140 uiManager_->setAnimationDelegate(animationDelegate);
141 }
142
~Scheduler()143 Scheduler::~Scheduler() {
144 LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this
145 << ").";
146
147 for (auto const &commitHook : commitHooks_) {
148 uiManager_->unregisterCommitHook(*commitHook);
149 }
150
151 // All Surfaces must be explicitly stopped before destroying `Scheduler`.
152 // The idea is that `UIManager` is allowed to call `Scheduler` only if the
153 // corresponding `ShadowTree` instance exists.
154
155 // The thread-safety of this operation is guaranteed by this requirement.
156 uiManager_->setDelegate(nullptr);
157 uiManager_->setAnimationDelegate(nullptr);
158
159 // Then, let's verify that the requirement was satisfied.
160 auto surfaceIds = std::vector<SurfaceId>{};
161 uiManager_->getShadowTreeRegistry().enumerate(
162 [&surfaceIds](ShadowTree const &shadowTree, bool &) {
163 surfaceIds.push_back(shadowTree.getSurfaceId());
164 });
165
166 // TODO(T88046056): Fix Android memory leak before uncommenting changes
167 // ABI49_0_0React_native_assert(
168 // surfaceIds.empty() &&
169 // "Scheduler was destroyed with outstanding Surfaces.");
170
171 if (surfaceIds.empty()) {
172 return;
173 }
174
175 LOG(ERROR) << "Scheduler was destroyed with outstanding Surfaces.";
176
177 // If we are here, that means assert didn't fire which indicates that we in
178 // production.
179
180 // Now we have still-running surfaces, which is no good, no good.
181 // That's indeed a sign of a severe issue on the application layer.
182 // At this point, we don't have much to lose, so we are trying to unmount all
183 // outstanding `ShadowTree`s to prevent all stored JSI entities from
184 // overliving the `Scheduler`. (Unmounting `ShadowNode`s disables
185 // `EventEmitter`s which destroys JSI objects.)
186 for (auto surfaceId : surfaceIds) {
187 uiManager_->getShadowTreeRegistry().visit(
188 surfaceId,
189 [](ShadowTree const &shadowTree) { shadowTree.commitEmptyTree(); });
190
191 // Removing surfaces is gated because it acquires mutex waiting for commits
192 // in flight; in theory, it can deadlock.
193 if (removeOutstandingSurfacesOnDestruction_) {
194 uiManager_->getShadowTreeRegistry().remove(surfaceId);
195 }
196 }
197 }
198
registerSurface(SurfaceHandler const & surfaceHandler) const199 void Scheduler::registerSurface(
200 SurfaceHandler const &surfaceHandler) const noexcept {
201 surfaceHandler.setContextContainer(getContextContainer());
202 surfaceHandler.setUIManager(uiManager_.get());
203 }
204
getInspectorDataForInstance(EventEmitter const & eventEmitter) const205 InspectorData Scheduler::getInspectorDataForInstance(
206 EventEmitter const &eventEmitter) const noexcept {
207 return executeSynchronouslyOnSameThread_CAN_DEADLOCK<InspectorData>(
208 runtimeExecutor_, [=](jsi::Runtime &runtime) -> InspectorData {
209 auto uiManagerBinding = UIManagerBinding::getBinding(runtime);
210 auto value = uiManagerBinding->getInspectorDataForInstance(
211 runtime, eventEmitter);
212
213 // TODO T97216348: avoid transforming jsi into folly::dynamic
214 auto dynamic = jsi::dynamicFromValue(runtime, value);
215 auto source = dynamic["source"];
216
217 InspectorData result = {};
218 result.fileName =
219 source["fileName"].isNull() ? "" : source["fileName"].c_str();
220 result.lineNumber = (int)source["lineNumber"].getDouble();
221 result.columnNumber = (int)source["columnNumber"].getDouble();
222 result.selectedIndex = (int)dynamic["selectedIndex"].getDouble();
223 // TODO T97216348: remove folly::dynamic from InspectorData struct
224 result.props = dynamic["props"];
225 auto hierarchy = dynamic["hierarchy"];
226 for (auto &i : hierarchy) {
227 auto viewHierarchyValue = i["name"];
228 if (!viewHierarchyValue.isNull()) {
229 result.hierarchy.emplace_back(viewHierarchyValue.c_str());
230 }
231 }
232 return result;
233 });
234 }
235
unregisterSurface(SurfaceHandler const & surfaceHandler) const236 void Scheduler::unregisterSurface(
237 SurfaceHandler const &surfaceHandler) const noexcept {
238 surfaceHandler.setUIManager(nullptr);
239 }
240
renderTemplateToSurface(SurfaceId surfaceId,const std::string & uiTemplate)241 void Scheduler::renderTemplateToSurface(
242 SurfaceId surfaceId,
243 const std::string &uiTemplate) {
244 SystraceSection s("Scheduler::renderTemplateToSurface");
245 try {
246 if (uiTemplate.empty()) {
247 return;
248 }
249 NativeModuleRegistry nMR;
250 auto tree = UITemplateProcessor::buildShadowTree(
251 uiTemplate,
252 surfaceId,
253 folly::dynamic::object(),
254 *componentDescriptorRegistry_,
255 nMR,
256 ABI49_0_0ReactNativeConfig_);
257
258 uiManager_->getShadowTreeRegistry().visit(
259 surfaceId, [=](const ShadowTree &shadowTree) {
260 return shadowTree.tryCommit(
261 [&](RootShadowNode const &oldRootShadowNode) {
262 return std::make_shared<RootShadowNode>(
263 oldRootShadowNode,
264 ShadowNodeFragment{
265 /* .props = */ ShadowNodeFragment::propsPlaceholder(),
266 /* .children = */
267 std::make_shared<ShadowNode::ListOfShared>(
268 ShadowNode::ListOfShared{tree}),
269 });
270 },
271 {/* default commit options */});
272 });
273 } catch (const std::exception &e) {
274 LOG(ERROR) << " >>>> ABI49_0_0EXCEPTION <<< rendering uiTemplate in "
275 << "Scheduler::renderTemplateToSurface: " << e.what();
276 }
277 }
278
279 ComponentDescriptor const *
findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(ComponentHandle handle) const280 Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
281 ComponentHandle handle) const {
282 return componentDescriptorRegistry_
283 ->findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(handle);
284 }
285
286 #pragma mark - Delegate
287
setDelegate(SchedulerDelegate * delegate)288 void Scheduler::setDelegate(SchedulerDelegate *delegate) {
289 delegate_ = delegate;
290 }
291
getDelegate() const292 SchedulerDelegate *Scheduler::getDelegate() const {
293 return delegate_;
294 }
295
296 #pragma mark - UIManagerAnimationDelegate
297
animationTick() const298 void Scheduler::animationTick() const {
299 uiManager_->animationTick();
300 }
301
302 #pragma mark - UIManagerDelegate
303
uiManagerDidFinishTransaction(MountingCoordinator::Shared mountingCoordinator,bool mountSynchronously)304 void Scheduler::uiManagerDidFinishTransaction(
305 MountingCoordinator::Shared mountingCoordinator,
306 bool mountSynchronously) {
307 SystraceSection s("Scheduler::uiManagerDidFinishTransaction");
308
309 if (delegate_ != nullptr) {
310 if (CoreFeatures::blockPaintForUseLayoutEffect) {
311 auto weakRuntimeScheduler =
312 contextContainer_->find<std::weak_ptr<RuntimeScheduler>>(
313 "RuntimeScheduler");
314 auto runtimeScheduler = weakRuntimeScheduler.has_value()
315 ? weakRuntimeScheduler.value().lock()
316 : nullptr;
317 if (runtimeScheduler && !mountSynchronously) {
318 runtimeScheduler->scheduleTask(
319 SchedulerPriority::UserBlockingPriority,
320 [delegate = delegate_,
321 mountingCoordinator =
322 std::move(mountingCoordinator)](jsi::Runtime &) {
323 delegate->schedulerDidFinishTransaction(
324 std::move(mountingCoordinator));
325 });
326 } else {
327 delegate_->schedulerDidFinishTransaction(
328 std::move(mountingCoordinator));
329 }
330 } else {
331 delegate_->schedulerDidFinishTransaction(std::move(mountingCoordinator));
332 }
333 }
334 }
uiManagerDidCreateShadowNode(const ShadowNode & shadowNode)335 void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode &shadowNode) {
336 SystraceSection s("Scheduler::uiManagerDidCreateShadowNode");
337
338 if (delegate_ != nullptr) {
339 delegate_->schedulerDidRequestPreliminaryViewAllocation(
340 shadowNode.getSurfaceId(), shadowNode);
341 }
342 }
343
uiManagerDidDispatchCommand(const ShadowNode::Shared & shadowNode,std::string const & commandName,folly::dynamic const & args)344 void Scheduler::uiManagerDidDispatchCommand(
345 const ShadowNode::Shared &shadowNode,
346 std::string const &commandName,
347 folly::dynamic const &args) {
348 SystraceSection s("Scheduler::uiManagerDispatchCommand");
349
350 if (delegate_ != nullptr) {
351 auto shadowView = ShadowView(*shadowNode);
352 delegate_->schedulerDidDispatchCommand(shadowView, commandName, args);
353 }
354 }
355
uiManagerDidSendAccessibilityEvent(const ShadowNode::Shared & shadowNode,std::string const & eventType)356 void Scheduler::uiManagerDidSendAccessibilityEvent(
357 const ShadowNode::Shared &shadowNode,
358 std::string const &eventType) {
359 SystraceSection s("Scheduler::uiManagerDidSendAccessibilityEvent");
360
361 if (delegate_ != nullptr) {
362 auto shadowView = ShadowView(*shadowNode);
363 delegate_->schedulerDidSendAccessibilityEvent(shadowView, eventType);
364 }
365 }
366
367 /*
368 * Set JS responder for a view.
369 */
uiManagerDidSetIsJSResponder(ShadowNode::Shared const & shadowNode,bool isJSResponder,bool blockNativeResponder)370 void Scheduler::uiManagerDidSetIsJSResponder(
371 ShadowNode::Shared const &shadowNode,
372 bool isJSResponder,
373 bool blockNativeResponder) {
374 if (delegate_ != nullptr) {
375 delegate_->schedulerDidSetIsJSResponder(
376 ShadowView(*shadowNode), isJSResponder, blockNativeResponder);
377 }
378 }
379
getContextContainer() const380 ContextContainer::Shared Scheduler::getContextContainer() const {
381 return contextContainer_;
382 }
383
getUIManager() const384 std::shared_ptr<UIManager> Scheduler::getUIManager() const {
385 return uiManager_;
386 }
387
addEventListener(const std::shared_ptr<EventListener const> & listener)388 void Scheduler::addEventListener(
389 const std::shared_ptr<EventListener const> &listener) {
390 if (eventDispatcher_->has_value()) {
391 eventDispatcher_->value().addListener(listener);
392 }
393 }
394
removeEventListener(const std::shared_ptr<EventListener const> & listener)395 void Scheduler::removeEventListener(
396 const std::shared_ptr<EventListener const> &listener) {
397 if (eventDispatcher_->has_value()) {
398 eventDispatcher_->value().removeListener(listener);
399 }
400 }
401
402 } // namespace ABI49_0_0facebook::ABI49_0_0React
403