Files
react-native/ReactCommon/react/renderer/core/ConcreteState.h
T
Samuel Susla 64d9364095 Copy data in updateState instead of moving them
Summary:
Changelog: [internal]

`UpdateState` lambda can be called multiple times because of state auto-repeater. But previously we moved state data into the lambda and then moved it into constructor shared data constructor. If auto-repeater called update state lambda again, the data would be in invalid state.
To fix this, state data has to be copied into shared data constructor.

Background executor was more likely to kick trigger state auto-repeater, that's why the crash was happening in BE experiment.
State-autorepeater was enabled at the end of December. That explains why the crash started appearing in v301 (prior to 301, this crash was not happening).

Reviewed By: JoshuaGross, shergin

Differential Revision: D26173800

fbshipit-source-id: 46d1bae226e607d04d5ba28e6c2a8ec86258593a
2021-02-01 10:17:56 -08:00

112 lines
3.1 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.
*/
#pragma once
#include <functional>
#include <memory>
#include <react/renderer/core/State.h>
namespace facebook {
namespace react {
/*
* Concrete and only template implementation of State interface.
* State wraps an arbitrary data type and provides an interface to initiate a
* state update transaction. A data object does not need to be copyable but
* needs to be moveable.
*/
template <typename DataT>
class ConcreteState : public State {
public:
using Shared = std::shared_ptr<ConcreteState const>;
using Data = DataT;
using SharedData = std::shared_ptr<Data const>;
/*
* Creates an updated `State` object with given previous one and `data`.
*/
explicit ConcreteState(SharedData const &data, State const &state)
: State(data, state) {}
/*
* Creates a first-of-its-family `State` object with given `family` and
* `data`.
*/
explicit ConcreteState(
SharedData const &data,
ShadowNodeFamily::Shared const &family)
: State(data, family) {}
virtual ~ConcreteState() = default;
/*
* Returns stored data.
*/
Data const &getData() const {
return *std::static_pointer_cast<Data const>(data_);
}
/*
* Initiate a state update process with given new data and priority.
* This is a simplified convenience version of the method that receives a
* function for cases where a new value of data does not depend on an old
* value.
*/
void updateState(
Data &&newData,
EventPriority priority = EventPriority::AsynchronousUnbatched) const {
updateState(
[data{std::move(newData)}](Data const &oldData) -> SharedData {
return std::make_shared<Data const>(data);
},
priority);
}
/*
* Initiate a state update process with a given function (that transforms an
* old data value to a new one) and priority. The callback function can be
* called from any thread any moment later.
* In case of a conflict, the `callback` might be called several times until
* it succeeded. To cancel the state update operation, the callback needs to
* return `nullptr`.
*/
void updateState(
std::function<StateData::Shared(Data const &oldData)> callback,
EventPriority priority = EventPriority::AsynchronousBatched) const {
auto family = family_.lock();
if (!family) {
// No more nodes of this family exist anymore,
// updating state is impossible.
return;
}
auto stateUpdate = StateUpdate{
family, [=](StateData::Shared const &oldData) -> StateData::Shared {
assert(oldData);
return callback(*std::static_pointer_cast<Data const>(oldData));
}};
family->dispatchRawState(std::move(stateUpdate), priority);
}
#ifdef ANDROID
folly::dynamic getDynamic() const override {
return getData().getDynamic();
}
void updateState(folly::dynamic data) const override {
updateState(std::move(Data(getData(), data)));
}
#endif
};
} // namespace react
} // namespace facebook