1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 09:48:11 +00:00

LibWeb: Use copy-on-write to make cloning a FormattingState much cheaper

Instead of making a full copy of every NodeState when cloning a
FormattingState, we make NodeState ref-counted and implement a basic
copy-on-write mechanism.

FormattingState::get_mutable() now makes a deep copy of the NodeState
when first accessed *if* it is shared with other FormattingStates.
This commit is contained in:
Andreas Kling 2022-02-28 12:32:23 +01:00
parent 0a533da22f
commit aa72ebf7aa
2 changed files with 36 additions and 18 deletions

View file

@ -9,6 +9,23 @@
namespace Web::Layout {
FormattingState::NodeState& FormattingState::get_mutable(NodeWithStyleAndBoxModelMetrics const& box)
{
auto state = nodes.ensure(&box, [] { return adopt_ref(*new NodeState); });
// CoW if ref_count > 2 (1 for the entry in `this->nodes`, 1 for the `state` local in this function)
if (state->ref_count > 2) {
state = adopt_ref(*new NodeState { *state });
state->ref_count = 1;
nodes.set(&box, state);
}
return state;
}
FormattingState::NodeState const& FormattingState::get(NodeWithStyleAndBoxModelMetrics const& box) const
{
return *const_cast<FormattingState&>(*this).nodes.ensure(&box, [] { return adopt_ref(*new NodeState); });
}
void FormattingState::commit()
{
for (auto& it : nodes) {

View file

@ -62,30 +62,31 @@ struct FormattingState {
overflow_data = Layout::Box::OverflowData {};
return *overflow_data;
}
// NOTE: NodeState is ref-counted and accessed via copy-on-write helpers below.
size_t ref_count { 1 };
void ref()
{
VERIFY(ref_count);
++ref_count;
}
void unref()
{
VERIFY(ref_count);
if (!--ref_count)
delete this;
}
};
void commit();
FormattingState clone() const
{
FormattingState new_state;
for (auto& it : nodes) {
new_state.nodes.set(it.key, make<NodeState>(*it.value));
}
return new_state;
}
// NOTE: get_mutable() will CoW the NodeState if it's shared with another FormattingContext.
NodeState& get_mutable(NodeWithStyleAndBoxModelMetrics const&);
NodeState& get_mutable(NodeWithStyleAndBoxModelMetrics const& box)
{
return *nodes.ensure(&box, [] { return make<NodeState>(); });
}
// NOTE: get() will not CoW the NodeState.
NodeState const& get(NodeWithStyleAndBoxModelMetrics const&) const;
NodeState const& get(NodeWithStyleAndBoxModelMetrics const& box) const
{
return const_cast<FormattingState&>(*this).get_mutable(box);
}
HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullOwnPtr<NodeState>> nodes;
HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullRefPtr<NodeState>> nodes;
};
Gfx::FloatRect absolute_content_rect(Box const&, FormattingState const&);