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

%new-style
%enable-all-warnings
%require-types
%strict-args
%no-child-restrictions

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

%exec-class ThreadPoolTest

class RefTest {
    test(reference<hash<auto>> val) {
        ThreadPool thread_pool();
        foreach string key in (keys val) {
            string ckey = key;
            thread_pool.submit(sub() {
                ++val{ckey};
                ++val.i;
            });
        }
        thread_pool.stopWait();
        check(\val);
    }

    private check(reference<hash<auto>> val) {
        val.other = 3;
    }
}

class ThreadPoolTest inherits QUnit::Test {
    private {
        hash<auto> val;
    }

    constructor() : QUnit::Test("ThreadPool", "1.0") {
        addTestCase("ref test", \refTest());
        addTestCase("stopWait() test", \stopWaitTest());
        addTestCase("ThreadPoolTest", \ThreadPoolTest());
        set_return_value(main());
    }

    # issue #3939
    refTest() {
        val = {
            "test-1": 1,
            "test-2": 1,
        };
        RefTest rt();
        rt.test(\val);
        assertEq(2, val.i);
    }

    private doRef(reference<hash<auto>> val) {
        ThreadPool thread_pool();
        foreach string key in (keys val) {
            thread_pool.submit(sub() {
                ++val{key};
                ++val.i;
            });
        }
        thread_pool.stopWait();
        check(\val);
    }

    private check(reference<hash<auto>> val) {
        val.other = 3;
    }

    # issue #3897
    stopWaitTest() {
        int c;
        ThreadPool tp();
        code job = sub () { ++c; };
        tp.submit(job);
        tp.submit(job);
        tp.stopWait();
        assertEq(2, c);
    }

    ThreadPoolTest() {
        Program p(PO_NEW_STYLE | PO_STRICT_ARGS | PO_REQUIRE_TYPES);
        p.parse("sub set(string k, any val) { save_thread_data((k: val)); } any sub get(string k) { return get_thread_data(k); }", "");
        p.callFunction("set", "key", "value1");
        assertEq("value1", p.callFunction("get", "key"));

        Counter c(1);
        Counter start(1);

        code task = sub () {
            assertEq(NOTHING, p.callFunction("get", "key"));
            p.callFunction("set", "key", "value");
            assertEq("value", p.callFunction("get", "key"));
            start.dec();
            c.waitForZero();
        };

        ThreadPool tp();
        tp.submit(task);

        # wait for task to be run
        start.waitForZero();

        assertEq("value1", p.callFunction("get", "key"));

        delete p;
        delete task;

        # signal background task to exit
        c.dec();
    }
}
