GlobalRWLock::GlobalRWLock(thread_db* tdbb, MemoryPool& p, locktype_t lckType, lck_owner_t lock_owner, bool lock_caching, size_t lockLen, const UCHAR* lockStr) : PermanentStorage(p), pendingLock(0), readers(0), pendingWriters(0), currentWriter(false), lockCaching(lock_caching), blocking(false) { SET_TDBB(tdbb); cachedLock = FB_NEW_RPT(getPool(), lockLen) Lock(); cachedLock->lck_type = static_cast<lck_t>(lckType); cachedLock->lck_owner_handle = LCK_get_owner_handle_by_type(tdbb, lock_owner); cachedLock->lck_length = lockLen; Database* dbb = tdbb->getDatabase(); cachedLock->lck_dbb = dbb; cachedLock->lck_parent = dbb->dbb_lock; cachedLock->lck_object = this; cachedLock->lck_ast = lockCaching ? blocking_ast_cached_lock : NULL; memcpy(&cachedLock->lck_key, lockStr, lockLen); }
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); } }
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); } }
// Remove a routine from cache. void Routine::remove(thread_db* tdbb) { SET_TDBB(tdbb); Jrd::Attachment* att = tdbb->getAttachment(); // MET_procedure locks it. Lets unlock it now to avoid troubles later if (existenceLock) LCK_release(tdbb, existenceLock); // Routine that is being altered may have references // to it by other routines via pointer to current meta // data structure, so don't lose the structure or the pointer. if (checkCache(tdbb) && !(flags & Routine::FLAG_BEING_ALTERED)) clearCache(tdbb); // deallocate all structure which were allocated. The routine // blr is originally read into a new pool from which all request // are allocated. That will not be freed up. if (existenceLock) { delete existenceLock; existenceLock = NULL; } // deallocate input param structures for (Array<NestConst<Parameter> >::iterator i = getInputFields().begin(); i != getInputFields().end(); ++i) { if (*i) delete i->getObject(); } getInputFields().clear(); // deallocate output param structures for (Array<NestConst<Parameter> >::iterator i = getOutputFields().begin(); i != getOutputFields().end(); ++i) { if (*i) delete i->getObject(); } getOutputFields().clear(); if (!useCount) releaseFormat(); if (!(flags & Routine::FLAG_BEING_ALTERED) && useCount == 0) delete this; else { // Fully clear routine block. Some pieces of code check for empty // routine name and ID, this is why we do it. setName(QualifiedName()); setSecurityName(""); setId(0); setDefaultCount(0); } }