typetui 0.2.1

A terminal-based typing test.
Documentation
#import "Thread";

global_counter: int;
global_mutex: Mutex;

main :: () {
    print("=== Basic Threading ===\n");
    basic_threading();
    
    print("\n=== Mutex Example ===\n");
    mutex_example();
    
    print("\n=== Thread Pool ===\n");
    thread_pool_example();
    
    print("\n=== Work Queue ===\n");
    work_queue_example();
    
    print("\n=== Async/Await Pattern ===\n");
    async_await_example();
    
    print("\n=== Atomic Operations ===\n");
    atomic_example();
}

thread_func :: (data: *void) -> *void #c_call {
    id := cast(int) data;
    print("Thread % starting\n", id);
    
    for 0..4 {
        print("Thread %: iteration %\n", id, it);
        sleep_milliseconds(100);
    }
    
    print("Thread % finished\n", id);
    return null;
}

basic_threading :: () {
    thread: Thread;
    
    thread_init(*thread, thread_func, cast(*void) 1);
    thread_start(*thread);
    
    for 0..4 {
        print("Main: iteration %\n", it);
        sleep_milliseconds(100);
    }
    
    thread_join(*thread);
    thread_deinit(*thread);
    
    print("All threads completed\n");
}

mutex_example :: () {
    global_counter = 0;
    mutex_init(*global_mutex);
    
    threads: [4]Thread;
    
    for 0..3 {
        thread_init(*threads[it], increment_worker, cast(*void) it);
        thread_start(*threads[it]);
    }
    
    for 0..3 {
        thread_join(*threads[it]);
        thread_deinit(*threads[it]);
    }
    
    mutex_destroy(*global_mutex);
    
    print("Final counter value: % (expected 4000)\n", global_counter);
}

increment_worker :: (data: *void) -> *void #c_call {
    id := cast(int) data;
    
    for 0..999 {
        mutex_lock(*global_mutex);
        global_counter += 1;
        mutex_unlock(*global_mutex);
    }
    
    print("Worker % completed\n", id);
    return null;
}

WorkItem :: struct {
    id: int;
    data: int;
}

ThreadPool :: struct {
    threads: [..]*Thread;
    work_queue: [..]WorkItem;
    queue_mutex: Mutex;
    work_available: Condition_Variable;
    shutdown: bool;
}

thread_pool_example :: () {
    pool: ThreadPool;
    mutex_init(*pool.queue_mutex);
    condition_variable_init(*pool.work_available);
    defer {
        mutex_destroy(*pool.queue_mutex);
        condition_variable_destroy(*pool.work_available);
    }
    
    for 0..3 {
        t := New(Thread);
        thread_init(t, pool_worker, *pool);
        array_add(*pool.threads, t);
        thread_start(t);
    }
    
    for 0..9 {
        mutex_lock(*pool.queue_mutex);
        array_add(*pool.work_queue, .{it, it * it});
        condition_variable_signal(*pool.work_available);
        mutex_unlock(*pool.queue_mutex);
    }
    
    sleep_milliseconds(500);
    
    pool.shutdown = true;
    for pool.threads {
        condition_variable_broadcast(*pool.work_available);
    }
    
    for pool.threads {
        thread_join(it);
        thread_deinit(it);
        free(it);
    }
    
    print("Thread pool completed\n");
}

pool_worker :: (data: *void) -> *void #c_call {
    pool := cast(*ThreadPool) data;
    
    while true {
        mutex_lock(*pool.queue_mutex);
        
        while pool.work_queue.count == 0 && !pool.shutdown {
            condition_variable_wait(*pool.work_available, *pool.queue_mutex);
        }
        
        if pool.shutdown && pool.work_queue.count == 0 {
            mutex_unlock(*pool.queue_mutex);
            break;
        }
        
        work := pool.work_queue[0];
        array_ordered_remove_by_index(*pool.work_queue, 0);
        mutex_unlock(*pool.queue_mutex);
        
        print("Worker processing item %: %\n", work.id, work.data);
        sleep_milliseconds(50);
    }
    
    return null;
}

