Skip to content

fix(examples): handle N jobs and job errors in thread-queue-timer; make three checks falsifiable - #478

Merged
TMHSDigital merged 1 commit into
mainfrom
fix/timers-threading-and-check-falsifiers
Oct 7, 2026
Merged

TMHSDigital merged 1 commit into
mainfrom
fix/timers-threading-and-check-falsifiers

Conversation

@TMHSDigital

Copy link
Copy Markdown
Owner

Closes #463
Closes #470

What changed

#463: thread-queue-timer handles N jobs and job errors

  • snippets/thread-queue-timer.py and skills/timers-modal-and-threading/SKILL.md now keep a pending-job counter. Each tick takes every result that has arrived, and the drain unregisters only when the count reaches 0. The worker wraps job() in try/except BaseException and puts (False, e), so the main thread reports the error and the drain never polls forever.
  • examples/timers-modal-threading runs that pattern with three concurrent jobs, one of which raises ValueError. It asserts that both results are applied, the error is reported, the pending count reaches 0 and the drain unregisters itself.

#470: three examples with checks that passed by construction or could not fire

  • image-pixels-testcard: the byte image is saved with Image.save() and the PNG is decoded with zlib + struct only, no bpy. Every PNG row k must match card row H-1-k, and the origin marker must be in the file's last row. This is new exit 13. --wrong-origin writes the flipped buffer through Blender and now expects 13. The foreach round trip (exit 4) no longer claims to cover orientation.
  • extension-package-lifecycle: --validate-only, which could only reach exit 4 by tautology, is replaced by --ship-wheel. That flag creates the wheel the manifest names, so build succeeds and the check must exit 4.
  • timers-modal-threading:
    • The main-thread check compares threading.get_ident() recorded in each worker against the ident of the code that touches bpy. The old current_thread() is main_thread() check ran inside a timer, so it was always true.
    • still_registered is read before read_factory_settings. Before, the file load had already dropped every plain timer, so the read was always False.
    • Exits 3, 6 and 7 each get a falsifier.

New catalog falsifiers for timers-modal-threading:

Flag Exit What breaks
--windowed-background-child 3 timer child gets an event loop, timer fires
--return-zero-once 5 run-once count (existing)
--return-late-once 5 count stays 1, but is_registered (read before the load) is True
--apply-in-worker 6 applied ident is the worker's
--no-event-timer 7 no TIMER events, modal never finishes
--non-persistent 8 existing
--stop-after-first 9 the old drain: 1 of 2 results applied, 2 jobs left pending
--no-catch 12 the old worker: no error reported, pending stays 1, drain still registered

Exit codes 2, 10 and 11 are not used for any contract.

Evidence

Live run (local, via tests/smoke/run_catalog.py on a scratch catalog of these three rows):

  • Blender 5.2.1 LTS (.scratch/blender-5.2.1-windows-x64/blender.exe, reports 5.2.1 LTS hash 9e2066aef7ef): all three happy paths PASS, and every falsifier exits exactly as declared.
  • Blender 4.5.11 LTS (.scratch/blender-4.5.11-windows-x64/blender.exe, reports 4.5.11 LTS hash 4db51e9d1e1e): all three happy paths PASS, and every falsifier exits exactly as declared.
    • One 4.5 run of --apply-in-worker exited 4 first. Its windowed child logged "Saved session recovery to quit.blend" without writing its JSON, which is what closing the Blender window does, and the window opened on the user's desktop. I infer the window was closed by hand. A clean re-run exited 6 as declared.

Measured falsifier output:

  • image-pixels, happy path: PNG rows vs bottom-up card 0.0019608 (tol 0.0019618), marker in PNG rows 247..287 of 0..287.
  • --wrong-origin: row error 0.9216, marker in rows 0..40, exit 13 (5.2.1 and 4.5.11).
  • --ship-wheel: validate exit 0, build exit 0, zip written, exit 4 (5.2.1 and 4.5.11).
  • --return-zero-once: once ran 30x on 5.2.1 and 21x on 4.5.11, exit 5.
  • --return-late-once: once=1, registered=[True, False], exit 5.
  • --apply-in-worker: handled on the worker idents, exit 6.
  • --no-event-timer: ticks=0, result=None, exit 7.
  • --stop-after-first: got {'heights': 3} vs expected heights+labels, exit 9.
  • --no-catch: errors=[], pending=1, drain registered, exit 12.

Inspection only: Blender 5.1 was not re-run locally. The README tables say so, and the weekly cron covers it. CI xvfb runs the full matrix on 5.2 and 4.5.

Validators: python tests/run_all.py: 31 passed, 0 failed. scripts/build_examples_index.py and scripts/build_gallery.py were re-run. I read the regenerated docs/gallery/image-pixels-testcard/index.html: the new list item, the exit-13 row and the img alt are correct. examples/gallery.json is unchanged.

🤖 Generated with Claude Code

…ke three checks falsifiable

The thread-queue-timer pattern stopped its drain after the first result,
leaving a second job's result stranded, and a raising job left the timer
polling forever. The snippet and skill now count pending jobs, drain
everything per tick, and have the worker put the exception.

Three examples carried checks that passed by construction: the
image-pixels orientation check compared two Python lists, the extension
--validate-only falsifier reached exit 4 by tautology, and the timers
main-thread and still-registered checks could never fire. Each now
fails only through the Blender behavior it claims, with a catalog
falsifier per check.

Closes #463
Closes #470

Signed-off-by: TMHSDigital <tmhospitalitystrategies@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added skills snippets examples Runnable smoke-gated examples under examples/ documentation Improvements or additions to documentation labels Oct 7, 2026
@TMHSDigital
TMHSDigital merged commit 4aeb805 into main Oct 7, 2026
13 checks passed
@TMHSDigital
TMHSDigital deleted the fix/timers-threading-and-check-falsifiers branch October 7, 2026 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation examples Runnable smoke-gated examples under examples/ skills snippets

Projects

None yet

1 participant