#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%new-style
%enable-all-warnings
%require-types
%strict-args

%requires ../../../../qlib/Util.qm
%requires ../../../../qlib/QUnit.qm

%exec-class MaxThreadCountTest

our Queue q;

public class MaxThreadCountTest inherits QUnit::Test {
    private {
        int limit = 0;
    }

    constructor() : Test("Max thread count test", "1.0") {
        addTestCase("max thread count", \testMaxThreadCount());
        addTestCase("TID assignments", \testTidAssignments());
        # run the TID assignment test twice
        addTestCase("TID assignments", \testTidAssignments());

        # Return for compatibility with test harness that checks return value.
        set_return_value(main());
    }

    globalSetUp() {
        q = new Queue();
    }

    t(Counter c, Counter c0) {
        c.waitForZero();
        c0.dec();
    }

    testMaxThreadCount() {
        if (ENV.SKIP_MAX_THREAD_TEST) {
            testSkip("skipping max thread test due to environment variable");
        }

        Counter c(1);
        Counter c0();
        try {
            while (True) {
                c0.inc();
                on_error {
                    c.dec();
                    c0.dec();
                }
                background t(c, c0);
            }
        } catch (hash<ExceptionInfo> ex) {
            assertEq("THREAD-CREATION-FAILURE", ex.err, "found thread count limit");
            limit = num_threads();
        }

        if (m_options.verbose > 2) {
            printf("max threads: %d\n", limit);
        }

        assertGt(100, limit);

        c0.waitForZero(1s);
    }

    testTidAssignments() {
        list<int> l;
        Counter c(1);

        code t = sub (Counter c0) {
            l += gettid();
            c0.dec();
            c.waitForZero();
        };

        # wait for all threads to complete
        {
            date now = now_us();
            while (num_threads() > 1) {
                sleep(100ms);
                date d = now_us() - now;
                if (d > 20s) {
                    throw "TIMEOUT-ERROR", "timeout waiting for threads to terminate";
                }
            }
        }

        try {
            while (True) {
                Counter c0(1);
                background t(c0);
                c0.waitForZero();
            }
        } catch (hash<ExceptionInfo> ex) {
            assertEq("THREAD-CREATION-FAILURE", ex.err, "thread limit for assignment test");
            c.dec();
        }

        assertGt(100, l.size());

        # check TIDs for order
        int last = 0;
        int ooo;
        foreach int tid in (l) {
            if (tid < last) {
                ++ooo;
            }
            last = tid;
        }
        assertTrue(ooo < 2);
    }
}
