diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables
index 16fce089b02..f8d15e06879 100644
--- a/builds/posix/make.shared.variables
+++ b/builds/posix/make.shared.variables
@@ -92,7 +92,7 @@ Engine_Objects:= $(call dirObjects,jrd) $(call dirObjects,dsql) $(call dirObject
$(call dirObjects,jrd/sys-packages) $(call dirObjects,jrd/trace) \
$(call makeObjects,lock,lock.cpp)
-Engine_Test_Objects:= $(call dirObjects,jrd/tests)
+Engine_Test_Objects:= $(call dirObjects,jrd/tests) $(call dirObjects,lock/tests)
AllObjects += $(Engine_Objects) $(Engine_Test_Objects)
diff --git a/builds/win32/msvc15/engine_test.vcxproj b/builds/win32/msvc15/engine_test.vcxproj
index 0b36794b8e1..cba0b0c6a13 100644
--- a/builds/win32/msvc15/engine_test.vcxproj
+++ b/builds/win32/msvc15/engine_test.vcxproj
@@ -263,6 +263,9 @@
+
+
+
{0d616380-1a5a-4230-a80b-021360e4e669}
diff --git a/builds/win32/msvc15/engine_test.vcxproj.filters b/builds/win32/msvc15/engine_test.vcxproj.filters
index 5bfcdee7723..6dd45c98d06 100644
--- a/builds/win32/msvc15/engine_test.vcxproj.filters
+++ b/builds/win32/msvc15/engine_test.vcxproj.filters
@@ -24,5 +24,8 @@
source
+
+ source
+
diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp
index 8a3baf6bf8d..c2fa9d04c94 100644
--- a/src/jrd/lck.cpp
+++ b/src/jrd/lck.cpp
@@ -95,6 +95,24 @@ namespace
}
#endif
+
+ISC_STATUS LockManagerEngineCallbacks::getCancelState() const
+{
+ return tdbb->getCancelState();
+}
+
+ULONG LockManagerEngineCallbacks::adjustWait(ULONG wait) const
+{
+ return tdbb->adjustWait(wait);
+}
+
+void LockManagerEngineCallbacks::checkoutRun(std::function func) const
+{
+ EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
+ func();
+}
+
+
// globals and macros
inline LOCK_OWNER_T LCK_OWNER_ID_DBB(thread_db* tdbb)
@@ -155,8 +173,8 @@ inline bool CONVERT(thread_db* tdbb, CheckStatusWrapper* statusVector, Lock* loc
return lock->lck_compatible ?
internal_enqueue(tdbb, statusVector, lock, level, wait, true) :
- dbb->lockManager()->convert(tdbb, statusVector, lock->lck_id, level, wait, lock->lck_ast,
- lock->lck_object);
+ dbb->lockManager()->convert(LockManagerEngineCallbacks(tdbb), statusVector, lock->lck_id, level,
+ wait, lock->lck_ast, lock->lck_object);
}
inline void DEQUEUE(thread_db* tdbb, Lock* lock)
@@ -177,7 +195,7 @@ inline USHORT DOWNGRADE(thread_db* tdbb, Lock* lock)
USHORT ret = lock->lck_compatible ?
internal_downgrade(tdbb, &statusVector, lock) :
- dbb->lockManager()->downgrade(tdbb, &statusVector, lock->lck_id);
+ dbb->lockManager()->downgrade(LockManagerEngineCallbacks(tdbb), &statusVector, lock->lck_id);
fb_assert(statusVector.isEmpty());
@@ -499,7 +517,7 @@ void LCK_fini(thread_db* tdbb, enum lck_owner_t owner_type)
}
if (*owner_handle_ptr)
- dbb->lockManager()->shutdownOwner(tdbb, owner_handle_ptr);
+ dbb->lockManager()->shutdownOwner(LockManagerEngineCallbacks(tdbb), owner_handle_ptr);
}
@@ -841,7 +859,8 @@ void LCK_re_post(thread_db* tdbb, Lock* lock)
return;
}
- dbb->lockManager()->repost(tdbb, lock->lck_ast, lock->lck_object, lock->lck_owner_handle);
+ dbb->lockManager()->repost(LockManagerEngineCallbacks(tdbb),
+ lock->lck_ast, lock->lck_object, lock->lck_owner_handle);
fb_assert(LCK_CHECK_LOCK(lock));
}
@@ -945,7 +964,7 @@ static void enqueue(thread_db* tdbb, CheckStatusWrapper* statusVector, Lock* loc
fb_assert(LCK_CHECK_LOCK(lock));
- lock->lck_id = dbb->lockManager()->enqueue(tdbb, statusVector, lock->lck_id,
+ lock->lck_id = dbb->lockManager()->enqueue(LockManagerEngineCallbacks(tdbb), statusVector, lock->lck_id,
lock->lck_type, lock->getKeyPtr(), lock->lck_length,
level, lock->lck_ast, lock->lck_object, lock->lck_data, wait,
lock->lck_owner_handle);
@@ -1335,8 +1354,8 @@ static USHORT internal_downgrade(thread_db* tdbb, CheckStatusWrapper* statusVect
if (level < first->lck_physical)
{
- if (dbb->lockManager()->convert(tdbb, statusVector, first->lck_id, level, LCK_NO_WAIT,
- external_ast, first))
+ if (dbb->lockManager()->convert(LockManagerEngineCallbacks(tdbb), statusVector,
+ first->lck_id, level, LCK_NO_WAIT, external_ast, first))
{
for (Lock* lock = first; lock; lock = lock->lck_identical)
{
@@ -1402,8 +1421,8 @@ static bool internal_enqueue(thread_db* tdbb, CheckStatusWrapper* statusVector,
if (level > match->lck_physical)
{
- if (!dbb->lockManager()->convert(tdbb, statusVector, match->lck_id, level, wait,
- external_ast, lock))
+ if (!dbb->lockManager()->convert(LockManagerEngineCallbacks(tdbb), statusVector,
+ match->lck_id, level, wait, external_ast, lock))
{
return false;
}
@@ -1431,7 +1450,7 @@ static bool internal_enqueue(thread_db* tdbb, CheckStatusWrapper* statusVector,
// enqueue the lock, but swap out the ast and the ast argument
// with the local ast handler, passing it the lock block itself
- lock->lck_id = dbb->lockManager()->enqueue(tdbb, statusVector, lock->lck_id,
+ lock->lck_id = dbb->lockManager()->enqueue(LockManagerEngineCallbacks(tdbb), statusVector, lock->lck_id,
lock->lck_type, lock->getKeyPtr(), lock->lck_length,
level, external_ast, lock, lock->lck_data, wait, lock->lck_owner_handle);
diff --git a/src/jrd/lck.h b/src/jrd/lck.h
index dc8f9274984..08ea8992f0e 100644
--- a/src/jrd/lck.h
+++ b/src/jrd/lck.h
@@ -86,6 +86,22 @@ enum lck_owner_t {
LCK_OWNER_attachment // An attachment is the owner of the lock
};
+class LockManagerEngineCallbacks final : public LockManager::Callbacks
+{
+public:
+ LockManagerEngineCallbacks(thread_db* aTdbb)
+ : tdbb(aTdbb)
+ {
+ }
+
+ ISC_STATUS getCancelState() const override;
+ ULONG adjustWait(ULONG wait) const override;
+ void checkoutRun(std::function func) const override;
+
+private:
+ thread_db* const tdbb;
+};
+
class Lock : public pool_alloc_rpt
{
public:
diff --git a/src/lock/lock.cpp b/src/lock/lock.cpp
index b04a7dee847..3739f3ce6af 100644
--- a/src/lock/lock.cpp
+++ b/src/lock/lock.cpp
@@ -40,9 +40,8 @@
#include "firebird.h"
#include "../lock/lock_proto.h"
+#include "../common/StatusHolder.h"
#include "../common/ThreadStart.h"
-#include "../jrd/jrd.h"
-#include "../jrd/Attachment.h"
#include "iberror.h"
#include "../yvalve/gds_proto.h"
#include "../common/gdsassert.h"
@@ -381,7 +380,7 @@ bool LockManager::initializeOwner(CheckStatusWrapper* statusVector,
}
-void LockManager::shutdownOwner(thread_db* tdbb, SRQ_PTR* owner_handle)
+void LockManager::shutdownOwner(const Callbacks& callbacks, SRQ_PTR* owner_handle)
{
/**************************************
*
@@ -413,8 +412,9 @@ void LockManager::shutdownOwner(thread_db* tdbb, SRQ_PTR* owner_handle)
{
{ // checkout scope
LockTableCheckout checkout(this, FB_FUNCTION);
- EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
- Thread::sleep(10);
+ callbacks.checkoutRun([] {
+ Thread::sleep(10);
+ });
}
owner = (own*) SRQ_ABS_PTR(owner_offset);
@@ -431,7 +431,7 @@ void LockManager::shutdownOwner(thread_db* tdbb, SRQ_PTR* owner_handle)
}
-SRQ_PTR LockManager::enqueue(thread_db* tdbb,
+SRQ_PTR LockManager::enqueue(const Callbacks& callbacks,
CheckStatusWrapper* statusVector,
SRQ_PTR prior_request,
const USHORT series,
@@ -528,7 +528,7 @@ SRQ_PTR LockManager::enqueue(thread_db* tdbb,
insert_tail(&lock->lbl_requests, &request->lrq_lbl_requests);
request->lrq_data = data;
- if (grant_or_que(tdbb, request, lock, lck_wait))
+ if (grant_or_que(callbacks, request, lock, lck_wait))
return request_offset;
Arg::Gds(lck_wait > 0 ? isc_deadlock : lck_wait < 0 ? isc_lock_timeout :
@@ -584,7 +584,7 @@ SRQ_PTR LockManager::enqueue(thread_db* tdbb,
}
-bool LockManager::convert(thread_db* tdbb,
+bool LockManager::convert(const Callbacks& callbacks,
CheckStatusWrapper* statusVector,
SRQ_PTR request_offset,
UCHAR type,
@@ -623,14 +623,14 @@ bool LockManager::convert(thread_db* tdbb,
++(m_sharedMemory->getHeader()->lhb_operations[0]);
const bool result =
- internal_convert(tdbb, statusVector, request_offset, type, lck_wait,
+ internal_convert(callbacks, statusVector, request_offset, type, lck_wait,
ast_routine, ast_argument);
return result;
}
-UCHAR LockManager::downgrade(thread_db* tdbb,
+UCHAR LockManager::downgrade(const Callbacks& callbacks,
CheckStatusWrapper* statusVector,
const SRQ_PTR request_offset)
{
@@ -688,7 +688,7 @@ UCHAR LockManager::downgrade(thread_db* tdbb,
}
else
{
- internal_convert(tdbb, statusVector, request_offset, state, LCK_NO_WAIT,
+ internal_convert(callbacks, statusVector, request_offset, state, LCK_NO_WAIT,
request->lrq_ast_routine, request->lrq_ast_argument);
}
@@ -733,7 +733,7 @@ bool LockManager::dequeue(const SRQ_PTR request_offset)
}
-void LockManager::repost(thread_db* tdbb, lock_ast_t ast, void* arg, SRQ_PTR owner_offset)
+void LockManager::repost(const Callbacks& callbacks, lock_ast_t ast, void* arg, SRQ_PTR owner_offset)
{
/**************************************
*
@@ -790,7 +790,7 @@ void LockManager::repost(thread_db* tdbb, lock_ast_t ast, void* arg, SRQ_PTR own
DEBUG_DELAY;
if (!(owner->own_flags & OWN_signaled))
- signal_owner(tdbb, owner);
+ signal_owner(callbacks, owner);
}
@@ -1340,7 +1340,8 @@ lbl* LockManager::alloc_lock(USHORT length, CheckStatusWrapper* statusVector)
}
-void LockManager::blocking_action(thread_db* tdbb, SRQ_PTR blocking_owner_offset)
+void LockManager::blocking_action(const Callbacks& callbacks,
+ SRQ_PTR blocking_owner_offset)
{
/**************************************
*
@@ -1397,20 +1398,21 @@ void LockManager::blocking_action(thread_db* tdbb, SRQ_PTR blocking_owner_offset
{ // checkout scope
LockTableCheckout checkout(this, FB_FUNCTION);
- EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
- try
- {
- (*routine)(arg);
- }
- catch (const Exception& ex)
- {
- iscLogException("Exception from AST routine - this should never happen", ex);
- }
- catch (...)
- {
- gds__log("Unknown C++ exception from AST routine - this should never happen");
- }
+ callbacks.checkoutRun([&] {
+ try
+ {
+ (*routine)(arg);
+ }
+ catch (const Exception& ex)
+ {
+ iscLogException("Exception from AST routine - this should never happen", ex);
+ }
+ catch (...)
+ {
+ gds__log("Unknown C++ exception from AST routine - this should never happen");
+ }
+ });
}
owner = (own*) SRQ_ABS_PTR(blocking_owner_offset);
@@ -1485,7 +1487,29 @@ void LockManager::blocking_action_thread()
{
const SRQ_PTR owner_offset = SRQ_REL_PTR(owner);
guard.setOwner(owner_offset);
- blocking_action(NULL, owner_offset);
+
+ class BlockingActionCallbacks final : public Callbacks
+ {
+ public:
+ ISC_STATUS getCancelState() const override
+ {
+ fb_assert(false);
+ return 0;
+ }
+
+ ULONG adjustWait(ULONG wait) const override
+ {
+ fb_assert(false);
+ return wait;
+ }
+
+ void checkoutRun(std::function func) const override
+ {
+ func();
+ }
+ };
+
+ blocking_action(BlockingActionCallbacks(), owner_offset);
completed = false;
break;
}
@@ -2163,7 +2187,7 @@ void LockManager::grant(lrq* request, lbl* lock)
}
-bool LockManager::grant_or_que(thread_db* tdbb, lrq* request, lbl* lock, SSHORT lck_wait)
+bool LockManager::grant_or_que(const Callbacks& callbacks, lrq* request, lbl* lock, SSHORT lck_wait)
{
/**************************************
*
@@ -2200,7 +2224,7 @@ bool LockManager::grant_or_que(thread_db* tdbb, lrq* request, lbl* lock, SSHORT
{
const SRQ_PTR request_offset = SRQ_REL_PTR(request);
- wait_for_request(tdbb, request, lck_wait);
+ wait_for_request(callbacks, request, lck_wait);
request = (lrq*) SRQ_ABS_PTR(request_offset);
@@ -2458,7 +2482,7 @@ void LockManager::insert_tail(SRQ lock_srq, SRQ node)
}
-bool LockManager::internal_convert(thread_db* tdbb,
+bool LockManager::internal_convert(const Callbacks& callbacks,
CheckStatusWrapper* statusVector,
SRQ_PTR request_offset,
UCHAR type,
@@ -2520,7 +2544,7 @@ bool LockManager::internal_convert(thread_db* tdbb,
else
new_ast = false;
- wait_for_request(tdbb, request, lck_wait);
+ wait_for_request(callbacks, request, lck_wait);
request = (lrq*) SRQ_ABS_PTR(request_offset);
lock = (lbl*) SRQ_ABS_PTR(request->lrq_lock);
@@ -2605,7 +2629,7 @@ USHORT LockManager::lock_state(const lbl* lock)
}
-void LockManager::post_blockage(thread_db* tdbb, lrq* request, lbl* lock)
+void LockManager::post_blockage(const Callbacks& callbacks, lrq* request, lbl* lock)
{
/**************************************
*
@@ -2673,7 +2697,7 @@ void LockManager::post_blockage(thread_db* tdbb, lrq* request, lbl* lock)
if (blocking_owner->own_count &&
!(blocking_owner->own_flags & OWN_signaled) &&
- !signal_owner(tdbb, blocking_owner))
+ !signal_owner(callbacks, blocking_owner))
{
dead_processes.add(blocking_owner->own_process);
}
@@ -3169,7 +3193,7 @@ void LockManager::release_request(lrq* request)
}
-bool LockManager::signal_owner(thread_db* tdbb, own* blocking_owner)
+bool LockManager::signal_owner(const Callbacks& callbacks, own* blocking_owner)
{
/**************************************
*
@@ -3202,7 +3226,7 @@ bool LockManager::signal_owner(thread_db* tdbb, own* blocking_owner)
if (process->prc_process_id == PID)
{
DEBUG_DELAY;
- blocking_action(tdbb, SRQ_REL_PTR(blocking_owner));
+ blocking_action(callbacks, SRQ_REL_PTR(blocking_owner));
DEBUG_DELAY;
return true;
}
@@ -3706,7 +3730,7 @@ void LockManager::validate_shb(const SRQ_PTR shb_ptr)
}
-void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wait)
+void LockManager::wait_for_request(const Callbacks& callbacks, lrq* request, SSHORT lck_wait)
{
/**************************************
*
@@ -3763,7 +3787,7 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
// Post blockage. If the blocking owner has disappeared, the blockage
// may clear spontaneously.
- post_blockage(tdbb, request, lock);
+ post_blockage(callbacks, request, lock);
post_history(his_wait, owner_offset, lock_offset, request_offset, true);
owner = (own*) SRQ_ABS_PTR(owner_offset);
@@ -3776,7 +3800,7 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
// out the time when the lock request will timeout
const time_t lock_timeout = (lck_wait < 0) ? current_time + (-lck_wait) : 0;
- time_t deadlock_timeout = current_time + tdbb->adjustWait(scan_interval);
+ time_t deadlock_timeout = current_time + callbacks.adjustWait(scan_interval);
// Wait in a loop until the lock becomes available
@@ -3822,9 +3846,10 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
}
{ // scope
- EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
- ret = m_sharedMemory->eventWait(&owner->own_wakeup, value, (timeout - current_time) * 1000000);
- --m_waitingOwners;
+ callbacks.checkoutRun([&] {
+ ret = m_sharedMemory->eventWait(&owner->own_wakeup, value, (timeout - current_time) * 1000000);
+ --m_waitingOwners;
+ });
}
}
@@ -3881,8 +3906,7 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
// See if we've waited beyond the lock timeout -
// if so we mark our own request as rejected
- // !!! this will be changed to have no dependency on thread_db !!!
- const bool cancelled = (tdbb->getCancelState() != FB_SUCCESS);
+ const bool cancelled = (callbacks.getCancelState() != FB_SUCCESS);
if (cancelled || (lck_wait < 0 && lock_timeout <= current_time))
{
@@ -3901,7 +3925,7 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
// We're going to do some real work - reset when we next want to
// do a deadlock scan
- deadlock_timeout = current_time + tdbb->adjustWait(scan_interval);
+ deadlock_timeout = current_time + callbacks.adjustWait(scan_interval);
// Handle lock event first
if (ret == FB_SUCCESS)
@@ -3911,7 +3935,7 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
// This could happen if the lock was granted to a different request,
// we have to tell the new owner of the lock that they are blocking us.
- post_blockage(tdbb, request, lock);
+ post_blockage(callbacks, request, lock);
continue;
}
@@ -3966,7 +3990,7 @@ void LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_wai
// We need to inform the new owner.
DEBUG_MSG(0, ("wait_for_request: forcing a resignal of blockers\n"));
- post_blockage(tdbb, request, lock);
+ post_blockage(callbacks, request, lock);
#ifdef DEV_BUILD
repost_counter++;
if (repost_counter % 50 == 0)
diff --git a/src/lock/lock_proto.h b/src/lock/lock_proto.h
index a4f3219997d..b0b14511938 100644
--- a/src/lock/lock_proto.h
+++ b/src/lock/lock_proto.h
@@ -290,10 +290,20 @@ namespace Firebird {
namespace Jrd {
-class thread_db;
-
class LockManager final : public Firebird::GlobalStorage, public Firebird::IpcObject
{
+public:
+ class Callbacks
+ {
+ public:
+ virtual ~Callbacks() = default;
+
+ virtual ISC_STATUS getCancelState() const = 0;
+ virtual ULONG adjustWait(ULONG wait) const = 0;
+ virtual void checkoutRun(std::function func) const = 0;
+ };
+
+private:
class LockTableGuard
{
public:
@@ -395,15 +405,15 @@ class LockManager final : public Firebird::GlobalStorage, public Firebird::IpcOb
~LockManager();
bool initializeOwner(Firebird::CheckStatusWrapper*, LOCK_OWNER_T, UCHAR, SRQ_PTR*);
- void shutdownOwner(thread_db*, SRQ_PTR*);
+ void shutdownOwner(const Callbacks&, SRQ_PTR*);
- SRQ_PTR enqueue(thread_db*, Firebird::CheckStatusWrapper*, SRQ_PTR, const USHORT,
+ SRQ_PTR enqueue(const Callbacks&, Firebird::CheckStatusWrapper*, SRQ_PTR, const USHORT,
const UCHAR*, const USHORT, UCHAR, lock_ast_t, void*, LOCK_DATA_T, SSHORT, SRQ_PTR);
- bool convert(thread_db*, Firebird::CheckStatusWrapper*, SRQ_PTR, UCHAR, SSHORT, lock_ast_t, void*);
- UCHAR downgrade(thread_db*, Firebird::CheckStatusWrapper*, const SRQ_PTR);
+ bool convert(const Callbacks&, Firebird::CheckStatusWrapper*, SRQ_PTR, UCHAR, SSHORT, lock_ast_t, void*);
+ UCHAR downgrade(const Callbacks&, Firebird::CheckStatusWrapper*, const SRQ_PTR);
bool dequeue(const SRQ_PTR);
- void repost(thread_db*, lock_ast_t, void*, SRQ_PTR);
+ void repost(const Callbacks&, lock_ast_t, void*, SRQ_PTR);
bool cancelWait(SRQ_PTR);
LOCK_DATA_T queryData(const USHORT, const USHORT);
@@ -417,7 +427,7 @@ class LockManager final : public Firebird::GlobalStorage, public Firebird::IpcOb
void acquire_shmem(SRQ_PTR);
UCHAR* alloc(USHORT, Firebird::CheckStatusWrapper*);
lbl* alloc_lock(USHORT, Firebird::CheckStatusWrapper*);
- void blocking_action(thread_db*, SRQ_PTR);
+ void blocking_action(const Callbacks&, SRQ_PTR);
void blocking_action_thread();
void bug(Firebird::CheckStatusWrapper*, const TEXT*);
void bug_assert(const TEXT*, ULONG);
@@ -430,15 +440,15 @@ class LockManager final : public Firebird::GlobalStorage, public Firebird::IpcOb
lbl* find_lock(USHORT, const UCHAR*, USHORT, USHORT*);
lrq* get_request(SRQ_PTR);
void grant(lrq*, lbl*);
- bool grant_or_que(thread_db*, lrq*, lbl*, SSHORT);
+ bool grant_or_que(const Callbacks&, lrq*, lbl*, SSHORT);
bool init_owner_block(Firebird::CheckStatusWrapper*, own*, UCHAR, LOCK_OWNER_T);
void insert_data_que(lbl*);
void insert_tail(SRQ, SRQ);
- bool internal_convert(thread_db* database, Firebird::CheckStatusWrapper*, SRQ_PTR, UCHAR, SSHORT,
+ bool internal_convert(const Callbacks&, Firebird::CheckStatusWrapper*, SRQ_PTR, UCHAR, SSHORT,
lock_ast_t, void*);
void internal_dequeue(SRQ_PTR);
static USHORT lock_state(const lbl*);
- void post_blockage(thread_db*, lrq*, lbl*);
+ void post_blockage(const Callbacks&, lrq*, lbl*);
void post_history(USHORT, SRQ_PTR, SRQ_PTR, SRQ_PTR, bool);
void post_pending(lbl*);
void post_wakeup(own*);
@@ -449,7 +459,7 @@ class LockManager final : public Firebird::GlobalStorage, public Firebird::IpcOb
void remove_que(SRQ);
void release_shmem(SRQ_PTR);
void release_request(lrq*);
- bool signal_owner(thread_db*, own*);
+ bool signal_owner(const Callbacks&, own*);
void validate_history(const SRQ_PTR history_header);
void validate_lhb(const lhb*);
@@ -458,7 +468,7 @@ class LockManager final : public Firebird::GlobalStorage, public Firebird::IpcOb
void validate_request(const SRQ_PTR, USHORT, USHORT);
void validate_shb(const SRQ_PTR);
- void wait_for_request(thread_db*, lrq*, SSHORT);
+ void wait_for_request(const Callbacks&, lrq*, SSHORT);
bool init_shared_file(Firebird::CheckStatusWrapper*);
void get_shared_file_name(Firebird::PathName&, ULONG extend = 0) const;
diff --git a/src/lock/tests/LockManagerTest.cpp b/src/lock/tests/LockManagerTest.cpp
new file mode 100644
index 00000000000..4ac2ee5ff66
--- /dev/null
+++ b/src/lock/tests/LockManagerTest.cpp
@@ -0,0 +1,178 @@
+#include "firebird.h"
+#include "boost/test/unit_test.hpp"
+#include
+#include
+#include
+#include
+#include
+#include "../common/status.h"
+#include "../common/classes/fb_string.h"
+#include "../common/config/config.h"
+#include "../lock/lock_proto.h"
+#include "../jrd/lck.h"
+
+using namespace Firebird;
+using namespace Jrd;
+
+
+namespace
+{
+ class LockManagerTestCallbacks : public LockManager::Callbacks
+ {
+ public:
+ ISC_STATUS getCancelState() const
+ {
+ return 0;
+ }
+
+ ULONG adjustWait(ULONG wait) const
+ {
+ return wait;
+ }
+
+ void checkoutRun(std::function func) const
+ {
+ func();
+ }
+ };
+}
+
+
+static std::string getUniqueId()
+{
+ static std::atomic lockSuccess{0};
+
+ const auto now = std::chrono::system_clock::now();
+ const auto nowNs = std::chrono::duration_cast(now.time_since_epoch()).count();
+
+ return "lm_" +
+ std::to_string(nowNs) + "_" +
+ std::to_string(lockSuccess.fetch_add(1));
+}
+
+
+BOOST_AUTO_TEST_SUITE(EngineSuite)
+BOOST_AUTO_TEST_SUITE(LockManagerSuite)
+BOOST_AUTO_TEST_SUITE(LockManagerTests)
+
+
+BOOST_AUTO_TEST_CASE(LockUnlockWaitTest)
+{
+ constexpr unsigned THREAD_COUNT = 8u;
+ constexpr unsigned ITERATION_COUNT = 10'000u;
+
+ ConfigFile configFile(ConfigFile::USE_TEXT, "\n");
+ Config config(configFile);
+
+ LockManagerTestCallbacks callbacks;
+ const string lockManagerId(getUniqueId().c_str());
+ auto lockManager = std::make_unique(lockManagerId, &config);
+
+ unsigned lockSuccess = 0u;
+ std::atomic_uint lockFail = 0;
+
+ std::vector threads;
+ std::latch latch(THREAD_COUNT);
+
+ for (unsigned threadNum = 0u; threadNum < THREAD_COUNT; ++threadNum)
+ {
+ threads.emplace_back([&, threadNum]() {
+ const UCHAR LOCK_KEY[] = {'1'};
+ FbLocalStatus statusVector;
+ LOCK_OWNER_T ownerId = threadNum + 1;
+ SLONG ownerHandle = 0;
+
+ lockManager->initializeOwner(&statusVector, ownerId, LCK_OWNER_attachment, &ownerHandle);
+
+ latch.arrive_and_wait();
+
+ for (unsigned i = 0; i < ITERATION_COUNT; ++i)
+ {
+ const auto lockId = lockManager->enqueue(callbacks, &statusVector, 0,
+ LCK_expression, LOCK_KEY, sizeof(LOCK_KEY), LCK_EX, nullptr, nullptr, 0, LCK_WAIT, ownerHandle);
+
+ if (lockId)
+ {
+ ++lockSuccess;
+ lockManager->dequeue(lockId);
+ }
+ else
+ ++lockFail;
+ }
+
+ lockManager->shutdownOwner(callbacks, &ownerHandle);
+ });
+ }
+
+ for (auto& thread : threads)
+ thread.join();
+
+ BOOST_CHECK_EQUAL(lockFail.load(), 0u);
+ BOOST_CHECK_EQUAL(lockSuccess, THREAD_COUNT * ITERATION_COUNT);
+
+ lockManager.reset();
+}
+
+
+BOOST_AUTO_TEST_CASE(LockUnlockNoWaitTest)
+{
+ constexpr unsigned THREAD_COUNT = 8u;
+ constexpr unsigned ITERATION_COUNT = 10'000u;
+
+ ConfigFile configFile(ConfigFile::USE_TEXT, "\n");
+ Config config(configFile);
+
+ LockManagerTestCallbacks callbacks;
+ const string lockManagerId(getUniqueId().c_str());
+ auto lockManager = std::make_unique(lockManagerId, &config);
+
+ unsigned lockSuccess = 0u;
+ std::atomic_uint lockFail = 0;
+
+ std::vector threads;
+ std::latch latch(THREAD_COUNT);
+
+ for (unsigned threadNum = 0u; threadNum < THREAD_COUNT; ++threadNum)
+ {
+ threads.emplace_back([&, threadNum]() {
+ const UCHAR LOCK_KEY[] = {'1'};
+ FbLocalStatus statusVector;
+ LOCK_OWNER_T ownerId = threadNum + 1;
+ SLONG ownerHandle = 0;
+
+ lockManager->initializeOwner(&statusVector, ownerId, LCK_OWNER_attachment, &ownerHandle);
+
+ latch.arrive_and_wait();
+
+ for (unsigned i = 0; i < ITERATION_COUNT; ++i)
+ {
+ const auto lockId = lockManager->enqueue(callbacks, &statusVector, 0,
+ LCK_expression, LOCK_KEY, sizeof(LOCK_KEY), LCK_EX, nullptr, nullptr, 0, LCK_NO_WAIT, ownerHandle);
+
+ if (lockId)
+ {
+ ++lockSuccess;
+ lockManager->dequeue(lockId);
+ }
+ else
+ ++lockFail;
+ }
+
+ lockManager->shutdownOwner(callbacks, &ownerHandle);
+ });
+ }
+
+ for (auto& thread : threads)
+ thread.join();
+
+ BOOST_CHECK_GT(lockFail.load(), 0u);
+ BOOST_CHECK_GT(lockSuccess, 0u);
+ BOOST_CHECK_EQUAL(lockSuccess + lockFail, THREAD_COUNT * ITERATION_COUNT);
+
+ lockManager.reset();
+}
+
+
+BOOST_AUTO_TEST_SUITE_END() // LockManagerTests
+BOOST_AUTO_TEST_SUITE_END() // LockManagerSuite
+BOOST_AUTO_TEST_SUITE_END() // EngineSuite