yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
4a255d211
master
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 ( 14Path ::getParentDirectory (Path ::getExecutablePath ())+ "/test_lock_file" + 15String (Process ::getId ())); 16 17SLANG_UNIT_TEST (lockFileOpenClose ) 18{ 19LockFile file ; 20SLANG_CHECK (file .isOpen ()== false); 21SLANG_CHECK_ABORT (file .open (fileName )== SLANG_OK ); 22SLANG_CHECK (file .isOpen ()== true); 23SLANG_CHECK (File ::exists (fileName )== true); 24file .close (); 25SLANG_CHECK (file .isOpen ()== false); 26 27// Cleanup. 28File ::remove (fileName ); 29SLANG_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 37SLANG_IGNORE_TEST 38#endif 39 40// Clean up any leftover lock file from previous runs 41File ::remove (fileName ); 42 43// Test using multiple threads. 44 { 45static std::atomic < uint32_t > lockCounter ; 46static std::atomic < uint32_t > unlockCounter ; 47 48lockCounter = 0 ; 49unlockCounter = 0 ; 50 51struct LockTask 52 { 53 std::thread thread ; 54 std::promise < void > startPromise ; 55 std::future < void > startFuture ; 56LockFile lockFile ; 57SlangResult openResult = false; 58SlangResult tryLockSharedResult = false; 59SlangResult tryLockExclusiveResult = false; 60SlangResult lockResult = false; 61SlangResult unlockResult = false; 62uint32_t lockIteration = 0 ; 63uint32_t unlockIteration = 0 ; 64 65LockTask () 66 :startFuture (startPromise .get_future ()) 67 { 68openResult = lockFile .open (fileName ); 69 } 70 71void run () 72 { 73tryLockSharedResult = lockFile .tryLock (LockFile ::LockType ::Shared ); 74tryLockExclusiveResult = lockFile .tryLock (LockFile ::LockType ::Exclusive ); 75startPromise .set_value (); 76lockResult = lockFile .lock (LockFile ::LockType ::Exclusive ); 77lockIteration = lockCounter .fetch_add (1 ); 78 std::this_thread::sleep_for (std::chrono::milliseconds (1 )); 79unlockIteration = unlockCounter .fetch_add (1 ); 80unlockResult = lockFile .unlock (); 81 } 82 }; 83 84// Acquire lock from main thread. 85LockFile lockFile ; 86SLANG_CHECK (lockFile .open (fileName )== SLANG_OK ); 87SLANG_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. 90LockFile lockFile2 ; 91SLANG_CHECK (lockFile2 .open (fileName )== SLANG_OK ); 92SLANG_CHECK (lockFile2 .tryLock (LockFile ::LockType ::Shared )== SLANG_E_TIME_OUT ); 93SLANG_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 ); 99for (auto & task :tasks ) 100 { 101task .thread = std::thread (& LockTask ::run ,& task ); 102task .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 )); 107SLANG_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. 111SLANG_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); 116for (auto & task :tasks ) 117 { 118task .thread .join (); 119 120SLANG_CHECK (task .openResult == SLANG_OK ); 121SLANG_CHECK (task .tryLockSharedResult == SLANG_E_TIME_OUT ); 122SLANG_CHECK (task .tryLockExclusiveResult == SLANG_E_TIME_OUT ); 123SLANG_CHECK (task .lockResult == SLANG_OK ); 124SLANG_CHECK (task .unlockResult == SLANG_OK ); 125SLANG_CHECK (task .lockIteration < lockIterationUsed .size ()); 126SLANG_CHECK (task .unlockIteration < unlockIterationUsed .size ()); 127SLANG_CHECK (task .unlockIteration == task .lockIteration ); 128SLANG_CHECK (lockIterationUsed [task .lockIteration ]== false); 129SLANG_CHECK (unlockIterationUsed [task .unlockIteration ]== false); 130lockIterationUsed [task .lockIteration ]= true; 131unlockIterationUsed [task .unlockIteration ]= true; 132 } 133 134// Ensure all threads did manage to acquire the lock. 135SLANG_CHECK (lockCounter == tasks .size ()); 136SLANG_CHECK (unlockCounter == tasks .size ()); 137 138// Check that we can now acquire the lock in non-blocking mode. 139SLANG_CHECK (lockFile2 .tryLock (LockFile ::LockType ::Exclusive )== SLANG_OK ); 140SLANG_CHECK (lockFile2 .unlock ()== SLANG_OK ); 141 } 142 143// Cleanup. 144File ::remove (fileName ); 145SLANG_CHECK (File ::exists (fileName )== false); 146}