feat(jobs): real OS threads - Job.parallel_for, fn name, thread-safe Sync
- `fn name` names a top-level function as a value (E_FNREF, lowers to @fn_<name>); the worker entry point for Job.parallel_for, which checks it takes (int, pointer-like) and returns void. - runtime/native/threads.ll (pthreads) and threads_win.ll (Win32 SRWLOCK/CONDITION_VARIABLE): a pool of one worker per core but one, parked between batches; every thread claims chunks by compare-and-swap. Linked only into programs that use Job/Promise/Sync, by `ludicc -o`, `ludic build` and the test suite's build helper. - Sync.* is real: native mutexes, atomics as cmpxchg retry loops (neither clang takes atomicrw, the PC's rejects seq_consistent), mutex-guarded channels, Sync.cpu_count from the OS. - spawn/despawn on a pool thread stop the program with a located panic. - examples/library/threads.ludic and its test; docs for fn, Job.parallel_for, Job.is_worker. - Reseeded (bootstrap-cfree: out.ll == seed.ll). 141/141 on macOS; jobs, threads and the guard pass on Windows from the reseeded Windows seed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f5d1a62ccf
commit
c10abd9f9f
22 changed files with 57464 additions and 55794 deletions
|
|
@ -118,7 +118,27 @@ function prefab_record(name: pointer, cn: pointer) -> Node {
|
|||
return merge_records(prefab_record(p.ty, cn), spawn_record(p, cn))
|
||||
}
|
||||
|
||||
# The threads rule's debug guard: a Job.parallel_for worker computes on what it was handed and
|
||||
# never changes the world, so spawn and despawn stop the program when a pool thread reaches them.
|
||||
# Emitted only into programs that use Job/Promise/Sync, which are the only ones with a pool.
|
||||
function emit_worker_guard(what: pointer, line: int) -> void {
|
||||
if not g_uses_jobs { return }
|
||||
g_uses_panic = true
|
||||
let w = emit_bind("call i32 @thr_is_worker()")
|
||||
let on = emit_bind(`icmp ne i32 {w}, 0`)
|
||||
let lok = lbl("wgok"); let lbad = lbl("wgbad")
|
||||
emit(` br i1 {on}, label %{lbad}, label %{lok}\n`)
|
||||
emit(`{lbad}:\n`)
|
||||
let prefix = emit_str_const(`{g_src_name}:{itoa(line)}: panic: `)
|
||||
let m = emit_str_const(`{what} on a Job.parallel_for worker thread (workers must not change the world)`)
|
||||
let se = emit_bind(stdstream_rhs(2))
|
||||
emit(` call i32 (ptr, ptr, ...) @fprintf(ptr {se}, ptr @.fmt_panic, ptr {prefix}, ptr {m})\n`)
|
||||
emit(" call void @exit(i32 1)\n unreachable\n")
|
||||
emit(`{lok}:\n`)
|
||||
}
|
||||
|
||||
function emit_spawn(st: Node) -> pointer {
|
||||
emit_worker_guard("spawn", st.line)
|
||||
let e = emit_bind("call i32 @L_alloc()")
|
||||
let model = spawn_model(st.s)
|
||||
let ak = find_arch_id(model)
|
||||
|
|
@ -153,6 +173,7 @@ function emit_spawn(st: Node) -> pointer {
|
|||
}
|
||||
|
||||
function emit_despawn(st: Node) -> void {
|
||||
emit_worker_guard("despawn", st.line)
|
||||
let v = emit_expr(st.a)
|
||||
# @OnDespawn: dispatch on the entity's kind and run the matching model's hook
|
||||
if len(g_ondespawn) > 0 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue