Threading
XXML provides comprehensive multithreading support through high-level classes that wrap platform-native threading primitives, with compile-time safety through Sendable and Sharable constraints.
Quick Start
xxml
#import Language::Core;
#import Language::Concurrent;
[ Entrypoint
{
// Create and start a thread with a lambda
Instantiate F(None^)()^ As <work> = [ Lambda [] Returns None^ Parameters () {
Run Console::printLine(String::Constructor("Hello from thread!"));
Return None::Constructor();
}];
Instantiate Concurrent::Thread^ As <t> = Concurrent::Thread::spawn(work);
// Wait for thread to complete
Run t.join();
Run Console::printLine(String::Constructor("Thread finished!"));
Exit(0);
}
]Thread Safety Constraints
Sendable
Marks types safe to move across thread boundaries:
xxml
@Derive(trait = "Sendable")
[ Class <Message> Final Extends None
[ Public <>
Property <id> Types Integer^;
Property <content> Types String^;
Constructor = default;
]
]
// Message can be safely moved to another threadSharable
Marks types safe to share (reference) across threads:
xxml
@Derive(trait = "Sharable")
[ Class <Config> Final Extends None
[ Public <>
Property <maxConnections> Types Integer^;
// Immutable after construction
Constructor Parameters (Parameter <max> Types Integer^) -> {
Set maxConnections = max;
}
]
]Note
Sendable types cannot have reference (&) fields. Sharable types should be immutable after construction.Thread Class
| Method | Returns | Description |
|---|---|---|
spawn(func) | Thread^ | Create and start a new thread |
join() | Bool^ | Wait for thread to complete |
detach() | Bool^ | Let thread run independently |
sleep(ms) | None | Sleep current thread |
currentId() | Integer^ | Get current thread ID |
Mutex and LockGuard
xxml
Instantiate Concurrent::Mutex^ As <mutex> = Concurrent::Mutex::Constructor();
// Using LockGuard (RAII-style)
{
Instantiate Concurrent::LockGuard^ As <guard> = Concurrent::LockGuard::Constructor(mutex);
// Critical section - mutex is held
// ...
// Lock automatically released when guard goes out of scope
}Atomic Operations
xxml
Instantiate Concurrent::Atomic^ As <counter> = Concurrent::Atomic::Constructor();
// Increment atomically
Instantiate Integer^ As <newVal> = counter.increment();
// Add value
Run counter.add(Integer::Constructor(5));
// Compare and swap
Instantiate Bool^ As <success> = counter.compareAndSwap(
Integer::Constructor(6), // expected
Integer::Constructor(10) // desired
);Condition Variables
xxml
Instantiate Concurrent::Mutex^ As <mutex> = Concurrent::Mutex::Constructor();
Instantiate Concurrent::ConditionVariable^ As <cond> = Concurrent::ConditionVariable::Constructor();
Instantiate Concurrent::Atomic^ As <dataReady> = Concurrent::Atomic::Constructor();
// Consumer waits for data
Run mutex.lock();
While (dataReady.get().equals(Integer::Constructor(0))) -> {
Run cond.wait(mutex);
}
Run mutex.unlock();
// Producer signals data ready
Run mutex.lock();
Run dataReady.set(Integer::Constructor(1));
Run cond.signal();
Run mutex.unlock();Semaphore
xxml
// Pool of 2 resources
Instantiate Concurrent::Semaphore^ As <pool> = Concurrent::Semaphore::Constructor(
Integer::Constructor(2)
);
// Acquire a resource
Run pool.acquire();
// ... use the resource ...
Run pool.release();Complete Example: Parallel Counter
parallel-counter.xxml
#import Language::Core;
#import Language::Concurrent;
[ Entrypoint
{
Instantiate Concurrent::Atomic^ As <counter> = Concurrent::Atomic::Constructor();
Instantiate F(None^)()^ As <worker1> = [ Lambda [&counter] Returns None^ Parameters () {
Run Console::printLine(String::Constructor("Worker 1 starting"));
Run counter.increment();
Run counter.increment();
Run counter.increment();
Return None::Constructor();
}];
Instantiate F(None^)()^ As <worker2> = [ Lambda [&counter] Returns None^ Parameters () {
Run Console::printLine(String::Constructor("Worker 2 starting"));
Run counter.increment();
Run counter.increment();
Run counter.increment();
Return None::Constructor();
}];
Instantiate Concurrent::Thread^ As <t1> = Concurrent::Thread::spawn(worker1);
Instantiate Concurrent::Thread^ As <t2> = Concurrent::Thread::spawn(worker2);
Run t1.join();
Run t2.join();
// Final count should be 6
Run Console::printLine(String::Constructor("Final count: "));
Run Console::printLine(counter.get().toString());
Exit(0);
}
]Best Practices
- Use LockGuard: RAII-style locking prevents deadlocks from forgotten unlocks
- Prefer Atomics: For simple counters, atomics are faster than mutex locks
- Avoid Deadlocks: Always acquire locks in the same order
- Loop on Conditions: Always use while loops with condition variables to handle spurious wakeups
Class Reference
| Class | Description |
|---|---|
Thread | Execution thread with lambda support |
Mutex | Mutual exclusion lock |
LockGuard | RAII scoped lock |
Atomic | Thread-safe atomic integer |
ConditionVariable | Thread coordination/signaling |
Semaphore | Counting semaphore |
ThreadLocal<T> | Per-thread storage |
Next Steps
Learn about Derives for Sendable and Sharable constraints, or explore Lambdas for creating thread functions.