pull/15844/head
Rich Harris 2 months ago
commit 6912cd3621

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: simplify reaction/source ownership tracking

@ -10,7 +10,7 @@ jobs:
if: github.repository == 'sveltejs/svelte' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run') if: github.repository == 'sveltejs/svelte' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
permissions: permissions:
issues: write # to add / delete reactions issues: write # to add / delete reactions
pull-requests: read # to read PR data pull-requests: write # to read PR data, and to add labels
actions: read # to check workflow status actions: read # to check workflow status
contents: read # to clone the repo contents: read # to clone the repo
steps: steps:

@ -1,6 +1,13 @@
/** @import { Source } from '#client' */ /** @import { Source } from '#client' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { get, active_effect, active_reaction, set_active_reaction } from './runtime.js'; import {
get,
active_effect,
update_version,
active_reaction,
set_update_version,
set_active_reaction
} from './runtime.js';
import { import {
array_prototype, array_prototype,
get_descriptor, get_descriptor,
@ -41,7 +48,7 @@ export function proxy(value) {
var version = source(0); var version = source(0);
var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null; var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null;
var reaction = active_reaction; var parent_version = update_version;
/** /**
* Executes the proxy in the context of the reaction it was originally created in, if any * Executes the proxy in the context of the reaction it was originally created in, if any
@ -49,13 +56,23 @@ export function proxy(value) {
* @param {() => T} fn * @param {() => T} fn
*/ */
var with_parent = (fn) => { var with_parent = (fn) => {
var previous_reaction = active_reaction; if (update_version === parent_version) {
set_active_reaction(reaction); return fn();
}
// child source is being created after the initial proxy —
// prevent it from being associated with the current reaction
var reaction = active_reaction;
var version = update_version;
set_active_reaction(null);
set_update_version(parent_version);
/** @type {T} */
var result = fn(); var result = fn();
set_active_reaction(previous_reaction); set_active_reaction(reaction);
set_update_version(version);
return result; return result;
}; };

@ -10,7 +10,7 @@ import {
untrack, untrack,
increment_write_version, increment_write_version,
update_effect, update_effect,
source_ownership, current_sources,
is_dirty, is_dirty,
untracking, untracking,
is_destroying_effect, is_destroying_effect,
@ -141,7 +141,7 @@ export function set(source, value, should_proxy = false) {
(!untracking || (active_reaction.f & INSPECT_EFFECT) !== 0) && (!untracking || (active_reaction.f & INSPECT_EFFECT) !== 0) &&
is_runes() && is_runes() &&
(active_reaction.f & (DERIVED | BLOCK_EFFECT | EFFECT_ASYNC | INSPECT_EFFECT)) !== 0 && (active_reaction.f & (DERIVED | BLOCK_EFFECT | EFFECT_ASYNC | INSPECT_EFFECT)) !== 0 &&
!(source_ownership?.reaction === active_reaction && source_ownership.sources.includes(source)) !current_sources?.includes(source)
) { ) {
e.state_unsafe_mutation(); e.state_unsafe_mutation();
} }

@ -87,17 +87,17 @@ export function set_active_effect(effect) {
/** /**
* When sources are created within a reaction, reading and writing * When sources are created within a reaction, reading and writing
* them within that reaction should not cause a re-run * them within that reaction should not cause a re-run
* @type {null | { reaction: Reaction, sources: Source[] }} * @type {null | Source[]}
*/ */
export let source_ownership = null; export let current_sources = null;
/** @param {Value} value */ /** @param {Value} value */
export function push_reaction_value(value) { export function push_reaction_value(value) {
if (active_reaction !== null && (!async_mode_flag || (active_reaction.f & DERIVED) !== 0)) { if (active_reaction !== null && (!async_mode_flag || (active_reaction.f & DERIVED) !== 0)) {
if (source_ownership === null) { if (current_sources === null) {
source_ownership = { reaction: active_reaction, sources: [value] }; current_sources = [value];
} else { } else {
source_ownership.sources.push(value); current_sources.push(value);
} }
} }
} }
@ -135,6 +135,11 @@ let read_version = 0;
export let update_version = read_version; export let update_version = read_version;
/** @param {number} value */
export function set_update_version(value) {
update_version = value;
}
// If we are working with a get() chain that has no active container, // If we are working with a get() chain that has no active container,
// to prevent memory leaks, we skip adding the reaction. // to prevent memory leaks, we skip adding the reaction.
export let skip_reaction = false; export let skip_reaction = false;
@ -239,11 +244,7 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true)
var reactions = signal.reactions; var reactions = signal.reactions;
if (reactions === null) return; if (reactions === null) return;
if ( if (!async_mode_flag && current_sources?.includes(signal)) {
!async_mode_flag &&
source_ownership?.reaction === active_reaction &&
source_ownership.sources.includes(signal)
) {
return; return;
} }
@ -270,7 +271,7 @@ export function update_reaction(reaction) {
var previous_untracked_writes = untracked_writes; var previous_untracked_writes = untracked_writes;
var previous_reaction = active_reaction; var previous_reaction = active_reaction;
var previous_skip_reaction = skip_reaction; var previous_skip_reaction = skip_reaction;
var previous_reaction_sources = source_ownership; var previous_sources = current_sources;
var previous_component_context = component_context; var previous_component_context = component_context;
var previous_untracking = untracking; var previous_untracking = untracking;
var previous_update_version = update_version; var previous_update_version = update_version;
@ -284,7 +285,7 @@ export function update_reaction(reaction) {
(flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null);
active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
source_ownership = null; current_sources = null;
set_component_context(reaction.ctx); set_component_context(reaction.ctx);
untracking = false; untracking = false;
update_version = ++read_version; update_version = ++read_version;
@ -376,7 +377,7 @@ export function update_reaction(reaction) {
untracked_writes = previous_untracked_writes; untracked_writes = previous_untracked_writes;
active_reaction = previous_reaction; active_reaction = previous_reaction;
skip_reaction = previous_skip_reaction; skip_reaction = previous_skip_reaction;
source_ownership = previous_reaction_sources; current_sources = previous_sources;
set_component_context(previous_component_context); set_component_context(previous_component_context);
untracking = previous_untracking; untracking = previous_untracking;
update_version = previous_update_version; update_version = previous_update_version;
@ -550,10 +551,7 @@ export function get(signal) {
// we don't add the dependency, because that would create a memory leak // we don't add the dependency, because that would create a memory leak
var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0; var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0;
var is_owned_by_reaction = if (!destroyed && !current_sources?.includes(signal)) {
source_ownership?.reaction === active_reaction && source_ownership.sources.includes(signal);
if (!destroyed && !is_owned_by_reaction) {
var deps = active_reaction.deps; var deps = active_reaction.deps;
if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) { if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) {

Loading…
Cancel
Save