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+
176335BOOST_AUTO_TEST_SUITE_END () // LockManagerTests
177336BOOST_AUTO_TEST_SUITE_END() // LockManagerSuite
178337BOOST_AUTO_TEST_SUITE_END() // EngineSuite
0 commit comments