Task :: struct {
    func: (int) -> ();
    arg: int;
}

WorkQueue :: struct {
    tasks: [..]Task;
    mutex: Mutex;
    available: Condition_Variable;
}

work_queue_example :: () {
    queue: WorkQueue;
    mutex_init(*queue.mutex);
    condition_variable_init(*queue.available);
    defer {
        mutex_destroy(*queue.mutex);
        condition_variable_destroy(*queue.available);
    }
    
    producer: Thread;
    thread_init(*producer, (data: *void) -> *void #c_call {
        q := cast(*WorkQueue) data;
        
        for 0..4 {
            task: Task;
            task.func = (x: int) { print("Task executed with %\n", x); };
            task.arg = it;
            
            mutex_lock(*q.mutex);
            array_add(*q.tasks, task);
            condition_variable_signal(*q.available);
            mutex_unlock(*q.mutex);
            
            sleep_milliseconds(100);
        }
        
        return null;
    }, *queue);
    thread_start(*producer);
    
    completed := 0;
    while completed < 5 {
        mutex_lock(*queue.mutex);
        
        while queue.tasks.count == 0 {
            condition_variable_wait(*queue.available, *queue.mutex);
        }
        
        task := queue.tasks[0];
        array_ordered_remove_by_index(*queue.tasks, 0);
        mutex_unlock(*queue.mutex);
        
        task.func(task.arg);
        completed += 1;
    }
    
    thread_join(*producer);
    thread_deinit(*producer);
    
    print("Work queue completed\n");
}

Promise :: struct ($T: Type) {
    value: T;
    completed: bool;
    mutex: Mutex;
    available: Condition_Variable;
}

async_compute :: (promise: *Promise(int), input: int) #c_call {
    sleep_milliseconds(200);
    
    mutex_lock(*promise.mutex);
    promise.value = input * input;
    promise.completed = true;
    condition_variable_signal(*promise.available);
    mutex_unlock(*promise.mutex);
    
    return null;
}

async_await_example :: () {
    promise: Promise(int);
    mutex_init(*promise.mutex);
    condition_variable_init(*promise.available);
    defer {
        mutex_destroy(*promise.mutex);
        condition_variable_destroy(*promise.available);
    }
    
    thread: Thread;
    thread_init(*thread, (data: *void) -> *void #c_call {
        args := cast(*AsyncArgs) data;
        async_compute(args.promise, args.input);
        return null;
    }, *promise);
    thread_start(*thread);
    
    print("Doing other work while waiting...\n");
    sleep_milliseconds(100);
    
    mutex_lock(*promise.mutex);
    while !promise.completed {
        condition_variable_wait(*promise.available, *promise.mutex);
    }
    result := promise.value;
    mutex_unlock(*promise.mutex);
    
    print("Async result: %\n", result);
    
    thread_join(*thread);
    thread_deinit(*thread);
}

AsyncArgs :: struct {
    promise: *Promise(int);
    input: int;
}

atomic_counter: s64;

atomic_example :: () {
    atomic_counter = 0;
    
    threads: [3]Thread;
    
    for 0..2 {
        thread_init(*threads[it], atomic_worker, cast(*void) it);
        thread_start(*threads[it]);
    }
    
    for 0..2 {
        thread_join(*threads[it]);
        thread_deinit(*threads[it]);
    }
    
    print("Atomic counter: %\n", atomic_counter);
}

atomic_worker :: (data: *void) -> *void #c_call {
    id := cast(int) data;
    
    for 0..999 {
        atomic_add(*atomic_counter, 1);
    }
    
    print("Atomic worker % completed\n", id);
    return null;
}

atomic_add :: (val: *s64, delta: s64) -> s64 {
    #if CPU == .X64 {
        result: s64;
        #asm {
            mov rcx, val;
            mov rdx, delta;
            lock xadd [rcx], rdx;
            mov result, rdx;
        }
        return result;
    } else {
        val.* += delta;
        return val.*;
    }
}

#import "Basic";