// Copyright (c) 2023 NVIDIA CORPORATION.  All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#include <OgnTestIsolateDatabase.h>
#include "../include/omni/graph/test/ConcurrencyState.h"

// This is the implementation of the OGN node defined in OgnTestIsolate.ogn
// This node is used as part of a unit test in ../python/tests/test_execution.py

namespace omni {
namespace graph {
namespace test {

class OgnTestIsolate
{
public:
    static bool compute(OgnTestIsolateDatabase& db)
    {
        db.outputs.result() = false;

        // Use a race condition finder to confirm that there is no collision between serial and/or isolated
        // tasks. If we crash in here, that means ParallelScheduler is not respecting scheduling constraints.
        omni::graph::exec::unstable::RaceConditionFinder::Scope detectIssues(getConcurrencyState().raceConditionFinder);

        // In addition to ensuring that no other threads try to access the resources held by
        // this node/the thread this node gets evaluated in, Isolate scheduling implies that only
        // a SINGLE thread processing the current Isolate node can be computing (until said node
        // is done being evaluated). To validate that this is the case, try to grab an exclusive
        // lock over the global shared mutex. If this node is unable to do that (because concurrently-
        // running nodes have ownership over it at the moment), we'll return false. Also, if another
        // node/thread tries to access said shared mutex while this node is evaluating, the test will
        // end up failing as well.

        // Return early if TestIsolate can't exclusively own the shared mutex. outputs:result was already
        // set to false at the start, so no need to do anything else.
        if (!getConcurrencyState().sharedMutex.try_lock())
        {
            return true;
        }

        // Sleep for a while (simulate expensive compute/allows for potential race conditions to occur).
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        // Unlock the shared mutex, set output:result to true.
        db.outputs.result() = true;
        getConcurrencyState().sharedMutex.unlock();

        return true;
    }
};

REGISTER_OGN_NODE()

} // test
} // namespace graph
} // namespace omni
