Macaulay2 / Macaulay2/M2

No way to wait for the first of a set of tasks to finish; wait is undefined on Task and isReady must be polled

Open
#4,650 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bugs directory Interpreter threads
Dominant language
Macaulay2
Stars
435
Forks
297
Avg merge
4d 20h
Merged PRs (30d)
11

Description


This issue was triaged from one request inside bugs/gfurnish/TODO, one of the 857 files removed from the pre-GitHub bugs/ tree by d2c8d27826 and catalogued in #36. That file is a wishlist holding several unrelated requests, so its asks were split apart and filed separately rather than as one issue nobody could close. The commentary below was written by Claude (Claude Opus 5, via Claude Code), not by @d-torrance, whose account posted it -- please weigh it accordingly.

The request, verbatim

figure out how to wait for a thread, or to wait for the first thread of a set of threads; and, further down the file, to wait for any thread in a set of threads to terminate (wait for pthread_cond_t?). Two entries, one ask, merged here.

The rest of the file is unrelated to this request; it is linked in the footer below.

Where it stands today

There is no way to wait for the first of several tasks to finish. taskResult waits for one specific task,
and the only other introspection is isReady, so racing several strategies and taking whichever answers
first means busy-polling — burning a core doing nothing while you wait.

What exists
i1 : t = schedule(() -> (2^100000; 42))

o1 = <<task, created>>

i2 : wait t
stdio:2:1:(3): error: expected an integer, or an input file or list of input files

i3 : wait {t}
stdio:3:1:(3): error: expected a list of input files or listeners, or a list of small non-negative integers

i4 : methods wait

o4 = {}

wait is defined on processes and on input files
(d/actors4.d:815-843)
and has no Task case. isReady does:

i5 : methods isReady

o5 = {0 => (isReady, File)}
     {1 => (isReady, Task)}

So the available idiom for "give me the first answer" is

while not any(tasks, isReady) do nothing;

which spins.

The machinery is already there, one level down

system/supervisorinterface.h:34
declares

extern void* waitOnTask(struct ThreadTask* task);	// wait until done or canceled

and ThreadSupervisor already owns a condition variable for the other direction — workers blocking until
work arrives
(system/supervisor.hpp:136-138):

  ///mutex for accessing lists
  pthreadMutex m_Mutex;
  ///new task ready to run
  pthread_cond_t m_TaskReadyToRunCondition;

I have read the header rather than the implementation, so I do not know how much work this is. waitOnTask
waits on a single task, and "wait until any of these is done" may need a second condition variable signalled
on task completion rather than only a binding for what is there. That is the first thing to check.

Why it is worth having

This is what makes the common parallel pattern unavailable at top level: start several strategies for the
same computation, take the first answer, cancel the rest. cancelTask already handles the second half.
Without a wait-for-any, the first half is a spin loop that competes with the very computations it is
waiting on.

A closing condition would be some spelling of wait that accepts a list of tasks and returns when at least
one is ready — the same shape wait already has for a list of input files, which returns the ones that are
ready.

Provenance

The removed file asks for this twice, in two separate places:

to wait for a thread, or to wait for the first thread of a set of threads

and, thirty lines later,

to wait for any thread in a set of threads to terminate (wait for pthread_cond_t?)

The parenthesis in the second is a guess at the mechanism, and it guesses right about what the supervisor
would end up using.

Neither #3957, which asks for a task manager, nor
#143, on parallel performance, covers this.

open · disposition issue · ask 9 of bugs/gfurnish/TODO · source of truth: bug-triage/asks.tsv

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by inspecting waitOnTask in system/supervisorinterface.h and the ThreadSupervisor condition-variable code in system/supervisor.hpp, then trace the existing wait bindings in d/actors4.d:815-843. Done means a wait form for a list of tasks returns when at least one task finishes, without busy-polling, while preserving the existing single-task behavior and supporting cancellation of remaining tasks.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, cpp
Domain
backend, operating-systems
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.