bool Database::allowSweepThread(thread_db* tdbb) { if (readOnly()) return false; Jrd::Attachment* const attachment = tdbb->getAttachment(); if (attachment->att_flags & ATT_no_cleanup) return false; while (true) { AtomicCounter::counter_type old = dbb_flags; if ((old & (DBB_sweep_in_progress | DBB_sweep_starting)) || (dbb_ast_flags & DBB_shutdown)) return false; if (dbb_flags.compareExchange(old, old | DBB_sweep_starting)) break; } 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; return false; } return true; }
void CryptoManager::takeStateLock(thread_db* tdbb) { fb_assert(stateLock); fb_assert(tdbb->getAttachment()); if (needLock) { if (!LCK_lock(tdbb, stateLock, LCK_SR, LCK_WAIT)) { fb_assert(tdbb->tdbb_status_vector[1]); ERR_punt(); } fb_utils::init_status(tdbb->tdbb_status_vector); needLock = false; } }
SLONG Database::SharedCounter::generate(thread_db* tdbb, ULONG space, ULONG prefetch) { fb_assert(space < TOTAL_ITEMS); ValueCache* const counter = &m_counters[space]; if (m_localOnly) { SLONG result = 0; while ( !(result = (SLONG) ++counter->curVal) ) ; return result; } Database* const dbb = tdbb->getDatabase(); SyncLockGuard guard(&dbb->dbb_sh_counter_sync, SYNC_EXCLUSIVE, "Database::SharedCounter::generate"); if (!counter->lock) { Lock* const lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock(tdbb, sizeof(SLONG), LCK_shared_counter); counter->lock = lock; lock->lck_key.lck_long = space; LCK_lock(tdbb, lock, LCK_PW, LCK_WAIT); } SLONG result = (SLONG) ++counter->curVal; if (result > counter->maxVal) { LCK_convert(tdbb, counter->lock, LCK_PW, LCK_WAIT); result = LCK_read_data(tdbb, counter->lock); // zero IDs are somewhat special, so let's better skip them if (!result) result = 1; counter->curVal = result; counter->maxVal = result + prefetch - 1; LCK_write_data(tdbb, counter->lock, counter->maxVal + 1); LCK_convert(tdbb, counter->lock, LCK_SR, LCK_WAIT); } return result; }
SLONG Database::SharedCounter::generate(thread_db* tdbb, ULONG space, ULONG prefetch) { fb_assert(space < TOTAL_ITEMS); ValueCache* const counter = &m_counters[space]; Database* const dbb = tdbb->getDatabase(); if (!counter->lock) { Lock* const lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock(); counter->lock = lock; lock->lck_type = LCK_shared_counter; lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type); lock->lck_parent = dbb->dbb_lock; lock->lck_length = sizeof(SLONG); lock->lck_key.lck_long = space; lock->lck_dbb = dbb; lock->lck_ast = blockingAst; lock->lck_object = counter; LCK_lock(tdbb, lock, LCK_PW, LCK_WAIT); counter->curVal = 1; counter->maxVal = 0; } if (counter->curVal > counter->maxVal) { LCK_convert(tdbb, counter->lock, LCK_PW, LCK_WAIT); counter->curVal = LCK_read_data(tdbb, counter->lock); if (!counter->curVal) { // zero IDs are somewhat special, so let's better skip them counter->curVal = 1; } counter->maxVal = counter->curVal + prefetch - 1; LCK_write_data(tdbb, counter->lock, counter->maxVal + 1); } return counter->curVal++; }
bool GlobalRWLock::lockRead(thread_db* tdbb, SSHORT wait, const bool queueJump) { SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); bool needFetch; { // scope 1 Database::CheckoutLockGuard counterGuard(dbb, counterMutex); COS_TRACE(("(%p)->lockRead stage 1 readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); while (true) { if (readers > 0 && queueJump) { COS_TRACE(("(%p)->lockRead queueJump", this)); readers++; return true; } while (pendingWriters > 0 || currentWriter) { Database::Checkout checkoutDbb(dbb); writerFinished.wait(counterMutex); } COS_TRACE(("(%p)->lockRead stage 3 readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); if (!pendingLock) break; counterMutex.leave(); Database::Checkout checkoutDbb(dbb); counterMutex.enter(); } needFetch = cachedLock->lck_physical < LCK_read; if (!needFetch) { ++readers; return true; } ++pendingLock; fb_assert(cachedLock->lck_physical == LCK_none); } if (!LCK_lock(tdbb, cachedLock, LCK_read, wait)) { Database::CheckoutLockGuard counterGuard(dbb, counterMutex); --pendingLock; return false; } { // scope 2 Database::CheckoutLockGuard counterGuard(dbb, counterMutex); --pendingLock; ++readers; COS_TRACE(("(%p)->lockRead end readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); return fetch(tdbb); } }
bool GlobalRWLock::lockWrite(thread_db* tdbb, SSHORT wait) { SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); { // scope 1 Database::CheckoutLockGuard counterGuard(dbb, counterMutex); COS_TRACE(("(%p)->lockWrite stage 1 readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); ++pendingWriters; while (readers > 0 ) { Database::Checkout checkoutDbb(dbb); noReaders.wait(counterMutex); } COS_TRACE(("(%p)->lockWrite stage 2 readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); while (currentWriter || pendingLock) { Database::Checkout checkoutDbb(dbb); writerFinished.wait(counterMutex); } COS_TRACE(("(%p)->lockWrite stage 3 readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); fb_assert(!readers && !currentWriter); if (cachedLock->lck_physical > LCK_none) { LCK_release(tdbb, cachedLock); // To prevent self deadlock invalidate(tdbb); } ++pendingLock; } COS_TRACE(("(%p)->lockWrite LCK_lock readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d), pendingLock(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical, pendingLock)); if (!LCK_lock(tdbb, cachedLock, LCK_write, wait)) { Database::CheckoutLockGuard counterGuard(dbb, counterMutex); --pendingLock; if (--pendingWriters) { if (!currentWriter) writerFinished.notifyAll(); } return false; } { // scope 2 Database::CheckoutLockGuard counterGuard(dbb, counterMutex); --pendingLock; --pendingWriters; fb_assert(!currentWriter); currentWriter = true; COS_TRACE(("(%p)->lockWrite end readers(%d), blocking(%d), pendingWriters(%d), currentWriter(%d), lck_physical(%d)", this, readers, blocking, pendingWriters, currentWriter, cachedLock->lck_physical)); return fetch(tdbb); } }
void CryptoManager::cryptThread() { ISC_STATUS_ARRAY status_vector; try { // Try to take crypt mutex // If can't take that mutex - nothing to do, cryptThread already runs in our process MutexEnsureUnlock guard(cryptThreadMtx, FB_FUNCTION); if (!guard.tryEnter()) { return; } // establish context UserId user; user.usr_user_name = "(Crypt thread)"; Jrd::Attachment* const attachment = Jrd::Attachment::create(&dbb); RefPtr<SysAttachment> jAtt(new SysAttachment(attachment)); attachment->att_interface = jAtt; attachment->att_filename = dbb.dbb_filename; attachment->att_user = &user; BackgroundContextHolder tdbb(&dbb, attachment, status_vector, FB_FUNCTION); tdbb->tdbb_quantum = SWEEP_QUANTUM; ULONG lastPage = getLastPage(tdbb); ULONG runpage = 1; Stack<ULONG> pages; // Take exclusive threadLock // If can't take that lock - nothing to do, cryptThread already runs somewhere if (!LCK_lock(tdbb, threadLock, LCK_EX, LCK_NO_WAIT)) { return; } bool lckRelease = false; try { do { // Check is there some job to do while ((runpage = currentPage.exchangeAdd(+1)) < lastPage) { // forced terminate if (down) { break; } // nbackup state check if (dbb.dbb_backup_manager && dbb.dbb_backup_manager->getState() != nbak_state_normal) { if (currentPage.exchangeAdd(-1) >= lastPage) { // currentPage was set to last page by thread, closing database break; } THD_sleep(100); continue; } // scheduling if (--tdbb->tdbb_quantum < 0) { JRD_reschedule(tdbb, SWEEP_QUANTUM, true); } // writing page to disk will change it's crypt status in usual way WIN window(DB_PAGE_SPACE, runpage); 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(tdbb, &window); pages.push(runpage); } CCH_RELEASE_TAIL(tdbb, &window); // sometimes save currentPage into DB header ++runpage; if ((runpage & 0x3FF) == 0) { writeDbHeader(tdbb, runpage, pages); } } // At this moment of time all pages with number < lastpage // are guaranteed to change crypt state. Check for added pages. lastPage = getLastPage(tdbb); // forced terminate if (down) { break; } } while (runpage < lastPage); // Finalize crypt if (!down) { writeDbHeader(tdbb, 0, pages); } // Release exclusive lock on StartCryptThread lckRelease = true; LCK_release(tdbb, threadLock); } catch (const Exception&) { try { if (!lckRelease) { // try to save current state of crypt thread if (!down) { writeDbHeader(tdbb, runpage, pages); } // Release exclusive lock on StartCryptThread LCK_release(tdbb, threadLock); } } catch (const Exception&) { } throw; } } catch (const Exception& ex) { // Error during context creation - we can't even release lock iscLogException("Crypt thread:", ex); } }
void CryptoManager::startCryptThread(thread_db* tdbb) { // Try to take crypt mutex // If can't take that mutex - nothing to do, cryptThread already runs in our process MutexEnsureUnlock guard(cryptThreadMtx, FB_FUNCTION); if (!guard.tryEnter()) { return; } // Take exclusive threadLock // If can't take that lock - nothing to do, cryptThread already runs somewhere if (!LCK_lock(tdbb, threadLock, LCK_EX, LCK_NO_WAIT)) { // Cleanup lock manager error fb_utils::init_status(tdbb->tdbb_status_vector); return; } bool releasingLock = false; try { // Cleanup resources terminateCryptThread(tdbb); down = false; // Determine current page from the header Header hdr(tdbb, LCK_read); process = hdr->hdr_flags & Ods::hdr_crypt_process ? true : false; if (!process) { releasingLock = true; LCK_release(tdbb, threadLock); return; } currentPage.setValue(hdr->hdr_crypt_page); // Refresh encryption flag crypt = hdr->hdr_flags & Ods::hdr_encrypted ? true : false; // If we are going to start crypt thread, we need plugin to be loaded loadPlugin(hdr->hdr_crypt_plugin); // ready to go guard.leave(); // release in advance to avoid races with cryptThread() Thread::start(cryptThreadStatic, (THREAD_ENTRY_PARAM) this, 0, &cryptThreadId); } catch (const Firebird::Exception&) { if (!releasingLock) // avoid secondary exception in catch { try { LCK_release(tdbb, threadLock); } catch (const Firebird::Exception&) { } } throw; } }
void CryptoManager::changeCryptState(thread_db* tdbb, const Firebird::string& plugName) { if (plugName.length() > 31) { (Arg::Gds(isc_cp_name_too_long) << Arg::Num(31)).raise(); } bool newCryptState = plugName.hasData(); { // window scope Header hdr(tdbb, LCK_write); // Check header page for flags if (hdr->hdr_flags & Ods::hdr_crypt_process) { (Arg::Gds(isc_cp_process_active)).raise(); } bool headerCryptState = hdr->hdr_flags & Ods::hdr_encrypted; if (headerCryptState == newCryptState) { (Arg::Gds(isc_cp_already_crypted)).raise(); } fb_assert(stateLock); // Take exclusive stateLock bool ret = needLock ? LCK_lock(tdbb, stateLock, LCK_PW, LCK_WAIT) : LCK_convert(tdbb, stateLock, LCK_PW, LCK_WAIT); if (!ret) { fb_assert(tdbb->tdbb_status_vector[1]); ERR_punt(); } fb_utils::init_status(tdbb->tdbb_status_vector); needLock = false; // Load plugin if (newCryptState) { loadPlugin(plugName.c_str()); } crypt = newCryptState; // Write modified header page Ods::header_page* header = hdr.write(); if (crypt) { header->hdr_flags |= Ods::hdr_encrypted; plugName.copyTo(header->hdr_crypt_plugin, sizeof header->hdr_crypt_plugin); } else { header->hdr_flags &= ~Ods::hdr_encrypted; } header->hdr_flags |= Ods::hdr_crypt_process; process = true; } // Trigger lock on ChangeCryptState if (!LCK_convert(tdbb, stateLock, LCK_EX, LCK_WAIT)) { ERR_punt(); } if (!LCK_convert(tdbb, stateLock, LCK_SR, LCK_WAIT)) { ERR_punt(); } fb_utils::init_status(tdbb->tdbb_status_vector); // Now we may set hdr_crypt_page for crypt thread { // window scope Header hdr(tdbb, LCK_write); Ods::header_page* header = hdr.write(); header->hdr_crypt_page = 1; } startCryptThread(tdbb); }