The thread statement
Threadon lets you spawn OS threads with the thread statement at module level (outside any function). A thread runs a block of code in parallel with the main program.
Syntax:
thread ThreadName() on True:
# thread body
print("running on a thread")
The parts:
ThreadName— an identifier you choose. It becomes the internal function name and appears in debug output.()— always empty parentheses. Threads take no arguments directly; they read and write module-level variables instead.on True— a boolean condition. The thread only spawns if this evaluates toTrueat the point of the statement.- The indented block — runs on the new thread if the condition is true.
Module-level threads are the whole story — a thread statement only exists at module scope. Each one spawns real OS threads using pthread_create, so they run in the background, in parallel with main(). main does not wait for them; the thread and the main function interleave according to the OS scheduler.
thread Worker() on True:
print("from the thread")
def main() -> Int32
print("from main")
return 0
That output is one possible ordering. The thread may print before main, after it, or between a pair of main's prints — whatever the scheduler decides. Don't rely on the order of output from different threads.
The thread statement is non-blocking. All spawned threads are automatically joined before the program exits (see below).
Sharing variables with threads
A thread body is compiled as a normal function and can read and write module-level variables directly — there is no copying. Module-level variables live in a global section, and both main and the threads see the same storage. A write in the thread is visible in main, and the other way round.
counter: Int32 = 0
thread Inc() on True:
counter = counter + 1
def main() -> Int32
print("counter after thread: " + String(counter))
return 0
The printed value is 0 or 1 depending on whether the thread has run yet — nothing guarantees one or the other.
Because counter = counter + 1 is a read-modify-write, two threads doing it at once can lose updates. Threadon has no locks, so sharing mutable state this way is only safe if at most one thread writes a given variable (or you accept lossy counters).
Module-level code is declarative. Only variable declarations, functions, structs, classes, and thread statements can live at the top of a file. Assignments such as counter = 1 outside a function are compile errors — writes must happen inside a function or a thread body.
Ending a thread early with thread_exit()
Inside a thread body you can call thread_exit() to stop the thread immediately. It acts like an early return from the thread's function.
thread ExitDemo() on True:
print("before exit")
thread_exit()
print("this never prints")
def main() -> Int32
print("main continues")
return 0
The on condition
The expression after on must evaluate to a Bool. It is checked at the moment the thread statement is reached. If false, the thread body is skipped entirely and no thread is created.
flag: Bool = False
thread Conditional() on flag:
print("won't run")
def main() -> Int32
print("no thread was spawned")
return 0
Thread joining and program exit
All threads spawned by thread statements are automatically joined when main() returns, before the process exits. You don't need to call any join function yourself.
The flow is:
- At the start of
main, module-level threads are spawned (non-blocking). main()runs, in parallel with those threads.- When
main()returns, the program waits for every spawned thread to finish, then exits.
So the program waits for all threads to complete before exiting. A thread that runs forever will hang the program — main returning does not stop the background threads; the ending join does, by never returning.
The join only happens at the very end. While main is running, the threads are genuinely in the background. A long-running thread (say, a loop that prints or reads input) runs alongside main, not after it.
Multiple threads
You can spawn as many module-level threads as you like. They all run concurrently (subject to OS scheduling).
thread T1() on True:
print("thread 1")
thread T2() on True:
print("thread 2")
thread T3() on True:
print("thread 3")
def main() -> Int32
print("main done")
return 0
The order of output from different threads is non-deterministic, and main's own prints interleave with them.
Current limitations
- Threads are only supported at module level (outside functions).
- No user-level synchronization primitives yet (mutexes, channels, locks). A single variable assignment compiles to an atomic load or store, but a read-modify-write like
counter += 1is not atomic — racing threads can lose updates. - Threads cannot return values to the parent; communicate through module-level variables.
- No way to wait for a specific thread from inside another thread.
- Thread stacks are not configurable; they use the OS default.
See the Limits page for more things that don't work yet.