Skip to content

Commit 14b0926

Browse files
authored
fix: don't deactivate other batches (#17132)
There's a possibility of a race condition where `batch.deactivate()` is called while `current_batch !== batch`, and so it would deactivate another batch, which can lead to zombie batches that are never flushed, subsequently leading to wrong `batch_values`. This fixes that by checking if the current batch is the own batch. Fixes #17109
1 parent 554202e commit 14b0926

File tree

5 files changed

+61
-0
lines changed

5 files changed

+61
-0
lines changed

.changeset/eleven-moons-smash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: don't deactivate other batches

packages/svelte/src/internal/client/reactivity/batch.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ export class Batch {
299299
}
300300

301301
deactivate() {
302+
// If we're not the current batch, don't deactivate,
303+
// else we could create zombie batches that are never flushed
304+
if (current_batch !== this) return;
305+
302306
current_batch = null;
303307
batch_values = null;
304308
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
let { ref } = $props();
3+
4+
let tick = $state(0);
5+
let ref_exists = $state(true);
6+
7+
$effect(() => {
8+
tick;
9+
ref_exists = ref !== null;
10+
});
11+
</script>
12+
13+
<p>{ref_exists}</p>
14+
<button onclick={() => tick++}>check</button>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
// This test regresses against batches deactivating other batches than themselves
5+
export default test({
6+
async test({ assert, target }) {
7+
await tick(); // settle initial await
8+
9+
const button = target.querySelector('button');
10+
11+
button?.click();
12+
await tick();
13+
assert.htmlEqual(
14+
target.innerHTML,
15+
`
16+
<div>div</div>
17+
<p>true</p>
18+
<button>check</button>
19+
<p></p>
20+
`
21+
);
22+
}
23+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
import Component from "./Component.svelte";
3+
4+
let ref = $state(null);
5+
6+
let foo = $derived(await 1);
7+
</script>
8+
9+
<div bind:this={ref}>div</div>
10+
11+
<Component {ref} />
12+
13+
{#if foo}
14+
<p></p>
15+
{/if}

0 commit comments

Comments
 (0)