With Locks (Old):
Thread 1: Lock → Do Work → Unlock
Thread 2: [WAITING...] Lock → Do Work → Unlock ← Blocked!
One thread blocks others.
Lock-Free (New):
Thread 1: Do Work (atomic operations)
Thread 2: Do Work (atomic operations) ← No blocking!
Multiple threads work simultaneously.
Benefits:
- No waiting - threads never block each other
- Lower overhead - eliminates mutex lock/unlock operations
- Scalable - performance improves with more cores
-
Lock-Free Message Pools
- TLS pools have no mutex
- Zero contention on hot path
- Automatic per-thread allocation
-
Lock-Free Mailboxes
- SPSC (Single Producer Single Consumer) atomic queue
- 64-byte cache line padding
- Runtime switchable
-
Runtime Configuration API
- Control optimizations via flags
- Auto-detect CPU features
- Enabled by default without manual configuration
#include "runtime/aether_runtime.h"
int main() {
// One line - automatically enables best optimizations
aether_runtime_init(0, AETHER_FLAG_AUTO_DETECT);
// Your code here...
aether_runtime_shutdown();
}Example output on a modern CPU:
- Lock-free mailboxes: ENABLED
- Lock-free pools: ENABLED
- MWAIT idle: ENABLED
- AVX2 SIMD: ENABLED
// Explicit flags - full control
aether_runtime_init(8,
AETHER_FLAG_LOCKFREE_MAILBOX | // Use lock-free mailboxes
AETHER_FLAG_LOCKFREE_POOLS | // Use lock-free TLS pools
AETHER_FLAG_ENABLE_SIMD | // Use AVX2 vectorization
AETHER_FLAG_ENABLE_MWAIT); // Use MWAIT for idle// No optimizations - works on any CPU
aether_runtime_init(4, 0); // 4 cores, no flags// Print configuration on startup
aether_runtime_init(0,
AETHER_FLAG_AUTO_DETECT |
AETHER_FLAG_VERBOSE);
// Output:
// ========================================
// Aether Runtime Configuration
// ========================================
// CPU: <detected processor>
// Active Optimizations:
// Lock-free mailbox: ENABLED
// Lock-free pools: ENABLED
// ...| Flag | Hex | Description |
|---|---|---|
AETHER_FLAG_AUTO_DETECT |
0x01 | Auto-detect CPU features (recommended) |
AETHER_FLAG_LOCKFREE_MAILBOX |
0x02 | Use lock-free SPSC mailboxes |
AETHER_FLAG_LOCKFREE_POOLS |
0x04 | Use lock-free TLS message pools |
AETHER_FLAG_ENABLE_SIMD |
0x08 | Enable AVX2 vectorization |
AETHER_FLAG_ENABLE_MWAIT |
0x10 | Enable MWAIT idle strategy |
AETHER_FLAG_VERBOSE |
0x20 | Print configuration on init |
Combine flags with bitwise OR:
int flags = AETHER_FLAG_LOCKFREE_MAILBOX |
AETHER_FLAG_LOCKFREE_POOLS;
aether_runtime_init(8, flags);Performance varies by hardware and workload. Use the profiling tools to measure throughput on your target platform.
See benchmarks/cross-language for detailed measurements.
Actors are produced by the Aether compiler: an actor block becomes a struct
carrying a mailbox, and the runtime flags below decide which mailbox and pool
implementations it uses. A C program embedding the runtime configures it and
then drives compiler-generated actors, or its own structs built on the mailbox
API in runtime/actors/actor_state_machine.h.
#include "runtime/aether_runtime.h"
#include "runtime/actors/actor_state_machine.h"
typedef struct Counter {
int id;
atomic_int active;
Mailbox mailbox;
int count;
} Counter;
int main() {
// Initialize with auto-detect
aether_runtime_init(0, AETHER_FLAG_AUTO_DETECT);
Counter c = {0};
mailbox_init(&c.mailbox);
Message msg = {1, 0, 42, NULL};
mailbox_send(&c.mailbox, msg);
if (mailbox_receive(&c.mailbox, &msg)) {
c.count++;
}
aether_runtime_shutdown();
return 0;
}const AetherRuntimeInitConfig* config = aether_runtime_get_config();
if (config->use_lockfree_mailbox) {
printf("Using lock-free mailboxes!\n");
}
if (config->use_simd) {
printf("AVX2 SIMD enabled\n");
}if (aether_runtime_has_feature(AETHER_FLAG_ENABLE_MWAIT)) {
printf("MWAIT is enabled\n");
}Use the verbose flag to see detected CPU features at startup:
aether_runtime_init(0, AETHER_FLAG_AUTO_DETECT | AETHER_FLAG_VERBOSE);Build and run the C example under runtime/examples/:
gcc -O2 -o runtime_config runtime/examples/runtime_config_example.c \
runtime/aether_runtime.c runtime/config/aether_optimization_config.c \
runtime/utils/aether_cpu_detect.c -Iruntime
./runtime_configEvery actor carries one Mailbox (the ring buffer in
runtime/actors/actor_state_machine.h).
The send path in
runtime/actors/aether_send_message.c
picks how it gets there:
// aether_send_message(), abridged
const int my_core = current_core_id;
if (my_core >= 0 && my_core == atomic_load(&actor->assigned_core)) {
scheduler_send_local(actor, msg); // same core: lock-free SPSC queue
} else {
scheduler_send_remote(actor, msg, my_core);
}A single-actor program takes an earlier path still: main-thread mode runs
step() inline on the sender's thread rather than queueing at all. The SPSC
queue is allocated on first use (ensure_spsc_queue in
runtime/scheduler/multicore_scheduler.c),
so an actor that never receives a same-core send does not pay for one.
// runtime/actors/aether_message_pool.h, per-thread pool, abridged
static inline Message* message_pool_alloc(void) {
if (!g_msg_pool) message_pool_init_thread();
if (atomic_load_explicit(&g_msg_pool->count, memory_order_relaxed) == 0) {
return malloc(sizeof(Message)); // pool exhausted: fall back to heap
}
// pop an index off the thread's free list
}| Variable | Effect |
|---|---|
AETHER_NO_INLINE |
Disables Main Thread Actor Mode (forces scheduler-based processing) |
AETHER_INLINE |
Forces Main Thread Actor Mode even with multiple actors |
AETHER_SINGLE_CORE |
Pins all actors to core 0 (eliminates cross-core overhead) |
AETHER_PROFILE |
Memory profile: micro, small, medium (default), large |
AETHER_MSG_POOL_SIZE |
Override message pool size (default from profile) |
AETHER_VERBOSE |
Print optimization configuration at startup |
AETHER_PREEMPT |
Enable cooperative preemption (time-based drain loop break, default 1ms) |
AETHER_PREEMPT_MS |
Override preemption threshold in milliseconds (default 1) |
BENCHMARK_MESSAGES |
Sets message count for benchmark programs |
- Always use
AETHER_FLAG_AUTO_DETECTunless you have a specific reason not to - Use
AETHER_FLAG_VERBOSEduring development to see what's enabled - Don't recompile for different CPUs - auto-detect handles it
- Test on target hardware to verify optimizations are available
- Profile first - measure actual performance gains
| Platform | Lock-Free | MWAIT | SIMD | Thread Affinity | Status |
|---|---|---|---|---|---|
| Intel x86 (2013+) | Yes | Yes | AVX2 | Hard | Full support |
| AMD Ryzen | Yes | Yes | AVX2 | Hard | Full support |
| Apple Silicon (M1/M2/M3) | Yes | No (WFE) | NEON | Advisory | P-cores only |
| Old x86 (pre-2013) | Yes | No | SSE4.2 | Hard | Partial |
| ARM Linux | Yes | No (WFE) | NEON | Hard | Full support |
| Windows | Yes | Yes | AVX2 | Hard | Full support |
| WebAssembly (Emscripten) | N/A | No | No | No | Cooperative scheduler |
| Embedded (ARM bare-metal) | N/A | No | No | No | Cooperative scheduler |
With AETHER_FLAG_AUTO_DETECT, the runtime enables the optimizations each platform supports.
On platforms without pthreads (WASM, embedded), or when threading is explicitly disabled (-DAETHER_NO_THREADING), the cooperative scheduler handles all actor processing on a single thread. Multi-actor programs work correctly, messages are processed cooperatively during wait_for_idle().
# Force cooperative mode on native (for testing):
make stdlib EXTRA_CFLAGS="-DAETHER_NO_THREADING"
ae run examples/actors/counter.ae
# Build for WebAssembly:
make stdlib PLATFORM=wasmAll platform-dependent code is gated behind AETHER_HAS_* compile-time flags defined in runtime/config/aether_optimization_config.h. These are auto-detected but can be overridden:
# Disable specific features
make stdlib EXTRA_CFLAGS="-DAETHER_NO_FILESYSTEM -DAETHER_NO_NETWORKING"
# Query capabilities at runtime
AETHER_VERBOSE=1 ./programApple Silicon Notes:
- P-cores (Performance) detected via
hw.perflevel0.physicalcpu - E-cores (Efficiency) excluded for consistent throughput
- QoS hints encourage P-core scheduling
- Thread affinity is advisory; macOS may migrate threads
Solution: Use AETHER_FLAG_VERBOSE to see why:
aether_runtime_init(0, AETHER_FLAG_AUTO_DETECT | AETHER_FLAG_VERBOSE);Check:
- CPU actually supports features (use
AETHER_FLAG_VERBOSEto see detected capabilities) - Flags are set correctly
- Actors created after
aether_runtime_init()
Reason: Different CPU capabilities
Solution: Use AETHER_FLAG_AUTO_DETECT - it adapts automatically