Skip to content

Commit 51073d4

Browse files
authored
LockManager AST test (#8724)
* LockManager AST test * Checkout mutex in LockManagerTestCallbacks * Remove test globalMutex - thanks Vlad for the patch * Misc * Misc
1 parent 6a321bb commit 51073d4

File tree

1 file changed

+160
-1
lines changed

1 file changed

+160
-1
lines changed

src/lock/tests/LockManagerTest.cpp

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
#include "firebird.h"
22
#include "boost/test/unit_test.hpp"
33
#include <atomic>
4+
#include <chrono>
5+
#include <functional>
46
#include <latch>
57
#include <memory>
8+
#include <mutex>
9+
#include <optional>
10+
#include <unordered_map>
611
#include <thread>
712
#include <vector>
813
#include "../common/status.h"
@@ -19,6 +24,12 @@ namespace
1924
{
2025
class LockManagerTestCallbacks : public LockManager::Callbacks
2126
{
27+
public:
28+
LockManagerTestCallbacks(std::mutex* aMutex = nullptr)
29+
: mutex(aMutex)
30+
{
31+
}
32+
2233
public:
2334
ISC_STATUS getCancelState() const
2435
{
@@ -32,8 +43,18 @@ namespace
3243

3344
void checkoutRun(std::function<void()> func) const
3445
{
35-
func();
46+
if (mutex)
47+
{
48+
Cleanup lockCleanup([&]() { mutex->lock(); });
49+
mutex->unlock();
50+
func();
51+
}
52+
else
53+
func();
3654
}
55+
56+
private:
57+
std::mutex* mutex;
3758
};
3859
}
3960

@@ -173,6 +194,144 @@ BOOST_AUTO_TEST_CASE(LockUnlockNoWaitTest)
173194
}
174195

175196

197+
BOOST_AUTO_TEST_CASE(LockUnlockAstTest)
198+
{
199+
struct Lock;
200+
201+
struct ThreadData
202+
{
203+
std::thread::id threadId;
204+
LockManager* lockManager = nullptr;
205+
std::mutex localMutex;
206+
std::unordered_map<SLONG, std::unique_ptr<Lock>> locks;
207+
SLONG ownerHandle = 0;
208+
bool shutdown = false;
209+
};
210+
211+
struct Lock
212+
{
213+
ThreadData* threadData = nullptr;
214+
unsigned key = 0;
215+
std::optional<SLONG> lockId;
216+
bool blocking = false;
217+
};
218+
219+
constexpr unsigned THREAD_COUNT = 8u;
220+
constexpr unsigned ITERATION_COUNT = 10'000u;
221+
222+
ConfigFile configFile(ConfigFile::USE_TEXT, "\n");
223+
Config config(configFile);
224+
225+
const string lockManagerId(getUniqueId().c_str());
226+
auto lockManager = std::make_unique<LockManager>(lockManagerId, &config);
227+
228+
std::atomic_uint lockSuccess = 0u;
229+
std::atomic_uint lockFail = 0u;
230+
231+
std::vector<std::thread> threads;
232+
std::latch latch(THREAD_COUNT);
233+
234+
static const auto ast = [](void* astArg) -> int {
235+
const auto lock = static_cast<Lock*>(astArg);
236+
const auto threadData = lock->threadData;
237+
238+
std::lock_guard localMutexGuard(threadData->localMutex);
239+
240+
if (threadData->shutdown)
241+
return 0;
242+
243+
fb_assert(!lock->lockId.has_value() || lock->lockId.value() != 0);
244+
245+
if (lock->lockId.has_value())
246+
{
247+
if (!threadData->lockManager->dequeue(lock->lockId.value()))
248+
fb_assert(false);
249+
250+
[[maybe_unused]] const auto erasedCount = threadData->locks.erase(lock->lockId.value());
251+
fb_assert(erasedCount == 1);
252+
}
253+
else
254+
lock->blocking = true;
255+
256+
return 0;
257+
};
258+
259+
for (unsigned threadNum = 0u; threadNum < THREAD_COUNT; ++threadNum)
260+
{
261+
threads.emplace_back([&, threadNum]() {
262+
ThreadData threadData;
263+
threadData.threadId = std::this_thread::get_id();
264+
threadData.lockManager = lockManager.get();
265+
266+
LockManagerTestCallbacks callbacks(&threadData.localMutex);
267+
FbLocalStatus statusVector;
268+
LOCK_OWNER_T ownerId = threadNum + 1;
269+
270+
lockManager->initializeOwner(&statusVector, ownerId, LCK_OWNER_attachment, &threadData.ownerHandle);
271+
272+
latch.arrive_and_wait();
273+
274+
for (unsigned i = 0; i < ITERATION_COUNT; ++i)
275+
{
276+
auto lock = std::make_unique<Lock>();
277+
lock->threadData = &threadData;
278+
lock->key = i;
279+
280+
std::lock_guard localMutexGuard(threadData.localMutex);
281+
282+
const auto lockId = lockManager->enqueue(callbacks, &statusVector, 0,
283+
LCK_expression, (const UCHAR*) &lock->key, sizeof(lock->key), LCK_EX,
284+
ast, lock.get(), 0, LCK_WAIT, threadData.ownerHandle);
285+
286+
if (lockId)
287+
{
288+
++lockSuccess;
289+
290+
if (lock->blocking)
291+
lockManager->dequeue(lockId);
292+
else
293+
{
294+
lock->lockId = lockId;
295+
threadData.locks.insert({ lockId, std::move(lock) });
296+
}
297+
}
298+
else
299+
{
300+
fb_assert(false);
301+
++lockFail;
302+
}
303+
}
304+
305+
{ // scope
306+
std::lock_guard localMutexGuard(threadData.localMutex);
307+
308+
threadData.shutdown = true;
309+
310+
for (const auto& [lockId, lock] : threadData.locks)
311+
{
312+
fb_assert(!lock->blocking);
313+
fb_assert(lock->lockId.has_value() && lock->lockId.value() == lockId);
314+
lockManager->dequeue(lockId);
315+
}
316+
317+
lockManager->shutdownOwner(callbacks, &threadData.ownerHandle);
318+
319+
threadData.locks.clear();
320+
}
321+
322+
});
323+
}
324+
325+
for (auto& thread : threads)
326+
thread.join();
327+
328+
BOOST_CHECK_EQUAL(lockFail.load(), 0u);
329+
BOOST_CHECK_EQUAL(lockSuccess.load(), THREAD_COUNT * ITERATION_COUNT);
330+
331+
lockManager.reset();
332+
}
333+
334+
176335
BOOST_AUTO_TEST_SUITE_END() // LockManagerTests
177336
BOOST_AUTO_TEST_SUITE_END() // LockManagerSuite
178337
BOOST_AUTO_TEST_SUITE_END() // EngineSuite

0 commit comments

Comments
 (0)