mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 08:03:03 +01:00
Improvement CORE-3323 : Ability to cancel waiting in lock manager
This commit is contained in:
parent
cb34e0c6c9
commit
b2b911fe6c
161
src/jrd/jrd.cpp
161
src/jrd/jrd.cpp
@ -1583,6 +1583,7 @@ ISC_STATUS FB_CANCEL_OPERATION(ISC_STATUS* user_status, Attachment** handle, USH
|
||||
{
|
||||
attachment->att_flags |= ATT_cancel_raise;
|
||||
attachment->cancelExternalConnection(tdbb);
|
||||
LCK_cancel_wait(attachment);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -4033,84 +4034,24 @@ bool JRD_reschedule(thread_db* tdbb, SLONG quantum, bool punt)
|
||||
THREAD_YIELD();
|
||||
}
|
||||
|
||||
// Test various flags and unwind/throw if required.
|
||||
// But do that only if we're neither in the verb cleanup state
|
||||
// nor currently detaching, as these actions should never be interrupted.
|
||||
|
||||
if (!(tdbb->tdbb_flags & (TDBB_verb_cleanup | TDBB_detaching)))
|
||||
try {
|
||||
tdbb->checkCancelState(punt);
|
||||
}
|
||||
catch (const status_exception& ex)
|
||||
{
|
||||
// If database has been shutdown then get out
|
||||
tdbb->tdbb_flags |= TDBB_sys_error;
|
||||
|
||||
Attachment* const attachment = tdbb->getAttachment();
|
||||
jrd_tra* const transaction = tdbb->getTransaction();
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
const Arg::StatusVector status(ex.value());
|
||||
|
||||
try
|
||||
if (punt)
|
||||
{
|
||||
if (attachment)
|
||||
{
|
||||
if (attachment->att_flags & ATT_shutdown)
|
||||
{
|
||||
if (dbb->dbb_ast_flags & DBB_shutdown)
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_shutdown) <<
|
||||
Arg::Str(attachment->att_filename));
|
||||
}
|
||||
else if (!(tdbb->tdbb_flags & TDBB_shutdown_manager))
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_att_shutdown));
|
||||
}
|
||||
}
|
||||
|
||||
// If a cancel has been raised, defer its acknowledgement
|
||||
// when executing in the context of an internal request or
|
||||
// the system transaction.
|
||||
|
||||
if ((attachment->att_flags & ATT_cancel_raise) &&
|
||||
!(attachment->att_flags & ATT_cancel_disable))
|
||||
{
|
||||
if ((!request ||
|
||||
!(request->req_flags & (req_internal | req_sys_trigger))) &&
|
||||
(!transaction || !(transaction->tra_flags & TRA_system)))
|
||||
{
|
||||
attachment->att_flags &= ~ATT_cancel_raise;
|
||||
status_exception::raise(Arg::Gds(isc_cancelled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle request cancellation
|
||||
|
||||
if (transaction && (transaction->tra_flags & TRA_cancel_request))
|
||||
{
|
||||
transaction->tra_flags &= ~TRA_cancel_request;
|
||||
status_exception::raise(Arg::Gds(isc_cancelled));
|
||||
}
|
||||
|
||||
// Check the thread state for already posted system errors. If any still persists,
|
||||
// then someone tries to ignore our attempts to interrupt him. Let's insist.
|
||||
|
||||
if (tdbb->tdbb_flags & TDBB_sys_error)
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_cancelled));
|
||||
}
|
||||
CCH_unwind(tdbb, false);
|
||||
ERR_post(status);
|
||||
}
|
||||
catch (const status_exception& ex)
|
||||
else
|
||||
{
|
||||
tdbb->tdbb_flags |= TDBB_sys_error;
|
||||
|
||||
const Arg::StatusVector status(ex.value());
|
||||
|
||||
if (punt)
|
||||
{
|
||||
CCH_unwind(tdbb, false);
|
||||
ERR_post(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR_build_status(tdbb->tdbb_status_vector, status);
|
||||
return true;
|
||||
}
|
||||
ERR_build_status(tdbb->tdbb_status_vector, status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5785,6 +5726,7 @@ static unsigned int purge_transactions(thread_db* tdbb,
|
||||
if ((transaction->tra_flags & TRA_prepared) || (dbb->dbb_ast_flags & DBB_shutdown) ||
|
||||
(att_flags & ATT_shutdown))
|
||||
{
|
||||
EDS::Transaction::jrdTransactionEnd(tdbb, transaction, false, false, true);
|
||||
TRA_release_transaction(tdbb, transaction);
|
||||
}
|
||||
else if (force_flag)
|
||||
@ -6235,6 +6177,81 @@ static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool thread_db::checkCancelState(bool punt)
|
||||
{
|
||||
// Test various flags and unwind/throw if required.
|
||||
// But do that only if we're neither in the verb cleanup state
|
||||
// nor currently detaching, as these actions should never be interrupted.
|
||||
// Also don't break wait in LM if it is not safe.
|
||||
|
||||
if (tdbb_flags & (TDBB_verb_cleanup | TDBB_detaching | TDBB_wait_cancel_disable))
|
||||
return false;
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
if (attachment->att_flags & ATT_shutdown)
|
||||
{
|
||||
if (database->dbb_ast_flags & DBB_shutdown)
|
||||
{
|
||||
if (!punt)
|
||||
return true;
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_shutdown) <<
|
||||
Arg::Str(attachment->att_filename));
|
||||
}
|
||||
else if (!(tdbb_flags & TDBB_shutdown_manager))
|
||||
{
|
||||
if (!punt)
|
||||
return true;
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_att_shutdown));
|
||||
}
|
||||
}
|
||||
|
||||
// If a cancel has been raised, defer its acknowledgement
|
||||
// when executing in the context of an internal request or
|
||||
// the system transaction.
|
||||
|
||||
if ((attachment->att_flags & ATT_cancel_raise) &&
|
||||
!(attachment->att_flags & ATT_cancel_disable))
|
||||
{
|
||||
if ((!request ||
|
||||
!(request->req_flags & (req_internal | req_sys_trigger))) &&
|
||||
(!transaction || !(transaction->tra_flags & TRA_system)))
|
||||
{
|
||||
if (!punt)
|
||||
return true;
|
||||
|
||||
attachment->att_flags &= ~ATT_cancel_raise;
|
||||
status_exception::raise(Arg::Gds(isc_cancelled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle request cancellation
|
||||
|
||||
if (transaction && (transaction->tra_flags & TRA_cancel_request))
|
||||
{
|
||||
if (!punt)
|
||||
return true;
|
||||
|
||||
transaction->tra_flags &= ~TRA_cancel_request;
|
||||
status_exception::raise(Arg::Gds(isc_cancelled));
|
||||
}
|
||||
|
||||
// Check the thread state for already posted system errors. If any still persists,
|
||||
// then someone tries to ignore our attempts to interrupt him. Let's insist.
|
||||
|
||||
if (tdbb_flags & TDBB_sys_error)
|
||||
{
|
||||
if (!punt)
|
||||
return true;
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_cancelled));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void thread_db::setTransaction(jrd_tra* val)
|
||||
{
|
||||
|
@ -313,6 +313,7 @@ public:
|
||||
ULONG att_flags; // Flags describing the state of the attachment
|
||||
SSHORT att_charset; // user's charset specified in dpb
|
||||
Lock* att_long_locks; // outstanding two phased locks
|
||||
Lock* att_wait_lock; // lock at which attachment waits currently
|
||||
vec<Lock*>* att_compatibility_table; // hash table of compatible locks
|
||||
vcl* att_val_errors;
|
||||
Firebird::PathName att_working_directory; // Current working directory is cached
|
||||
@ -778,6 +779,8 @@ public:
|
||||
//attStat->bumpValue(index, relation_id);
|
||||
//dbbStat->bumpValue(index, relation_id);
|
||||
}
|
||||
|
||||
bool checkCancelState(bool punt);
|
||||
};
|
||||
|
||||
// tdbb_flags
|
||||
@ -793,6 +796,7 @@ const USHORT TDBB_sys_error = 128; // error shouldn't be handled by the loope
|
||||
const USHORT TDBB_verb_cleanup = 256; // verb cleanup is in progress
|
||||
const USHORT TDBB_use_db_page_space = 512; // use database (not temporary) page space in GTT operations
|
||||
const USHORT TDBB_detaching = 1024; // detach is in progress
|
||||
const USHORT TDBB_wait_cancel_disable = 2048; // don't cancel current waiting operation
|
||||
|
||||
|
||||
class ThreadContextHolder
|
||||
|
@ -211,6 +211,61 @@ inline bool checkLock(const Lock* l)
|
||||
#define LCK_CHECK_LOCK(x) true // nothing
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// This class is used as a guard around long waiting call into LM and have
|
||||
// two purposes :
|
||||
// - set and restore att_wait_lock while waiting inside the LM
|
||||
// - set or clear and restore TDBB_wait_cancel_disable flag in dependence
|
||||
// of safety of cancelling lock waiting. Currently we can safely cancel
|
||||
// only LCK_tra locks
|
||||
|
||||
class WaitCancelGuard
|
||||
{
|
||||
public:
|
||||
WaitCancelGuard(thread_db* tdbb, Lock* lock, int wait) :
|
||||
m_tdbb(tdbb),
|
||||
m_save_lock(NULL)
|
||||
{
|
||||
Attachment* att = m_tdbb->getAttachment();
|
||||
m_save_lock = att->att_wait_lock;
|
||||
|
||||
m_cancel_disabled = (m_tdbb->tdbb_flags & TDBB_wait_cancel_disable);
|
||||
m_tdbb->tdbb_flags |= TDBB_wait_cancel_disable;
|
||||
|
||||
if (!wait)
|
||||
return;
|
||||
|
||||
switch (lock->lck_type)
|
||||
{
|
||||
case LCK_tra:
|
||||
m_tdbb->tdbb_flags &= ~TDBB_wait_cancel_disable;
|
||||
att->att_wait_lock = lock;
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
~WaitCancelGuard()
|
||||
{
|
||||
Attachment* att = m_tdbb->getAttachment();
|
||||
att->att_wait_lock = m_save_lock;
|
||||
|
||||
if (m_cancel_disabled)
|
||||
m_tdbb->tdbb_flags |= TDBB_wait_cancel_disable;
|
||||
else
|
||||
m_tdbb->tdbb_flags &= ~TDBB_wait_cancel_disable;
|
||||
}
|
||||
|
||||
private:
|
||||
thread_db* m_tdbb;
|
||||
Lock* m_save_lock;
|
||||
bool m_cancel_disabled;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void LCK_assert(thread_db* tdbb, Lock* lock)
|
||||
{
|
||||
@ -259,6 +314,7 @@ bool LCK_convert(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait)
|
||||
Attachment* const old_attachment = lock->lck_attachment;
|
||||
set_lock_attachment(lock, tdbb->getAttachment());
|
||||
|
||||
WaitCancelGuard guard(tdbb, lock, wait);
|
||||
const bool result = CONVERT(tdbb, lock, level, wait);
|
||||
|
||||
if (!result)
|
||||
@ -270,6 +326,7 @@ bool LCK_convert(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait)
|
||||
case isc_deadlock:
|
||||
case isc_lock_conflict:
|
||||
case isc_lock_timeout:
|
||||
tdbb->checkCancelState(true);
|
||||
return false;
|
||||
case isc_lockmanerr:
|
||||
dbb->dbb_flags |= DBB_bugcheck;
|
||||
@ -316,6 +373,27 @@ bool LCK_convert_opt(thread_db* tdbb, Lock* lock, USHORT level)
|
||||
}
|
||||
|
||||
|
||||
bool LCK_cancel_wait(Attachment* attachment)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* L C K _ c a n c e l _ w a i t
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Try to cancel waiting of attachment inside the LM
|
||||
*
|
||||
**************************************/
|
||||
Database *dbb = attachment->att_database;
|
||||
|
||||
if (attachment->att_wait_lock)
|
||||
return dbb->dbb_lock_mgr->cancelWait(attachment->att_wait_lock->lck_owner_handle);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LCK_downgrade(thread_db* tdbb, Lock* lock)
|
||||
{
|
||||
/**************************************
|
||||
@ -535,6 +613,7 @@ bool LCK_lock(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait)
|
||||
Database* dbb = lock->lck_dbb;
|
||||
set_lock_attachment(lock, tdbb->getAttachment());
|
||||
|
||||
WaitCancelGuard guard(tdbb, lock, wait);
|
||||
ENQUEUE(tdbb, lock, level, wait);
|
||||
fb_assert(LCK_CHECK_LOCK(lock));
|
||||
if (!lock->lck_id)
|
||||
@ -548,6 +627,7 @@ bool LCK_lock(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait)
|
||||
case isc_deadlock:
|
||||
case isc_lock_conflict:
|
||||
case isc_lock_timeout:
|
||||
tdbb->checkCancelState(true);
|
||||
return false;
|
||||
case isc_lockmanerr:
|
||||
dbb->dbb_flags |= DBB_bugcheck;
|
||||
|
@ -32,6 +32,7 @@ namespace Jrd {
|
||||
}
|
||||
|
||||
void LCK_assert(Jrd::thread_db*, Jrd::Lock*);
|
||||
bool LCK_cancel_wait(Jrd::Attachment*);
|
||||
bool LCK_convert(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT);
|
||||
bool LCK_convert_opt(Jrd::thread_db*, Jrd::Lock*, USHORT);
|
||||
void LCK_downgrade(Jrd::thread_db*, Jrd::Lock*);
|
||||
|
@ -2044,6 +2044,7 @@ static int blocking_ast_attachment(void* ast_object)
|
||||
|
||||
attachment->att_flags |= ATT_shutdown;
|
||||
attachment->cancelExternalConnection(tdbb);
|
||||
LCK_cancel_wait(attachment);
|
||||
|
||||
JRD_shutdown_attachments(dbb);
|
||||
|
||||
|
@ -563,6 +563,7 @@ static bool shutdown_locks(thread_db* tdbb, SSHORT flag)
|
||||
{
|
||||
attachment->att_flags |= ATT_shutdown;
|
||||
attachment->cancelExternalConnection(tdbb);
|
||||
LCK_cancel_wait(attachment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1989,6 +1989,7 @@ static int blocking_ast_transaction(void* ast_object)
|
||||
transaction->tra_flags |= TRA_cancel_request;
|
||||
|
||||
att->cancelExternalConnection(tdbb);
|
||||
LCK_cancel_wait(att);
|
||||
}
|
||||
catch (const Firebird::Exception&)
|
||||
{} // no-op
|
||||
|
@ -829,6 +829,38 @@ void LockManager::repost(thread_db* tdbb, lock_ast_t ast, void* arg, SRQ_PTR own
|
||||
}
|
||||
|
||||
|
||||
bool LockManager::cancelWait(SRQ_PTR owner_offset)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* c a n c e l W a i t
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Wakeup waiting owner to make it check if wait should be cancelled.
|
||||
* As this routine could be called asyncronous, take extra care and
|
||||
* don't trust the input params blindly.
|
||||
*
|
||||
**************************************/
|
||||
LOCK_TRACE(("cancelWait (%ld)\n", owner_offset));
|
||||
|
||||
if (!owner_offset)
|
||||
return false;
|
||||
|
||||
Firebird::MutexLockGuard guard(m_localMutex);
|
||||
|
||||
acquire_shmem(DUMMY_OWNER);
|
||||
|
||||
own* owner = (own*) SRQ_ABS_PTR(owner_offset);
|
||||
if (owner->own_type == type_own)
|
||||
post_wakeup(owner);
|
||||
|
||||
release_shmem(DUMMY_OWNER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SLONG LockManager::queryData(SRQ_PTR parent_request, const USHORT series, const USHORT aggregate)
|
||||
{
|
||||
/**************************************
|
||||
@ -1196,7 +1228,7 @@ void LockManager::acquire_shmem(SRQ_PTR owner_offset)
|
||||
// the lock mutex. In that event, lets see if there is any unfinished work
|
||||
// left around that we need to finish up.
|
||||
|
||||
if (prior_active)
|
||||
if (prior_active > 0)
|
||||
{
|
||||
post_history(his_active, owner_offset, prior_active, (SRQ_PTR) 0, false);
|
||||
shb* const recover = (shb*) SRQ_ABS_PTR(m_header->lhb_secondary);
|
||||
@ -4033,7 +4065,9 @@ USHORT LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_w
|
||||
// See if we've waited beyond the lock timeout -
|
||||
// if so we mark our own request as rejected
|
||||
|
||||
if (lck_wait < 0 && lock_timeout <= current_time)
|
||||
const bool cancelled = tdbb->checkCancelState(false);
|
||||
|
||||
if (cancelled || lck_wait < 0 && lock_timeout <= current_time)
|
||||
{
|
||||
// We're going to reject our lock - it's the callers responsibility
|
||||
// to do cleanup and make sure post_pending() is called to wakeup
|
||||
@ -4041,6 +4075,7 @@ USHORT LockManager::wait_for_request(thread_db* tdbb, lrq* request, SSHORT lck_w
|
||||
request->lrq_flags |= LRQ_rejected;
|
||||
request->lrq_flags &= ~LRQ_pending;
|
||||
lock->lbl_pending_lrq_count--;
|
||||
|
||||
// and test - may be timeout due to missing process to deliver request
|
||||
probe_processes();
|
||||
release_shmem(owner_offset);
|
||||
|
@ -329,6 +329,7 @@ public:
|
||||
bool dequeue(const SRQ_PTR);
|
||||
|
||||
void repost(thread_db*, lock_ast_t, void*, SRQ_PTR);
|
||||
bool cancelWait(SRQ_PTR);
|
||||
|
||||
SLONG queryData(SRQ_PTR, const USHORT, const USHORT);
|
||||
SLONG readData(SRQ_PTR);
|
||||
|
Loading…
Reference in New Issue
Block a user