Skip to content

Worker - #29

Open
tmcgrath325 wants to merge 3 commits into
masterfrom
tmm/worker-dispatch
Open

Worker#29
tmcgrath325 wants to merge 3 commits into
masterfrom
tmm/worker-dispatch

Conversation

@tmcgrath325

Copy link
Copy Markdown

This closes #28 and implements the fixes suggested there.

Dispatching on `threadid()` inside `@threads :dynamic` made a worker's
`workertid` decide which images reached it, with two consequences.

Images whose task ran on a thread outside the set of worker ids were skipped
by the `tid in tpool` guard: never registered, their slice of the output never
written, and no error raised. Worker ids that match no thread -- `tid = i` over
`1:nthreads()`, say, when the scheduler hands out 2:17 -- lost every image.

Workers could also be shared. Tasks spawned per image interleave on a thread
at any yield point, so two running concurrently may observe the same id and
register against the same worker object and the same monitor dict, both of
which they mutate in place. Collisions grow with thread count; registering a
200-image stack across 16 threads showed 39 pairs of images handled at once by
one worker.

`driver` now spawns one task per worker, each taking the next unclaimed image
from an atomic counter, so a worker is only ever in use by one task and needs
no locking. Images are distributed independently of `workertid`, and
`algorithms` may have any length.

Two testsets cover this. "images are distributed independently of workertid"
gives the workers ids matching no thread and requires all 64 images to be
registered; it reported 0 under the old dispatch. "workers are used
exclusively" checks that `AlgExclusive` is never entered while already busy --
an invariant guard rather than a reproduction, since a dummy worker yields too
briefly to collide reliably.

The existing multi-worker test asserted that every worker handled at least one
image. That holds only when images outnumber workers -- it already failed on
more than seven threads -- and image distribution is now dynamic, so it
instead checks that every image was registered by one of the workers supplied.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
`driver` called `init!` and `close!` on `algorithms[1]` only, so workers 2:end
registered images without their per-worker resources ever being set up, and
whatever the first worker acquired was the only thing released. Algorithms with
no-op lifecycle methods were unaffected; those holding device contexts or
scratch buffers were not.

`AlgLifecycle` records its own lifecycle calls and flags any image that reaches
it before `init!`.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.66%. Comparing base (54faa5d) to head (dd0a4f8).

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #29      +/-   ##
==========================================
- Coverage   91.74%   91.66%   -0.08%     
==========================================
  Files           1        1              
  Lines         109      108       -1     
==========================================
- Hits          100       99       -1     
  Misses          9        9              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Lets consumers require the parallel `driver` that gives each worker its own
task and initializes every worker, rather than resolving to a version whose
thread-id dispatch shares workers between concurrent tasks and silently skips
frames.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

driver can hand one worker to two concurrent tasks

1 participant