Skip to content

Commit 098ccd9

Browse files
committed
feat(cursors): fix infinity loop and finding next dirty child
1 parent 29e8da1 commit 098ccd9

File tree

3 files changed

+26
-24
lines changed

3 files changed

+26
-24
lines changed

packages/qwik/src/core/shared/cursor/chore-execution.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ export function executeComponentChore(vNode: VNode, container: Container): Value
165165
const isServer = isServerPlatform();
166166
const props = container.getHostProp<Props | null>(host, ELEMENT_PROPS) || null;
167167

168+
vNode.dirty &= ~ChoreBits.COMPONENT;
169+
168170
const result = safeCall(
169171
() => executeComponent(container, host, host, componentQRL, props),
170172
(jsx) => {
@@ -188,9 +190,6 @@ export function executeComponentChore(vNode: VNode, container: Container): Value
188190
}
189191
);
190192

191-
// Clear the COMPONENT bit after execution
192-
vNode.dirty &= ~ChoreBits.COMPONENT;
193-
194193
if (isPromise(result)) {
195194
return result as Promise<void>;
196195
}

packages/qwik/src/core/shared/cursor/cursor-queue.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,15 @@ export function getHighestPriorityCursor(): Cursor | null {
5151
* @param cursor - The cursor to remove
5252
*/
5353
export function removeCursorFromQueue(cursor: Cursor): void {
54+
cursor.flags &= ~VNodeFlags.Cursor;
5455
const index = globalCursorQueue.indexOf(cursor);
5556
if (index !== -1) {
5657
// Move last element to the position of the element to remove, then pop
5758
const lastIndex = globalCursorQueue.length - 1;
5859
if (index !== lastIndex) {
5960
globalCursorQueue[index] = globalCursorQueue[lastIndex];
6061
}
61-
const removed = globalCursorQueue.pop();
62-
if (removed) {
63-
removed.flags &= ~VNodeFlags.Cursor;
64-
}
62+
globalCursorQueue.pop();
6563
}
6664
}
6765

packages/qwik/src/core/shared/cursor/cursor-walker.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,13 @@ export function processCursorQueue(
6565
): void {
6666
isNextTickScheduled = false;
6767

68+
console.log('processCursorQueue');
69+
6870
let cursor: Cursor | null = null;
6971
while ((cursor = getHighestPriorityCursor())) {
72+
console.log('while loop');
7073
walkCursor(cursor, options);
71-
if (!cursor.dirty) {
74+
if (!(cursor.dirty & ChoreBits.DIRTY_MASK)) {
7275
removeCursorFromQueue(cursor);
7376
}
7477
}
@@ -90,6 +93,7 @@ function findContainerForCursor(cursor: Cursor): Container {
9093
}
9194
return getDomContainer(element!);
9295
}
96+
let globalCount = 0;
9397

9498
/**
9599
* Walks a cursor through the vDOM tree, processing dirty vNodes in depth-first order.
@@ -127,13 +131,18 @@ export function walkCursor(cursor: Cursor, options: WalkOptions): void {
127131
return;
128132
}
129133

134+
globalCount++;
135+
if (globalCount > 100) {
136+
throw new Error('Infinite loop detected in cursor walker');
137+
}
138+
130139
const container = findContainerForCursor(cursor);
131140
// Get starting position (resume from last position or start at root)
132141
let currentVNode: VNode | null = null;
133142

134143
let count = 0;
135144
while ((currentVNode = getCursorPosition(cursor))) {
136-
if (count++ > 500000) {
145+
if (count++ > 100) {
137146
throw new Error('Infinite loop detected in cursor walker');
138147
}
139148
// Check time budget (only for DOM, not SSR)
@@ -185,7 +194,7 @@ export function walkCursor(cursor: Cursor, options: WalkOptions): void {
185194
if (result && isPromise(result)) {
186195
// Store promise on cursor and pause
187196
setVNodePromise(cursor, result);
188-
// pauseCursor(cursor, currentVNode);
197+
pauseCursor(cursor, currentVNode);
189198

190199
result
191200
.then(() => {
@@ -218,33 +227,33 @@ export function walkCursor(cursor: Cursor, options: WalkOptions): void {
218227
/** @returns Next vNode to process, or null if traversal is complete */
219228
function getNextVNode(): VNode | null {
220229
if (dirtyChildrenStack.length > 0) {
221-
const last = dirtyChildrenStack[dirtyChildrenStack.length - 1];
222-
const lastIndex = ++last.index;
230+
const nextItem = dirtyChildrenStack[dirtyChildrenStack.length - 1];
223231

224-
const dirtyChildren = last.parent.dirtyChildren;
232+
const dirtyChildren = nextItem.parent.dirtyChildren;
225233
if (!dirtyChildren) {
226234
return null;
227235
}
236+
let currentIndex = nextItem.index++;
228237

229-
if (lastIndex < dirtyChildren.length) {
230-
const nextVNode = dirtyChildren[lastIndex];
238+
if (currentIndex < dirtyChildren.length) {
239+
const nextVNode = dirtyChildren[currentIndex];
231240
if (nextVNode.dirty) {
232241
return nextVNode;
233242
} else {
234-
const found = findDirtyChild(last, lastIndex, dirtyChildren.length);
243+
const found = findDirtyChild(nextItem, currentIndex);
235244
if (found) {
236245
return found;
237246
}
238247
}
239248
} else {
240249
// no more dirty children, wrap around to check earlier children
241-
const found = findDirtyChild(last, 0, last.index);
250+
const found = findDirtyChild(nextItem, 0);
242251
if (found) {
243252
return found;
244253
}
245254
dirtyChildrenStack.pop();
246255
// children are no longer dirty
247-
last.parent.dirty &= ~ChoreBits.CHILDREN;
256+
nextItem.parent.dirty &= ~ChoreBits.CHILDREN;
248257
return getNextVNode();
249258
}
250259
}
@@ -260,13 +269,9 @@ function getNextVNode(): VNode | null {
260269
* @param end - End index (exclusive)
261270
* @returns The first dirty child found, or null if none found
262271
*/
263-
function findDirtyChild(
264-
last: { parent: VNode; index: number },
265-
start: number,
266-
end: number
267-
): VNode | null {
272+
function findDirtyChild(last: { parent: VNode; index: number }, start: number): VNode | null {
268273
const dirtyChildren = last.parent.dirtyChildren!;
269-
for (let i = start; i < end; i++) {
274+
for (let i = start; i < dirtyChildren.length; i++) {
270275
const child = dirtyChildren[i];
271276
if (child.dirty) {
272277
last.index = i;

0 commit comments

Comments
 (0)