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