Valentin Shergin bea80b2276 Fabric: Lock-free events 1/n: Removing WeakEventTarget trick from EventEmitter
Summary:
For some time we have had a nit trick inside `EventEmitter`: When event emitter is disabled, we switch to store `EventTarget` as a weak pointer instead of complete deleting it. Given some unpredictability of JS GC, it gave us some time to restore the pointer in case if we increment the counter right after decrementing this. Apparently, we found that it doesn't always work (obviously, sigh...), so we implement the `enableCounter_` and left this feature as it is assuming that it can nice optimization that illuminates some unnecessary constraints.
But, apparently, it's actually harmful, assuming that `jsi::WeakObject` (that thing that we use to refer to actual JS target) actually has "unsafe unretained" (not "weak") semantic.
So, we have to remove this.
This change will be particularly important for coming diffs in the stack.

Reviewed By: mdvacca

Differential Revision: D13324480

fbshipit-source-id: 4c4da2984dc6a36b94b564bc9eee144142b430b0
2019-01-16 20:22:39 -08:00

108 lines
2.8 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 "EventEmitter.h"
#include <folly/dynamic.h>
#include <jsi/JSIDynamic.h>
#include <jsi/jsi.h>
#include <react/debug/SystraceSection.h>
#include "RawEvent.h"
namespace facebook {
namespace react {
// TODO(T29874519): Get rid of "top" prefix once and for all.
/*
* Capitalizes the first letter of the event type and adds "top" prefix if
* necessary (e.g. "layout" becames "topLayout").
*/
static std::string normalizeEventType(const std::string &type) {
auto prefixedType = type;
if (type.find("top", 0) != 0) {
prefixedType.insert(0, "top");
prefixedType[3] = toupper(prefixedType[3]);
}
return prefixedType;
}
std::recursive_mutex &EventEmitter::DispatchMutex() {
static std::recursive_mutex mutex;
return mutex;
}
ValueFactory EventEmitter::defaultPayloadFactory() {
static auto payloadFactory =
ValueFactory{[](jsi::Runtime &runtime) { return jsi::Object(runtime); }};
return payloadFactory;
}
EventEmitter::EventEmitter(
SharedEventTarget eventTarget,
Tag tag,
WeakEventDispatcher eventDispatcher)
: eventTarget_(std::move(eventTarget)),
eventDispatcher_(std::move(eventDispatcher)) {}
void EventEmitter::dispatchEvent(
const std::string &type,
const folly::dynamic &payload,
const EventPriority &priority) const {
dispatchEvent(
type,
[payload](jsi::Runtime &runtime) {
return valueFromDynamic(runtime, payload);
},
priority);
}
void EventEmitter::dispatchEvent(
const std::string &type,
const ValueFactory &payloadFactory,
const EventPriority &priority) const {
SystraceSection s("EventEmitter::dispatchEvent");
auto eventDispatcher = eventDispatcher_.lock();
if (!eventDispatcher) {
return;
}
eventDispatcher->dispatchEvent(
RawEvent(normalizeEventType(type), payloadFactory, eventTarget_),
priority);
}
void EventEmitter::enable() const {
enableCounter_++;
toggleEventTargetOwnership_();
}
void EventEmitter::disable() const {
enableCounter_--;
toggleEventTargetOwnership_();
}
void EventEmitter::toggleEventTargetOwnership_() const {
// Note: Initially, the state of `eventTarget_` and the value `enableCounter_`
// is mismatched intentionally (it's `non-null` and `0` accordingly). We need
// this to support an initial nebula state where the event target must be
// retained without any associated mounted node.
bool shouldBeRetained = enableCounter_ > 0;
bool alreadyBeRetained = eventTarget_ != nullptr;
if (shouldBeRetained == alreadyBeRetained) {
return;
}
if (!shouldBeRetained) {
eventTarget_.reset();
}
}
} // namespace react
} // namespace facebook