Skip to content

Commit

Permalink
FIxed bug CORE-2050 : Performance regression - too many semop() syste…
Browse files Browse the repository at this point in the history
…m calls
  • Loading branch information
hvlad committed Sep 2, 2008
1 parent d091c93 commit 5eff6e9
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 21 deletions.
5 changes: 5 additions & 0 deletions src/jrd/GlobalRWLock.h
Expand Up @@ -26,6 +26,9 @@
*
*/

#ifndef GLOBALRWLOCK_H
#define GLOBALRWLOCK_H

#include "../common/classes/alloc.h"
#include "../jrd/jrd.h"
#include "../jrd/lck.h"
Expand Down Expand Up @@ -168,3 +171,5 @@ class GlobalRWLock : public Firebird::PermanentStorage {
};

}

#endif // GLOBALRWLOCK_H
179 changes: 179 additions & 0 deletions src/jrd/LocksCache.h
@@ -0,0 +1,179 @@
/*
* 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 Vlad Khorsun
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2008 Vlad Khorsun
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/

#ifndef LOCKSCACHE_H
#define LOCKSCACHE_H

#include "firebird.h"
#include "fb_types.h"
#include "../common/classes/alloc.h"

#include "../jrd/GlobalRWLock.h"
#include "../jrd/lck.h"
#include "../jrd/que.h"

namespace Jrd {

struct KeyHolder
{
KeyHolder(const void* key, size_t size)
{
m_key = key;
m_size = size;
}

static bool greaterThan(const KeyHolder &i1, const KeyHolder &i2) {
fb_assert(i1.m_size == i2.m_size);
return memcmp(i1.m_key, i2.m_key, i1.m_size) > 0;
}

const void* m_key;
size_t m_size;
};

class CachedLock : public GlobalRWLock
{
public:
CachedLock(thread_db* tdbb, Firebird::MemoryPool& p, locktype_t lckType,
size_t lockLen, const UCHAR* lockStr) :
GlobalRWLock(tdbb, p, lckType, lockLen, lockStr)
{
QUE_INIT(m_lru);
}


const UCHAR* getLockKey() const
{
fb_assert(cached_lock);
return reinterpret_cast<UCHAR*> (&cached_lock->lck_key);
}

void setLockKey(thread_db *tdbb, const UCHAR* key)
{
while (!tryReleaseLock(tdbb))
{
lock(tdbb, LCK_write, LCK_WAIT);
unlock(tdbb, LCK_write);
}

memcpy(&cached_lock->lck_key, key, cached_lock->lck_length);
}

static const KeyHolder generate(const void*, const CachedLock* lock) {
return KeyHolder(lock->getLockKey(), lock->cached_lock->lck_length);
}

static bool greaterThan(const KeyHolder &i1, const KeyHolder &i2) {
return KeyHolder::greaterThan(i1, i2);
}

que m_lru;
};


// LockClass must be descendant of CachedLock
template <class LockClass = CachedLock>
class LocksCache
{
public:
LocksCache(Jrd::thread_db *tdbb, Jrd::lck_t lockType, size_t lockLen, size_t maxCapacity);
~LocksCache();

GlobalRWLock* get(thread_db *tdbb, const UCHAR* key);

private:
Firebird::MemoryPool &m_pool;
que m_lru;
lck_t m_lockType;
size_t m_lockLen;
size_t m_capacity;

Firebird::SortedArray<LockClass*, Firebird::EmptyStorage<LockClass*>,
const KeyHolder, LockClass, LockClass> m_sortedLocks;
};

// implementation

template <class LockClass>
LocksCache<LockClass>::LocksCache(thread_db *tdbb, lck_t lockType, size_t lockLen, size_t maxCapacity) :
m_pool(*tdbb->tdbb_database->dbb_permanent),
m_sortedLocks(m_pool, maxCapacity)
{
QUE_INIT(m_lru);
m_lockType = lockType;
m_lockLen = lockLen;
m_capacity = maxCapacity;
}

template <class LockClass>
LocksCache<LockClass>::~LocksCache()
{
while (QUE_NOT_EMPTY(m_lru))
{
QUE que_inst = m_lru.que_forward;
QUE_DELETE((*que_inst));
LockClass *lock = (LockClass*) ((SCHAR*) que_inst - OFFSET (LockClass*, m_lru));
delete lock;
}
}

template <class LockClass>
GlobalRWLock* LocksCache<LockClass>::get(thread_db *tdbb, const UCHAR* key)
{
LockClass* lock = NULL;
size_t pos;
if (m_sortedLocks.find(KeyHolder(key, m_lockLen), pos))
{
lock = m_sortedLocks[pos];
QUE_DELETE(lock->m_lru);
}
else
{
if (m_sortedLocks.getCount() < m_capacity) {
lock = FB_NEW (m_pool) LockClass(tdbb, m_pool, m_lockType, m_lockLen, key);
}
else
{
QUE que_inst = m_lru.que_backward;
QUE_DELETE((*que_inst));
lock = (LockClass*) ((SCHAR*) que_inst - OFFSET (LockClass*, m_lru));

bool found = (m_sortedLocks.find(KeyHolder(lock->getLockKey(), m_lockLen), pos));
fb_assert(found);

m_sortedLocks.remove(pos);
lock->setLockKey(tdbb, key);

found = (m_sortedLocks.find(KeyHolder(key, m_lockLen), pos));
fb_assert(!found);
}
m_sortedLocks.insert(pos, lock);
}
QUE_INSERT(m_lru, lock->m_lru);

return lock;
}

}; // namespace Jrd


#endif // LOCKSCACHE_H
73 changes: 52 additions & 21 deletions src/jrd/btr.cpp
Expand Up @@ -42,6 +42,8 @@
#include "gen/iberror.h"
#include "../jrd/common.h"
#include "../jrd/lck.h"
#include "../jrd/LocksCache.h"
#include "../jrd/GlobalRWLock.h"
#include "../jrd/cch.h"
#include "../jrd/sort.h"
#include "../jrd/gdsassert.h"
Expand Down Expand Up @@ -219,54 +221,83 @@ static void update_selectivity(index_root_page*, USHORT, const SelectivityList&)
static void checkForLowerKeySkip(bool&, const bool, const IndexNode&, const temporary_key&,
const index_desc&, const IndexRetrieval*);

class Jrd::BtrPageGCLock : public Lock

typedef LocksCache<CachedLock> BtrPageLocks;

void Database::destroyBtrLocks()
{
BtrPageLocks *locks = reinterpret_cast<BtrPageLocks*> (dbb_btr_page_locks);
delete locks;
dbb_btr_page_locks = NULL;
}


class Jrd::BtrPageGCLock
{
public:
explicit BtrPageGCLock(thread_db* tdbb)
{
Database* dbb = tdbb->tdbb_database;
lck_parent = dbb->dbb_lock;
lck_dbb = dbb;
lck_length = sizeof(SLONG);
lck_type = LCK_btr_dont_gc;
lck_owner_handle = LCK_get_owner_handle(tdbb, lck_type);
m_lock = NULL;
}

~BtrPageGCLock()
{
// assert in debug build
fb_assert(!lck_id);
fb_assert(!m_lock);

// lck_id might be set only if exception occurs
if (lck_id) {
LCK_release(JRD_get_thread_data(), this);
if (m_lock) {
enablePageGC(JRD_get_thread_data());
}
}

void disablePageGC(thread_db* tdbb, SLONG page)
static BtrPageLocks* getLocksCache(thread_db* tdbb)
{
lck_key.lck_long = page;
LCK_lock(tdbb, this, LCK_read, LCK_WAIT);
Database *dbb = tdbb->tdbb_database;
BtrPageLocks* locks = reinterpret_cast<BtrPageLocks*> (dbb->dbb_btr_page_locks);
if (!locks)
{
locks = FB_NEW (*dbb->dbb_permanent)
BtrPageLocks(tdbb, LCK_btr_dont_gc, sizeof(SLONG), 128);
dbb->dbb_btr_page_locks = locks;
}

return locks;
}

void disablePageGC(thread_db* tdbb, const SLONG page)
{
fb_assert(!m_lock);
const UCHAR *key = reinterpret_cast<const UCHAR*> (&page);

BtrPageLocks* locks = getLocksCache(tdbb);
m_lock = locks->get(tdbb, key);
m_lock->lock(tdbb, LCK_read, LCK_WAIT);
}

void enablePageGC(thread_db* tdbb)
{
LCK_release(tdbb, this);
fb_assert(m_lock);
m_lock->unlock(tdbb, LCK_read);
m_lock = NULL;
}

static bool isPageGCAllowed(thread_db* tdbb, SLONG page)
static bool isPageGCAllowed(thread_db* tdbb, const SLONG page)
{
BtrPageGCLock lock(tdbb);
lock.lck_key.lck_long = page;
const UCHAR *key = reinterpret_cast<const UCHAR*> (&page);

BtrPageLocks* locks = getLocksCache(tdbb);
GlobalRWLock *lock = locks->get(tdbb, key);

const bool res = LCK_lock(tdbb, &lock, LCK_write, LCK_NO_WAIT);
const bool res = lock->lock(tdbb, LCK_write, LCK_NO_WAIT);

if (res) {
LCK_release(tdbb, &lock);
lock->unlock(tdbb, LCK_write);
}

return res;
}

private:
GlobalRWLock *m_lock;
};


Expand Down
2 changes: 2 additions & 0 deletions src/jrd/jrd.cpp
Expand Up @@ -5998,6 +5998,8 @@ static void shutdown_database(Database* dbb, const bool release_pools)
if (dbb->dbb_retaining_lock)
LCK_release(tdbb, dbb->dbb_retaining_lock);

dbb->destroyBtrLocks();

if (dbb->dbb_lock)
LCK_release(tdbb, dbb->dbb_lock);

Expand Down
2 changes: 2 additions & 0 deletions src/jrd/jrd.h
Expand Up @@ -181,6 +181,7 @@ class Database : private pool_alloc<type_dbb>
vec<jrd_rel*>* dbb_relations; // relation vector
vec<jrd_prc*>* dbb_procedures; // scanned procedures
Lock* dbb_lock; // granddaddy lock
void* dbb_btr_page_locks; // per page locks for BTR
jrd_tra* dbb_sys_trans; // system transaction
jrd_file* dbb_file; // files for I/O operations
Shadow* dbb_shadow; // shadow control block
Expand Down Expand Up @@ -283,6 +284,7 @@ class Database : private pool_alloc<type_dbb>

// returns true if primary file is located on raw device
bool onRawDevice() const;
void destroyBtrLocks(); // defined in btr.cpp

private:
explicit Database(MemoryPool& p)
Expand Down

0 comments on commit 5eff6e9

Please sign in to comment.