yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Jay KwakFix bug in ci test (#8005)4a255d211

master
5.5 KiB146 linesraw
1// unit-test-lock-file.cpp
2#include "../../source/core/slang-io.h"
3#include "../../source/core/slang-process.h"
4#include "unit-test/slang-unit-test.h"
5
6#include <atomic>
7#include <future>
8#include <thread>
9#include <vector>
10
11using namespace Slang;
12
13static const String fileName = Path::simplify(
14    Path::getParentDirectory(Path::getExecutablePath()) + "/test_lock_file" +
15    String(Process::getId()));
16
17SLANG_UNIT_TEST(lockFileOpenClose)
18{
19    LockFile file;
20    SLANG_CHECK(file.isOpen() == false);
21    SLANG_CHECK_ABORT(file.open(fileName) == SLANG_OK);
22    SLANG_CHECK(file.isOpen() == true);
23    SLANG_CHECK(File::exists(fileName) == true);
24    file.close();
25    SLANG_CHECK(file.isOpen() == false);
26
27    // Cleanup.
28    File::remove(fileName);
29    SLANG_CHECK(File::exists(fileName) == false);
30}
31
32SLANG_UNIT_TEST(lockFileSync)
33{
34    // aarch64/linux builds currently fail to run multi-threaded tests within the test-server.
35    // Tests work fine without the test-server, which is puzzling. For now we disable them.
36#if SLANG_PROCESSOR_ARM_64 || SLANG_LINUX
37    SLANG_IGNORE_TEST
38#endif
39
40    // Clean up any leftover lock file from previous runs
41    File::remove(fileName);
42
43    // Test using multiple threads.
44    {
45        static std::atomic<uint32_t> lockCounter;
46        static std::atomic<uint32_t> unlockCounter;
47
48        lockCounter = 0;
49        unlockCounter = 0;
50
51        struct LockTask
52        {
53            std::thread thread;
54            std::promise<void> startPromise;
55            std::future<void> startFuture;
56            LockFile lockFile;
57            SlangResult openResult = false;
58            SlangResult tryLockSharedResult = false;
59            SlangResult tryLockExclusiveResult = false;
60            SlangResult lockResult = false;
61            SlangResult unlockResult = false;
62            uint32_t lockIteration = 0;
63            uint32_t unlockIteration = 0;
64
65            LockTask()
66                : startFuture(startPromise.get_future())
67            {
68                openResult = lockFile.open(fileName);
69            }
70
71            void run()
72            {
73                tryLockSharedResult = lockFile.tryLock(LockFile::LockType::Shared);
74                tryLockExclusiveResult = lockFile.tryLock(LockFile::LockType::Exclusive);
75                startPromise.set_value();
76                lockResult = lockFile.lock(LockFile::LockType::Exclusive);
77                lockIteration = lockCounter.fetch_add(1);
78                std::this_thread::sleep_for(std::chrono::milliseconds(1));
79                unlockIteration = unlockCounter.fetch_add(1);
80                unlockResult = lockFile.unlock();
81            }
82        };
83
84        // Acquire lock from main thread.
85        LockFile lockFile;
86        SLANG_CHECK(lockFile.open(fileName) == SLANG_OK);
87        SLANG_CHECK(lockFile.lock(LockFile::LockType::Exclusive) == SLANG_OK);
88
89        // Make sure we cannot acquire the lock in non-blocking mode from a second instance.
90        LockFile lockFile2;
91        SLANG_CHECK(lockFile2.open(fileName) == SLANG_OK);
92        SLANG_CHECK(lockFile2.tryLock(LockFile::LockType::Shared) == SLANG_E_TIME_OUT);
93        SLANG_CHECK(lockFile2.tryLock(LockFile::LockType::Exclusive) == SLANG_E_TIME_OUT);
94
95        // Start a number of threads and wait for them to start up.
96        // Each thread immediately tries to acquire the lock in non-blocking mode (expected to
97        // fail). Next each thread acquires the lock in blocking mode.
98        std::vector<LockTask> tasks(32);
99        for (auto& task : tasks)
100        {
101            task.thread = std::thread(&LockTask::run, &task);
102            task.startFuture.wait();
103        }
104
105        // Make sure none of the threads were able to acquire the lock yet.
106        std::this_thread::sleep_for(std::chrono::milliseconds(5));
107        SLANG_CHECK(lockCounter == 0);
108
109        // Release the lock from the main thread. This will allow all the other
110        // threads to acquire the lock, one after the other.
111        SLANG_CHECK(lockFile.unlock() == SLANG_OK);
112
113        // Wait for all threads to finish and make sure they behaved as expected.
114        std::vector<bool> lockIterationUsed(tasks.size(), false);
115        std::vector<bool> unlockIterationUsed(tasks.size(), false);
116        for (auto& task : tasks)
117        {
118            task.thread.join();
119
120            SLANG_CHECK(task.openResult == SLANG_OK);
121            SLANG_CHECK(task.tryLockSharedResult == SLANG_E_TIME_OUT);
122            SLANG_CHECK(task.tryLockExclusiveResult == SLANG_E_TIME_OUT);
123            SLANG_CHECK(task.lockResult == SLANG_OK);
124            SLANG_CHECK(task.unlockResult == SLANG_OK);
125            SLANG_CHECK(task.lockIteration < lockIterationUsed.size());
126            SLANG_CHECK(task.unlockIteration < unlockIterationUsed.size());
127            SLANG_CHECK(task.unlockIteration == task.lockIteration);
128            SLANG_CHECK(lockIterationUsed[task.lockIteration] == false);
129            SLANG_CHECK(unlockIterationUsed[task.unlockIteration] == false);
130            lockIterationUsed[task.lockIteration] = true;
131            unlockIterationUsed[task.unlockIteration] = true;
132        }
133
134        // Ensure all threads did manage to acquire the lock.
135        SLANG_CHECK(lockCounter == tasks.size());
136        SLANG_CHECK(unlockCounter == tasks.size());
137
138        // Check that we can now acquire the lock in non-blocking mode.
139        SLANG_CHECK(lockFile2.tryLock(LockFile::LockType::Exclusive) == SLANG_OK);
140        SLANG_CHECK(lockFile2.unlock() == SLANG_OK);
141    }
142
143    // Cleanup.
144    File::remove(fileName);
145    SLANG_CHECK(File::exists(fileName) == false);
146}