Zulip Chat Archive
Stream: lean4
Topic: Nondeterministic number of threads created
Thomas Murrills (Apr 14 2025 at 04:17):
Is this expected behavior? When running the following under lean --run <filename>
on nightly (and earlier, e.g. 4.17.0), I sometimes get 7 total threads, sometimes 12, sometimes 5...
def foo (x : Nat) : IO Unit := do
IO.println s!"Starting task {x}…"
IO.sleep ((x.toUInt32 + 1) * 30000)
IO.println s!"Finishing task {x}…"
def main : IO Unit := do
for n in [:10] do
let _ ← (foo n).asTask
IO.println "Done main!"
When it starts with a certain number of threads, that number never increases; it will wait until a task is done to start the next.
Example output for e.g. 7 total threads (2 basic threads, plus one for each running task, I suppose) is
Output
Thomas Murrills (Apr 14 2025 at 04:21):
I'll also note that using e.g. lean --threads=10 --run <filename>
produces the same nondeterministic behavior, which seems strange to me.
Thomas Murrills (Apr 14 2025 at 04:36):
Also (perhaps more importantly): I hit a segfault when trying .asTask (prio := .dedicated)
here without supplying --threads=10
; and when I do supply --threads=10
, I see Starting task n...
in the terminal but not Finished task n...
.
The latter might just be me not having the right expectations, but I hope segfaults during innocent attempts at thread management aren't intentional! :sweat_smile:
Thomas Murrills (Apr 14 2025 at 04:38):
(Sometimes using prio := .dedicated
without a --threads
argument is a segfault immediately after Done main!
, but sometimes it's this:
error
and sometimes the output is simply
Done main!
Starting task 0…
(or just Done main!
) with no apparent error.)
Thomas Murrills (Apr 14 2025 at 04:50):
I suppose this might be two (or more) separate issues discovered while attempting to reach the same goal (namely, consistently create 10 concurrent task threads), so apologies if it turns out they don't share the same root cause! :)
Sebastian Ullrich (Apr 14 2025 at 07:54):
This might be a corner case at the end of main
so try waiting for the tasks
Thomas Murrills (Apr 15 2025 at 00:55):
To update this topic, the segfault and error messages with (prio := dedicated)
were indeed caused by main
not waiting for dedicated tasks by default, and this has now been changed in lean4#7958 thanks to quick work by @Henrik Böving :)
Thomas Murrills (Apr 15 2025 at 00:58):
The only remaining issues in this topic are now
- the number of threads in the thread pool appears to be nondeterministic, and not as large as it could/should be
- using
--threads
does not change that behavior
Sebastian Ullrich (Apr 15 2025 at 06:36):
I believe that, too, will be resolved if you wait for your tasks!
Thomas Murrills (Apr 15 2025 at 07:32):
To be clear I do mean when not using prio := .dedicated
, which was an attempt to work around the thread nondeterminism issue! :) (Waiting does indeed resolve the issue when using prio := .dedicated
.)
Thomas Murrills (Apr 15 2025 at 07:33):
Just to cover all bases, though, waiting does not seem to resolve issues 1 and 2 (assuming I haven't done anything wrong):
def foo (x : Nat) : IO Unit := do
IO.println s!"Starting task {x}…"
IO.sleep ((x.toUInt32 + 1) * 3000)
IO.println s!"Finishing task {x}…"
def main : IO Unit := do
let mut tasks := Array.mkEmpty 10
for n in [:10] do
tasks := tasks.push <|← (foo n).asTask
IO.println "Started all tasks."
for task in tasks do
let _ ← IO.wait task
IO.println "Done main!"
The following run used --threads=10
but had 7 total threads (note that task 5 doesn't start until another task ends).
Output
Last updated: May 02 2025 at 03:31 UTC