mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 22:03:03 +01:00
Backported CORE-5197: Segfault when process exits with active sweep thread
This commit is contained in:
parent
dacef52463
commit
b5ade14767
96
src/common/classes/XThreadMutex.h
Normal file
96
src/common/classes/XThreadMutex.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Alexander Peshkoff
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2020 Alexander Peshkoff <peshkoff@mail.ru>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_X_THREAD_MUTEX_H
|
||||
#define COMMON_X_THREAD_MUTEX_H
|
||||
|
||||
#include "../common/classes/Reasons.h"
|
||||
#include "../common/classes/RefMutex.h"
|
||||
#include "../common/classes/semaphore.h"
|
||||
|
||||
namespace Firebird
|
||||
{
|
||||
|
||||
// Non-recursive mutex that may be unlocked by any thread
|
||||
// Based on semaphore
|
||||
class XThreadMutex : private Semaphore, private Reasons
|
||||
{
|
||||
public:
|
||||
XThreadMutex()
|
||||
{
|
||||
Semaphore::release();
|
||||
#ifdef DEV_BUILD
|
||||
locked = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
~XThreadMutex()
|
||||
{
|
||||
fb_assert(!locked);
|
||||
}
|
||||
|
||||
void enter(const char* aReason)
|
||||
{
|
||||
Semaphore::enter();
|
||||
fb_assert(!locked);
|
||||
#ifdef DEV_BUILD
|
||||
locked = true;
|
||||
#endif
|
||||
reason(aReason);
|
||||
}
|
||||
|
||||
bool tryEnter(const char* aReason)
|
||||
{
|
||||
const bool ret = Semaphore::tryEnter();
|
||||
if (ret)
|
||||
{
|
||||
fb_assert(!locked);
|
||||
#ifdef DEV_BUILD
|
||||
locked = true;
|
||||
#endif
|
||||
reason(aReason);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void leave()
|
||||
{
|
||||
fb_assert(locked);
|
||||
#ifdef DEV_BUILD
|
||||
locked = false;
|
||||
#endif
|
||||
Semaphore::release();
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef DEV_BUILD
|
||||
bool locked;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef RaiiLockGuard<XThreadMutex> XThreadLockGuard;
|
||||
typedef EnsureUnlock<XThreadMutex, NotRefCounted<XThreadMutex> > XThreadEnsureUnlock;
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMON_X_THREAD_MUTEX_H
|
@ -337,16 +337,17 @@ typedef Mutex Spinlock;
|
||||
|
||||
|
||||
// RAII holder
|
||||
class MutexLockGuard
|
||||
template <typename M>
|
||||
class RaiiLockGuard
|
||||
{
|
||||
public:
|
||||
MutexLockGuard(Mutex& aLock, const char* aReason)
|
||||
RaiiLockGuard(M& aLock, const char* aReason)
|
||||
: lock(&aLock)
|
||||
{
|
||||
lock->enter(aReason);
|
||||
}
|
||||
|
||||
~MutexLockGuard()
|
||||
~RaiiLockGuard()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -360,12 +361,14 @@ public:
|
||||
|
||||
private:
|
||||
// Forbid copying
|
||||
MutexLockGuard(const MutexLockGuard&);
|
||||
MutexLockGuard& operator=(const MutexLockGuard&);
|
||||
RaiiLockGuard(const RaiiLockGuard&);
|
||||
RaiiLockGuard& operator=(const RaiiLockGuard&);
|
||||
|
||||
Mutex* lock;
|
||||
M* lock;
|
||||
};
|
||||
|
||||
typedef RaiiLockGuard<Mutex> MutexLockGuard;
|
||||
|
||||
class MutexUnlockGuard
|
||||
{
|
||||
public:
|
||||
|
@ -419,7 +419,7 @@ const ULONG ATT_creator = 0x08000L; // This attachment created the DB
|
||||
const ULONG ATT_monitor_done = 0x10000L; // Monitoring data is refreshed
|
||||
const ULONG ATT_security_db = 0x20000L; // Attachment used for security purposes
|
||||
const ULONG ATT_mapping = 0x40000L; // Attachment used for mapping auth block
|
||||
const ULONG ATT_crypt_thread = 0x80000L; // Attachment from crypt thread
|
||||
const ULONG ATT_from_thread = 0x80000L; // Attachment from internal special thread (sweep, crypt)
|
||||
const ULONG ATT_monitor_init = 0x100000L; // Attachment is registered in monitoring
|
||||
|
||||
const ULONG ATT_NO_CLEANUP = (ATT_no_cleanup | ATT_notify_gc);
|
||||
|
@ -286,7 +286,7 @@ namespace Jrd {
|
||||
slowIO(0),
|
||||
crypt(false),
|
||||
process(false),
|
||||
down(false),
|
||||
flDown(false),
|
||||
run(false)
|
||||
{
|
||||
stateLock = FB_NEW_RPT(getPool(), 0)
|
||||
@ -825,7 +825,7 @@ namespace Jrd {
|
||||
|
||||
void CryptoManager::terminateCryptThread(thread_db*, bool wait)
|
||||
{
|
||||
down = true;
|
||||
flDown = true;
|
||||
if (wait && cryptThreadId)
|
||||
{
|
||||
Thread::waitForCompletion(cryptThreadId);
|
||||
@ -962,114 +962,114 @@ namespace Jrd {
|
||||
writer.insertByte(isc_dpb_no_db_triggers, TRUE);
|
||||
|
||||
// Avoid races with release_attachment() in jrd.cpp
|
||||
MutexEnsureUnlock releaseGuard(cryptAttMutex, FB_FUNCTION);
|
||||
XThreadEnsureUnlock releaseGuard(dbb.dbb_thread_mutex, FB_FUNCTION);
|
||||
releaseGuard.enter();
|
||||
if (!down)
|
||||
if (!down())
|
||||
{
|
||||
AutoPlugin<JProvider> jInstance(JProvider::getInstance());
|
||||
jInstance->setDbCryptCallback(&status_vector, dbb.dbb_callback);
|
||||
check(&status_vector);
|
||||
AutoPlugin<JProvider> jInstance(JProvider::getInstance());
|
||||
jInstance->setDbCryptCallback(&status_vector, dbb.dbb_callback);
|
||||
check(&status_vector);
|
||||
|
||||
RefPtr<JAttachment> jAtt(REF_NO_INCR, jInstance->attachDatabase(&status_vector,
|
||||
dbb.dbb_database_name.c_str(), writer.getBufferLength(), writer.getBuffer()));
|
||||
check(&status_vector);
|
||||
RefPtr<JAttachment> jAtt(REF_NO_INCR, jInstance->attachDatabase(&status_vector,
|
||||
dbb.dbb_database_name.c_str(), writer.getBufferLength(), writer.getBuffer()));
|
||||
check(&status_vector);
|
||||
|
||||
MutexLockGuard attGuard(*(jAtt->getStable()->getMutex()), FB_FUNCTION);
|
||||
Attachment* att = jAtt->getHandle();
|
||||
if (!att)
|
||||
Arg::Gds(isc_att_shutdown).raise();
|
||||
att->att_flags |= ATT_crypt_thread;
|
||||
releaseGuard.leave();
|
||||
MutexLockGuard attGuard(*(jAtt->getStable()->getMutex()), FB_FUNCTION);
|
||||
Attachment* att = jAtt->getHandle();
|
||||
if (!att)
|
||||
Arg::Gds(isc_att_shutdown).raise();
|
||||
att->att_flags |= ATT_from_thread;
|
||||
releaseGuard.leave();
|
||||
|
||||
ThreadContextHolder tdbb(att->att_database, att, &status_vector);
|
||||
tdbb->markAsSweeper();
|
||||
ThreadContextHolder tdbb(att->att_database, att, &status_vector);
|
||||
tdbb->markAsSweeper();
|
||||
|
||||
DatabaseContextHolder dbHolder(tdbb);
|
||||
DatabaseContextHolder dbHolder(tdbb);
|
||||
|
||||
class UseCountHolder
|
||||
{
|
||||
public:
|
||||
explicit UseCountHolder(Attachment* a)
|
||||
: att(a)
|
||||
class UseCountHolder
|
||||
{
|
||||
att->att_use_count++;
|
||||
}
|
||||
~UseCountHolder()
|
||||
{
|
||||
att->att_use_count--;
|
||||
}
|
||||
private:
|
||||
Attachment* att;
|
||||
};
|
||||
UseCountHolder use_count(att);
|
||||
public:
|
||||
explicit UseCountHolder(Attachment* a)
|
||||
: att(a)
|
||||
{
|
||||
att->att_use_count++;
|
||||
}
|
||||
~UseCountHolder()
|
||||
{
|
||||
att->att_use_count--;
|
||||
}
|
||||
private:
|
||||
Attachment* att;
|
||||
};
|
||||
UseCountHolder use_count(att);
|
||||
|
||||
// get ready...
|
||||
AutoSetRestore<Attachment*> attSet(&cryptAtt, att);
|
||||
ULONG lastPage = getLastPage(tdbb);
|
||||
// get ready...
|
||||
AutoSetRestore<Attachment*> attSet(&cryptAtt, att);
|
||||
ULONG lastPage = getLastPage(tdbb);
|
||||
|
||||
do
|
||||
{
|
||||
// Check is there some job to do
|
||||
while (currentPage < lastPage)
|
||||
do
|
||||
{
|
||||
// Check is there some job to do
|
||||
while (currentPage < lastPage)
|
||||
{
|
||||
// forced terminate
|
||||
if (down())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// scheduling
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, true);
|
||||
|
||||
// nbackup state check
|
||||
BackupManager::StateReadGuard::lock(tdbb, 1);
|
||||
int bak_state = tdbb->getDatabase()->dbb_backup_manager->getState();
|
||||
BackupManager::StateReadGuard::unlock(tdbb);
|
||||
|
||||
if (bak_state != Ods::hdr_nbak_normal)
|
||||
{
|
||||
EngineCheckout checkout(tdbb, FB_FUNCTION);
|
||||
Thread::sleep(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
// writing page to disk will change it's crypt status in usual way
|
||||
WIN window(DB_PAGE_SPACE, currentPage);
|
||||
Ods::pag* page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined);
|
||||
if (page && page->pag_type <= pag_max &&
|
||||
(bool(page->pag_flags & Ods::crypted_page) != crypt) &&
|
||||
Ods::pag_crypt_page[page->pag_type])
|
||||
{
|
||||
CCH_MARK_MUST_WRITE(tdbb, &window);
|
||||
}
|
||||
CCH_RELEASE_TAIL(tdbb, &window);
|
||||
|
||||
// sometimes save currentPage into DB header
|
||||
++currentPage;
|
||||
if ((currentPage & 0x3FF) == 0)
|
||||
{
|
||||
writeDbHeader(tdbb, currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
// forced terminate
|
||||
if (down)
|
||||
if (down())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// scheduling
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, true);
|
||||
// At this moment of time all pages with number < lastpage
|
||||
// are guaranteed to change crypt state. Check for added pages.
|
||||
lastPage = getLastPage(tdbb);
|
||||
|
||||
// nbackup state check
|
||||
BackupManager::StateReadGuard::lock(tdbb, 1);
|
||||
int bak_state = tdbb->getDatabase()->dbb_backup_manager->getState();
|
||||
BackupManager::StateReadGuard::unlock(tdbb);
|
||||
} while (currentPage < lastPage);
|
||||
|
||||
if (bak_state != Ods::hdr_nbak_normal)
|
||||
{
|
||||
EngineCheckout checkout(tdbb, FB_FUNCTION);
|
||||
Thread::sleep(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
// writing page to disk will change it's crypt status in usual way
|
||||
WIN window(DB_PAGE_SPACE, currentPage);
|
||||
Ods::pag* page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined);
|
||||
if (page && page->pag_type <= pag_max &&
|
||||
(bool(page->pag_flags & Ods::crypted_page) != crypt) &&
|
||||
Ods::pag_crypt_page[page->pag_type])
|
||||
{
|
||||
CCH_MARK_MUST_WRITE(tdbb, &window);
|
||||
}
|
||||
CCH_RELEASE_TAIL(tdbb, &window);
|
||||
|
||||
// sometimes save currentPage into DB header
|
||||
++currentPage;
|
||||
if ((currentPage & 0x3FF) == 0)
|
||||
{
|
||||
writeDbHeader(tdbb, currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
// forced terminate
|
||||
if (down)
|
||||
// Finalize crypt
|
||||
if (!down())
|
||||
{
|
||||
break;
|
||||
writeDbHeader(tdbb, 0);
|
||||
}
|
||||
|
||||
// At this moment of time all pages with number < lastpage
|
||||
// are guaranteed to change crypt state. Check for added pages.
|
||||
lastPage = getLastPage(tdbb);
|
||||
|
||||
} while (currentPage < lastPage);
|
||||
|
||||
// Finalize crypt
|
||||
if (!down)
|
||||
{
|
||||
writeDbHeader(tdbb, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Release exclusive lock on StartCryptThread
|
||||
@ -1344,6 +1344,11 @@ namespace Jrd {
|
||||
return keyName.c_str();
|
||||
}
|
||||
|
||||
bool CryptoManager::down() const
|
||||
{
|
||||
return flDown || (dbb.dbb_flags & DBB_closing);
|
||||
}
|
||||
|
||||
void CryptoManager::addClumplet(string& signature, ClumpletReader& block, UCHAR tag)
|
||||
{
|
||||
if (block.find(tag))
|
||||
|
@ -42,8 +42,6 @@
|
||||
#include "../jrd/status.h"
|
||||
#include "firebird/Interface.h"
|
||||
|
||||
#define CRYPT_DEBUG(A)
|
||||
|
||||
// forward
|
||||
|
||||
class Config;
|
||||
@ -419,10 +417,9 @@ private:
|
||||
// normal operation.
|
||||
|
||||
SINT64 slowIO;
|
||||
bool crypt, process, down, run;
|
||||
bool crypt, process, flDown, run;
|
||||
|
||||
public:
|
||||
Firebird::Mutex cryptAttMutex;
|
||||
bool down() const;
|
||||
};
|
||||
|
||||
} // namespace Jrd
|
||||
|
@ -128,10 +128,21 @@ namespace Jrd
|
||||
Database* dbb = static_cast<Database*>(ast_object);
|
||||
AsyncContextHolder tdbb(dbb, FB_FUNCTION);
|
||||
|
||||
if ((dbb->dbb_flags & DBB_sweep_starting) && !(dbb->dbb_flags & DBB_sweep_in_progress))
|
||||
while (true)
|
||||
{
|
||||
dbb->dbb_flags &= ~DBB_sweep_starting;
|
||||
LCK_release(tdbb, dbb->dbb_sweep_lock);
|
||||
AtomicCounter::counter_type old = dbb->dbb_flags;
|
||||
if ((old & DBB_sweep_in_progress) || !(old & DBB_sweep_starting))
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "blocking_ast_sweep %p false wrong flags %lx\n", dbb, old));
|
||||
break;
|
||||
}
|
||||
|
||||
if (dbb->dbb_flags.compareExchange(old, old & ~DBB_sweep_starting))
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "blocking_ast_sweep true %p\n", dbb));
|
||||
dbb->dbb_thread_mutex.leave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Exception&)
|
||||
@ -153,6 +164,7 @@ namespace Jrd
|
||||
|
||||
bool Database::allowSweepThread(thread_db* tdbb)
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepThread %p\n", this));
|
||||
if (readOnly())
|
||||
return false;
|
||||
|
||||
@ -160,32 +172,74 @@ namespace Jrd
|
||||
if (attachment->att_flags & ATT_no_cleanup)
|
||||
return false;
|
||||
|
||||
if (!dbb_thread_mutex.tryEnter(FB_FUNCTION))
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepThread %p false, dbb_thread_mutex busy\n", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dbb_flags & DBB_closing)
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepThread false, dbb closing\n"));
|
||||
dbb_thread_mutex.leave();
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
AtomicCounter::counter_type old = dbb_flags;
|
||||
if ((old & (DBB_sweep_in_progress | DBB_sweep_starting)) || (dbb_ast_flags & DBB_shutdown))
|
||||
{
|
||||
dbb_thread_mutex.leave();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dbb_flags.compareExchange(old, old | DBB_sweep_starting))
|
||||
break;
|
||||
}
|
||||
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepThread - set DBB_sweep_starting\n"));
|
||||
|
||||
createSweepLock(tdbb);
|
||||
if (!LCK_lock(tdbb, dbb_sweep_lock, LCK_EX, LCK_NO_WAIT))
|
||||
{
|
||||
// clear lock error from status vector
|
||||
fb_utils::init_status(tdbb->tdbb_status_vector);
|
||||
|
||||
dbb_flags &= ~DBB_sweep_starting;
|
||||
clearSweepStarting();
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepThread - !LCK_lock\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepThread - TRUE\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::clearSweepStarting()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
AtomicCounter::counter_type old = dbb_flags;
|
||||
if (!(old & DBB_sweep_starting))
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "clearSweepStarting false %p\n", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dbb_flags.compareExchange(old, old & ~DBB_sweep_starting))
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "clearSweepStarting true %p\n", this));
|
||||
dbb_thread_mutex.leave();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Database::allowSweepRun(thread_db* tdbb)
|
||||
{
|
||||
if (readOnly())
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepRun %p\n", this));
|
||||
|
||||
if (readOnly() || (dbb_flags & DBB_closing))
|
||||
return false;
|
||||
|
||||
Jrd::Attachment* const attachment = tdbb->getAttachment();
|
||||
@ -196,14 +250,21 @@ namespace Jrd
|
||||
{
|
||||
AtomicCounter::counter_type old = dbb_flags;
|
||||
if (old & DBB_sweep_in_progress)
|
||||
{
|
||||
clearSweepStarting();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dbb_flags.compareExchange(old, old | DBB_sweep_in_progress))
|
||||
break;
|
||||
}
|
||||
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepRun - set DBB_sweep_in_progress\n"));
|
||||
|
||||
if (!(dbb_flags & DBB_sweep_starting))
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepRun - createSweepLock\n"));
|
||||
|
||||
createSweepLock(tdbb);
|
||||
if (!LCK_lock(tdbb, dbb_sweep_lock, LCK_EX, -1))
|
||||
{
|
||||
@ -215,20 +276,24 @@ namespace Jrd
|
||||
}
|
||||
}
|
||||
else
|
||||
dbb_flags &= ~DBB_sweep_starting;
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "allowSweepRun - clearSweepStarting\n"));
|
||||
attachment->att_flags |= ATT_from_thread;
|
||||
clearSweepStarting();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Database::clearSweepFlags(thread_db* tdbb)
|
||||
{
|
||||
if (!(dbb_flags & (DBB_sweep_starting | DBB_sweep_in_progress)))
|
||||
if (!(dbb_flags & DBB_sweep_in_progress))
|
||||
return;
|
||||
|
||||
if (dbb_sweep_lock)
|
||||
LCK_release(tdbb, dbb_sweep_lock);
|
||||
|
||||
dbb_flags &= ~(DBB_sweep_in_progress | DBB_sweep_starting);
|
||||
dbb_flags &= ~DBB_sweep_in_progress;
|
||||
}
|
||||
|
||||
void Database::registerModule(Module& module)
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "../common/classes/GenericMap.h"
|
||||
#include "../common/classes/RefCounted.h"
|
||||
#include "../common/classes/semaphore.h"
|
||||
#include "../common/classes/XThreadMutex.h"
|
||||
#include "../common/classes/DbImplementation.h"
|
||||
#include "../common/utils_proto.h"
|
||||
#include "../jrd/RandomGenerator.h"
|
||||
@ -68,6 +69,10 @@
|
||||
#include "../common/classes/Synchronize.h"
|
||||
#include "fb_types.h"
|
||||
|
||||
|
||||
#define SPTHR_DEBUG(A)
|
||||
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
template <typename T> class vec;
|
||||
@ -219,6 +224,7 @@ const ULONG DBB_no_fs_cache = 0x40000L; // Not using file system cache
|
||||
const ULONG DBB_sweep_starting = 0x80000L; // Auto-sweep is starting
|
||||
const ULONG DBB_creating = 0x100000L; // Database creation is in progress
|
||||
const ULONG DBB_shared = 0x200000L; // Database object is shared among connections
|
||||
const ULONG DBB_closing = 0x400000L; // Database closing, special backgroud threads should exit
|
||||
|
||||
//
|
||||
// dbb_ast_flags
|
||||
@ -467,6 +473,8 @@ public:
|
||||
SharedCounter dbb_shared_counter;
|
||||
CryptoManager* dbb_crypto_manager;
|
||||
Firebird::RefPtr<ExistenceRefMutex> dbb_init_fini;
|
||||
Firebird::XThreadMutex dbb_thread_mutex; // special threads start/stop mutex
|
||||
Thread::Handle dbb_sweep_thread;
|
||||
Firebird::RefPtr<Linger> dbb_linger_timer;
|
||||
unsigned dbb_linger_seconds;
|
||||
time_t dbb_linger_end;
|
||||
@ -522,6 +530,7 @@ private:
|
||||
dbb_external_file_directory_list(NULL),
|
||||
dbb_shared_counter(shared),
|
||||
dbb_init_fini(FB_NEW_POOL(*getDefaultMemoryPool()) ExistenceRefMutex()),
|
||||
dbb_sweep_thread(0),
|
||||
dbb_linger_seconds(0),
|
||||
dbb_linger_end(0),
|
||||
dbb_plugin_config(pConf)
|
||||
@ -561,8 +570,10 @@ public:
|
||||
bool allowSweepThread(thread_db* tdbb);
|
||||
// returns true if sweep could run
|
||||
bool allowSweepRun(thread_db* tdbb);
|
||||
// reset sweep flags and release sweep lock
|
||||
// reset sweep flag and release sweep lock
|
||||
void clearSweepFlags(thread_db* tdbb);
|
||||
// reset sweep starting flag, release thread starting mutex
|
||||
bool clearSweepStarting();
|
||||
|
||||
static void garbage_collector(Database* dbb);
|
||||
void exceptionHandler(const Firebird::Exception& ex, ThreadFinishSync<Database*>::ThreadRoutine* routine);
|
||||
|
@ -6451,54 +6451,67 @@ static void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment)
|
||||
|
||||
attachment->mergeStats();
|
||||
|
||||
// avoid races with crypt thread
|
||||
Mutex dummyMutex;
|
||||
MutexEnsureUnlock cryptGuard(dbb->dbb_crypto_manager ? dbb->dbb_crypto_manager->cryptAttMutex :
|
||||
dummyMutex, FB_FUNCTION);
|
||||
cryptGuard.enter();
|
||||
|
||||
Sync sync(&dbb->dbb_sync, "jrd.cpp: release_attachment");
|
||||
|
||||
// avoid races with special threads
|
||||
XThreadEnsureUnlock threadGuard(dbb->dbb_thread_mutex, FB_FUNCTION);
|
||||
threadGuard.enter();
|
||||
|
||||
sync.lock(SYNC_EXCLUSIVE);
|
||||
|
||||
// stop the crypt thread if we release last regular attachment
|
||||
Jrd::Attachment* crypt_att = NULL;
|
||||
// stop special threads if and only if we release last regular attachment
|
||||
bool other = false;
|
||||
CRYPT_DEBUG(fprintf(stderr, "\nrelease attachment=%p\n", attachment));
|
||||
for (Jrd::Attachment* att = dbb->dbb_attachments; att; att = att->att_next)
|
||||
{
|
||||
CRYPT_DEBUG(fprintf(stderr, "att=%p crypt_att=%p F=%c ", att, crypt_att, att->att_flags & ATT_crypt_thread ? '1' : '0'));
|
||||
if (att == attachment)
|
||||
{ // checkout scope
|
||||
EngineCheckout checkout(tdbb, FB_FUNCTION);
|
||||
|
||||
SPTHR_DEBUG(fprintf(stderr, "\nrelease attachment=%p\n", attachment));
|
||||
for (Jrd::Attachment* att = dbb->dbb_attachments; att; att = att->att_next)
|
||||
{
|
||||
CRYPT_DEBUG(fprintf(stderr, "self\n"));
|
||||
continue;
|
||||
SPTHR_DEBUG(fprintf(stderr, "att=%p FromThr=%c ", att, att->att_flags & ATT_from_thread ? '1' : '0'));
|
||||
if (att == attachment)
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "self\n"));
|
||||
continue;
|
||||
}
|
||||
if (att->att_flags & ATT_from_thread)
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "found special att=%p\n", att));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found attachment that is not current (to be released) and is not special
|
||||
other = true;
|
||||
SPTHR_DEBUG(fprintf(stderr, "other\n"));
|
||||
break;
|
||||
}
|
||||
if (att->att_flags & ATT_crypt_thread)
|
||||
|
||||
// Notify special threads
|
||||
if (!other)
|
||||
dbb->dbb_flags |= DBB_closing;
|
||||
threadGuard.leave();
|
||||
|
||||
// Sync with special threads
|
||||
if (!other)
|
||||
{
|
||||
crypt_att = att;
|
||||
CRYPT_DEBUG(fprintf(stderr, "found crypt_att=%p\n", crypt_att));
|
||||
continue;
|
||||
sync.unlock();
|
||||
|
||||
// crypt thread
|
||||
if (dbb->dbb_crypto_manager)
|
||||
dbb->dbb_crypto_manager->terminateCryptThread(tdbb, true);
|
||||
|
||||
// sweep thread
|
||||
if (dbb->dbb_sweep_thread)
|
||||
{
|
||||
Thread::waitForCompletion(dbb->dbb_sweep_thread);
|
||||
dbb->dbb_sweep_thread = 0;
|
||||
}
|
||||
}
|
||||
crypt_att = NULL;
|
||||
other = true;
|
||||
CRYPT_DEBUG(fprintf(stderr, "other\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (dbb->dbb_crypto_manager && !(other || crypt_att))
|
||||
dbb->dbb_crypto_manager->terminateCryptThread(tdbb, false);
|
||||
|
||||
cryptGuard.leave();
|
||||
|
||||
if (crypt_att)
|
||||
{
|
||||
sync.unlock();
|
||||
|
||||
CRYPT_DEBUG(fprintf(stderr, "crypt_att=%p terminateCryptThread\n", crypt_att));
|
||||
fb_assert(dbb->dbb_crypto_manager);
|
||||
dbb->dbb_crypto_manager->terminateCryptThread(tdbb, true);
|
||||
} // EngineCheckout scope
|
||||
|
||||
// restore database lock if needed
|
||||
if (!other)
|
||||
sync.lock(SYNC_EXCLUSIVE);
|
||||
}
|
||||
|
||||
// remove the attachment block from the dbb linked list
|
||||
for (Jrd::Attachment** ptr = &dbb->dbb_attachments; *ptr; ptr = &(*ptr)->att_next)
|
||||
|
@ -1789,6 +1789,13 @@ void TRA_sweep(thread_db* tdbb)
|
||||
|
||||
try {
|
||||
|
||||
// Avoid races with release_attachment()
|
||||
|
||||
XThreadEnsureUnlock releaseAttGuard(dbb->dbb_thread_mutex, FB_FUNCTION);
|
||||
releaseAttGuard.enter();
|
||||
if (dbb->dbb_flags & DBB_closing)
|
||||
return;
|
||||
|
||||
// Identify ourselves as a sweeper thread. This accomplishes two goals:
|
||||
// 1) Sweep transaction is started "precommitted" and
|
||||
// 2) Execution is throttled in JRD_reschedule() by
|
||||
@ -1817,6 +1824,11 @@ void TRA_sweep(thread_db* tdbb)
|
||||
|
||||
attachment->att_flags &= ~ATT_notify_gc;
|
||||
|
||||
// Mark our attachment as special one
|
||||
|
||||
attachment->att_flags |= ATT_from_thread;
|
||||
releaseAttGuard.leave();
|
||||
|
||||
if (VIO_sweep(tdbb, transaction, &traceSweep))
|
||||
{
|
||||
// At this point, we know that no record versions belonging to dead
|
||||
@ -2677,35 +2689,21 @@ static void start_sweeper(thread_db* tdbb)
|
||||
|
||||
TRA_update_counters(tdbb, dbb);
|
||||
|
||||
// allocate space for the string and a null at the end
|
||||
const char* pszFilename = tdbb->getAttachment()->att_filename.c_str();
|
||||
|
||||
char* database = (char*) gds__alloc(static_cast<SLONG>(strlen(pszFilename)) + 1);
|
||||
|
||||
if (database)
|
||||
// pass dbb to sweep thread - if allowSweepThread() returned TRUE that is safe
|
||||
try
|
||||
{
|
||||
strcpy(database, pszFilename);
|
||||
|
||||
try
|
||||
{
|
||||
Thread::start(sweep_database, database, THREAD_medium);
|
||||
return;
|
||||
}
|
||||
catch (const Firebird::Exception& ex)
|
||||
{
|
||||
gds__free(database);
|
||||
iscLogException("cannot start sweep thread", ex);
|
||||
}
|
||||
Thread::start(sweep_database, dbb, THREAD_medium, &dbb->dbb_sweep_thread);
|
||||
return;
|
||||
}
|
||||
else
|
||||
catch (const Firebird::Exception& ex)
|
||||
{
|
||||
ERR_log(0, 0, "cannot start sweep thread, Out of Memory");
|
||||
iscLogException("cannot start sweep thread", ex);
|
||||
dbb->clearSweepFlags(tdbb);
|
||||
}
|
||||
dbb->clearSweepFlags(tdbb);
|
||||
}
|
||||
|
||||
|
||||
static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM database)
|
||||
static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM d)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -2717,26 +2715,32 @@ static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM database)
|
||||
* Sweep database.
|
||||
*
|
||||
**************************************/
|
||||
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE);
|
||||
|
||||
dpb.insertByte(isc_dpb_sweep, isc_dpb_records);
|
||||
// use embedded authentication to attach database
|
||||
const char* szAuthenticator = "sweeper";
|
||||
dpb.insertString(isc_dpb_user_name, szAuthenticator, fb_strlen(szAuthenticator));
|
||||
|
||||
ISC_STATUS_ARRAY status_vector = {0};
|
||||
isc_db_handle db_handle = 0;
|
||||
|
||||
isc_attach_database(status_vector, 0, (const char*) database,
|
||||
&db_handle, dpb.getBufferLength(),
|
||||
reinterpret_cast<const char*>(dpb.getBuffer()));
|
||||
|
||||
if (db_handle)
|
||||
// determine database name
|
||||
// taking into an account that thread is started successfully
|
||||
// we should take care about parameters reference counter and DBB flags
|
||||
Database* dbb = (Database*) d;
|
||||
try
|
||||
{
|
||||
isc_detach_database(status_vector, &db_handle);
|
||||
}
|
||||
ISC_STATUS_ARRAY status_vector = {0};
|
||||
isc_db_handle db_handle = 0;
|
||||
|
||||
gds__free(database);
|
||||
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE);
|
||||
dpb.insertByte(isc_dpb_sweep, isc_dpb_records);
|
||||
// use embedded authentication to attach database
|
||||
const char* szAuthenticator = "sweeper";
|
||||
dpb.insertString(isc_dpb_user_name, szAuthenticator, fb_strlen(szAuthenticator));
|
||||
|
||||
isc_attach_database(status_vector, 0, dbb->dbb_database_name.c_str(),
|
||||
&db_handle, dpb.getBufferLength(),
|
||||
reinterpret_cast<const char*>(dpb.getBuffer()));
|
||||
if (db_handle)
|
||||
isc_detach_database(status_vector, &db_handle);
|
||||
}
|
||||
catch (const Exception&)
|
||||
{ }
|
||||
|
||||
dbb->clearSweepStarting(); // actually needed here only for classic,
|
||||
// but do danger calling for super
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3705,6 +3705,13 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
||||
|
||||
for (FB_SIZE_T i = 1; (vector = attachment->att_relations) && i < vector->count(); i++)
|
||||
{
|
||||
if (dbb->dbb_flags & DBB_closing)
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "VIO_sweep exits\n"));
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
relation = (*vector)[i];
|
||||
if (relation)
|
||||
relation = MET_lookup_relation_id(tdbb, i, false);
|
||||
@ -3735,6 +3742,12 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
|
||||
{
|
||||
CCH_RELEASE(tdbb, &rpb.getWindow(tdbb));
|
||||
|
||||
if (dbb->dbb_flags & DBB_closing)
|
||||
{
|
||||
SPTHR_DEBUG(fprintf(stderr, "VIO_sweep exits after VIO_next_record\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (relation->rel_flags & REL_deleting)
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user