2. Scheduling

Fiber Work Loop: Interruptible Unit-of-Work Traversal

midfrontend-internalsscheduling

Plain recursion can't be paused mid-traversal -- once you call render(child) from inside render(parent), you're committed to the whole subtree before control returns. React's fiber architecture solves this by replacing the call stack with an explicit linked structure: each fiber node points to its first child, its next sibling, and its parent (React calls this last one return). Walking the tree becomes a loop over "what's the next unit of work," which a scheduler can stop between any two calls to.

A fiber is a plain dict: {"id": ..., "parent": ..., "child": ..., "sibling": ...}.

Implement:

  • build_fiber_tree(spec, parent=None) -- spec is a nested dict {"id": ..., "children": [spec, ...]}. Build and return the root fiber with child/sibling/parent wired up (first child in children becomes child; the rest are chained via sibling).
  • perform_unit_of_work(fiber, visit_log) -- "begin work" on fiber (append ("begin", fiber["id"])). If it has a child, that's the next unit -- return it. Otherwise delegate to complete_unit_of_work.
  • complete_unit_of_work(fiber, visit_log) -- starting at fiber, repeatedly: append ("complete", node["id"]); if node has a sibling, return it (next unit); otherwise move to node["parent"] and repeat (the parent's work is ALSO now fully done, since its last child just finished). Reaching a node with no parent means the whole tree is done -- return None.
  • work_loop(root, visit_log, unit_budget=None) -- call perform_unit_of_work repeatedly, one unit per call, until the tree finishes or unit_budget units have run THIS call. Return the fiber to resume from (or None if finished) -- calling work_loop again with that returned fiber must continue exactly where it left off.

Example

spec = {"id": "root", "children": [
    {"id": "a", "children": [{"id": "a1"}, {"id": "a2"}]},
    {"id": "b"},
]}
root = build_fiber_tree(spec)
log = []
work_loop(root, log)
# log == [("begin","root"), ("begin","a"), ("begin","a1"), ("complete","a1"),
#         ("begin","a2"), ("complete","a2"), ("complete","a"),
#         ("begin","b"), ("complete","b"), ("complete","root")]

Note that completing a2 (the last child of a) immediately bubbles up and ALSO completes a in the same step -- that's not a bug, it's the real mechanism: a parent's work is only "complete" once its last child's subtree is.

Constraints

  • unit_budget counts calls to perform_unit_of_work, not begin/ complete log entries (one call can append several complete entries via the bubble-up loop).
  • Resuming with the fiber work_loop returned must produce a log identical to running the whole thing uninterrupted, just split across calls.

Real-world grounding

Real React's workLoopConcurrent checks shouldYield() (a real deadline tied to the browser's frame budget) between units instead of a fixed unit_budget, and beginWork/completeWork do actual rendering work (not just logging) -- this exercise implements the traversal shape both are built on.

hints
fiber_work_loop.pysign in to run
Loading...
test result

This is a preview. Sign in to run this exercise against the real tests.