module thread_test;

import std::thread;
import std::os;

const TEST_MAGNITUDE = 10;

fn void lock_control_test() @test
{
    Mutex m;
    m.init()!!;
    m.lock()!!;
    assert(@catch(m.lock()));
}

fn void unlock_control_test() @test
{
    Mutex m;
    m.init()!!;
    assert(@catch(m.unlock()));
}

fn void lock_with_double_unlock_test() @test
{
    Mutex m;
    m.init()!!;

    m.lock()!!;
    m.unlock()!!;
    assert(@catch(m.unlock()));
}

fn void? own_mutex(Mutex* m)
{
    m.lock()!;
    m.unlock()!;
}

fn void ensure_owner_checks() @test
{
    Mutex m;
    m.init()!!;

    Thread[3 * TEST_MAGNITUDE] threads;
    
    foreach(&t : threads)
    {
        t.create((ThreadFn)&own_mutex, &m)!!;
    }

    foreach(&t : threads)
    {
        t.join()!!;
    }

    own_mutex(&m)!!;
}

struct ArgsWrapper1
{
    Mutex* m;
    ulong* v;
}

fn void shared_mutex_increment(ArgsWrapper1* args)
{
    args.m.lock()!!;
    args.v++;
    args.m.unlock()!!;
}

fn void shared_mutex_decrement(ArgsWrapper1* args)
{
    args.m.lock()!!;
    args.v--;
    args.m.unlock()!!;
}

fn void shared_mutex() @test
{
    Mutex m;
    m.init()!!;
    m.lock()!!;

    ulong v;

    ArgsWrapper1 args =
    {
        .m = &m,
        .v = &v
    };

    // An even number of threads must be chosen
    Thread[6 * TEST_MAGNITUDE] threads;
    for (int i = 0; i < threads.len / 2; i++)
    {
        (&threads[i]).create((ThreadFn)&shared_mutex_increment, &args)!!;
    }
    for (int i = (threads.len / 2); i < threads.len; i++)
    {
        (&threads[i]).create((ThreadFn)&shared_mutex_decrement, &args)!!;
    }

    m.unlock()!!;
    foreach(&t : threads)
    {
        t.join()!!;
    }
    assert(v == 0);
}

// Recursive mutex

fn void acquire_recursively(RecursiveMutex* m)
{
    // TODO: The recursive mutex functions can not directly be called via pointer

    for (usz i = 0; i < 5 * TEST_MAGNITUDE; i++)
    {
        ((Mutex*)m).lock()!!;
    }

    for (usz i = 0; i < 5 * TEST_MAGNITUDE; i++)
    {
        ((Mutex*)m).unlock()!!;
    }
}

fn void test_recursive_mutex() @test
{
    RecursiveMutex m;
    m.init()!!;
    defer m.destroy()!!;

    Thread[3 * TEST_MAGNITUDE] threads;
    foreach(&t : threads)
    {
        t.create((ThreadFn)&acquire_recursively, &m)!!;
    }

    foreach(&t : threads)
    {
        t.join()!!;
    }

    return acquire_recursively(&m);
}
