2003-08-06 18:30:49 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: Client/Server Common Code
|
|
|
|
* MODULE: rwlock.h
|
|
|
|
* DESCRIPTION: Read/write multi-state locks
|
|
|
|
*
|
2003-09-08 22:23:46 +02:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
* You may obtain a copy of the Licence at
|
|
|
|
* http://www.gnu.org/licences/lgpl.html
|
|
|
|
*
|
|
|
|
* As a special exception this file can also be included in modules
|
|
|
|
* with other source code as long as that source code has been
|
|
|
|
* released under an Open Source Initiative certificed licence.
|
|
|
|
* More information about OSI certification can be found at:
|
|
|
|
* http://www.opensource.org
|
|
|
|
*
|
|
|
|
* This module is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public Licence for more details.
|
|
|
|
*
|
|
|
|
* This module was created by members of the firebird development
|
|
|
|
* team. All individual contributions remain the Copyright (C) of
|
|
|
|
* those individuals and all rights are reserved. Contributors to
|
|
|
|
* this file are either listed below or can be obtained from a CVS
|
|
|
|
* history command.
|
2003-08-06 18:30:49 +02:00
|
|
|
*
|
2003-09-08 22:23:46 +02:00
|
|
|
* Created by: Nickolay Samofatov <skidder@bssys.com>
|
2003-08-06 18:30:49 +02:00
|
|
|
*
|
2003-09-08 22:23:46 +02:00
|
|
|
* Contributor(s):
|
|
|
|
*
|
2003-08-06 18:30:49 +02:00
|
|
|
*
|
2004-05-03 01:06:37 +02:00
|
|
|
* $Id: rwlock.h,v 1.18 2004-05-02 23:03:47 skidder Exp $
|
2003-08-06 18:30:49 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
#ifndef CLASSES_RWLOCK_H
|
|
|
|
#define CLASSES_RWLOCK_H
|
2003-08-06 18:30:49 +02:00
|
|
|
|
|
|
|
#ifdef WIN_NT
|
|
|
|
|
|
|
|
#include <windows.h>
|
2003-08-06 19:21:10 +02:00
|
|
|
#include <limits.h>
|
2003-08-06 18:30:49 +02:00
|
|
|
|
2004-03-26 00:12:50 +01:00
|
|
|
#include "../common/classes/fb_atomic.h"
|
|
|
|
|
2003-08-06 18:30:49 +02:00
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
namespace Firebird
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
|
2004-03-26 00:12:50 +01:00
|
|
|
const int LOCK_WRITER_OFFSET = 50000;
|
|
|
|
|
|
|
|
// Should work pretty fast if atomic operations are native.
|
|
|
|
// This is not the case for Win95
|
2003-08-06 18:30:49 +02:00
|
|
|
class RWLock {
|
|
|
|
private:
|
2004-03-26 00:12:50 +01:00
|
|
|
AtomicCounter lock; // This is the actual lock
|
2003-08-06 18:30:49 +02:00
|
|
|
// -50000 - writer is active
|
|
|
|
// 0 - noone owns the lock
|
|
|
|
// positive value - number of concurrent readers
|
2004-03-26 00:12:50 +01:00
|
|
|
AtomicCounter blockedReaders;
|
|
|
|
AtomicCounter blockedWriters;
|
2003-08-06 18:30:49 +02:00
|
|
|
HANDLE writers_event, readers_semaphore;
|
2003-09-13 03:12:45 +02:00
|
|
|
|
2004-05-03 01:06:37 +02:00
|
|
|
// Forbid copy constructor
|
|
|
|
RWLock(RWLock& source);
|
2003-08-06 18:30:49 +02:00
|
|
|
public:
|
2004-02-20 07:43:27 +01:00
|
|
|
RWLock() : lock(0), blockedReaders(0), blockedWriters(0)
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
readers_semaphore = CreateSemaphore(NULL, 0 /*initial count*/,
|
2003-08-06 19:21:10 +02:00
|
|
|
INT_MAX, NULL);
|
2004-03-01 04:35:23 +01:00
|
|
|
if (readers_semaphore == NULL)
|
|
|
|
system_call_failed::raise("CreateSemaphore");
|
2003-08-06 18:30:49 +02:00
|
|
|
writers_event = CreateEvent(NULL, FALSE/*auto-reset*/, FALSE, NULL);
|
2004-03-01 04:35:23 +01:00
|
|
|
if (writers_event == NULL)
|
|
|
|
system_call_failed::raise("CreateEvent");
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
~RWLock()
|
|
|
|
{
|
2004-03-11 06:30:07 +01:00
|
|
|
if (readers_semaphore && !CloseHandle(readers_semaphore))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("CloseHandle");
|
2004-03-11 06:30:07 +01:00
|
|
|
if (writers_event && !CloseHandle(writers_event))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("CloseHandle");
|
2004-02-20 07:43:27 +01:00
|
|
|
}
|
2003-09-11 23:26:20 +02:00
|
|
|
// Returns negative value if writer is active.
|
|
|
|
// Otherwise returns a number of readers
|
2004-02-20 07:43:27 +01:00
|
|
|
LONG getState() const
|
|
|
|
{
|
2004-03-26 00:12:50 +01:00
|
|
|
return lock.value();
|
2003-09-11 23:26:20 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void unblockWaiting()
|
|
|
|
{
|
2004-03-26 00:36:03 +01:00
|
|
|
if (blockedWriters.value()) {
|
2004-03-01 04:35:23 +01:00
|
|
|
if (!SetEvent(writers_event))
|
|
|
|
system_call_failed::raise("SetEvent");
|
|
|
|
}
|
2003-08-06 18:30:49 +02:00
|
|
|
else
|
2004-03-26 00:36:03 +01:00
|
|
|
if (blockedReaders.value()) {
|
|
|
|
if (!ReleaseSemaphore(readers_semaphore, blockedReaders.value(), NULL))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("ReleaseSemaphore");
|
|
|
|
}
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
bool tryBeginRead()
|
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
if (lock.value() < 0)
|
|
|
|
return false;
|
|
|
|
if (++lock > 0)
|
|
|
|
return true;
|
2003-08-06 18:30:49 +02:00
|
|
|
// We stepped on writer's toes. Fix our mistake
|
2004-03-26 00:12:50 +01:00
|
|
|
if (--lock == 0)
|
2003-08-06 18:30:49 +02:00
|
|
|
unblockWaiting();
|
|
|
|
return false;
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
bool tryBeginWrite()
|
|
|
|
{
|
2004-03-28 11:10:30 +02:00
|
|
|
if (lock.value())
|
|
|
|
return false;
|
|
|
|
if (lock.exchangeAdd(-LOCK_WRITER_OFFSET) == 0)
|
|
|
|
return true;
|
2003-08-06 18:30:49 +02:00
|
|
|
// We stepped on somebody's toes. Fix our mistake
|
2004-03-26 00:12:50 +01:00
|
|
|
if (lock.exchangeAdd(LOCK_WRITER_OFFSET) == -LOCK_WRITER_OFFSET)
|
2003-08-06 18:30:49 +02:00
|
|
|
unblockWaiting();
|
|
|
|
return false;
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void beginRead()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (!tryBeginRead()) {
|
2004-03-28 01:38:13 +01:00
|
|
|
++blockedReaders;
|
2003-08-06 18:30:49 +02:00
|
|
|
while (!tryBeginRead())
|
2004-03-01 04:35:23 +01:00
|
|
|
if (WaitForSingleObject(readers_semaphore, INFINITE) != WAIT_OBJECT_0)
|
|
|
|
system_call_failed::raise("WaitForSingleObject");
|
2004-03-26 00:36:03 +01:00
|
|
|
--blockedReaders;
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void beginWrite()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (!tryBeginWrite()) {
|
2004-03-26 00:36:03 +01:00
|
|
|
++blockedWriters;
|
2003-08-06 18:30:49 +02:00
|
|
|
while (!tryBeginWrite())
|
2004-03-01 04:35:23 +01:00
|
|
|
if (WaitForSingleObject(writers_event, INFINITE) != WAIT_OBJECT_0)
|
|
|
|
system_call_failed::raise("WaitForSingleObject");
|
2004-03-26 00:36:03 +01:00
|
|
|
--blockedWriters;
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void endRead()
|
|
|
|
{
|
2004-03-26 00:12:50 +01:00
|
|
|
if (--lock == 0)
|
2003-08-06 18:30:49 +02:00
|
|
|
unblockWaiting();
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void endWrite()
|
|
|
|
{
|
2004-03-26 00:12:50 +01:00
|
|
|
if (lock.exchangeAdd(LOCK_WRITER_OFFSET) == -LOCK_WRITER_OFFSET)
|
2003-08-06 18:30:49 +02:00
|
|
|
unblockWaiting();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2003-09-13 03:12:45 +02:00
|
|
|
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2003-08-10 17:43:23 +02:00
|
|
|
#ifdef MULTI_THREAD
|
2003-08-06 18:30:49 +02:00
|
|
|
|
2003-11-21 20:42:06 +01:00
|
|
|
#ifdef SOLARIS
|
|
|
|
|
|
|
|
#include <thread.h>
|
|
|
|
#include <synch.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
namespace Firebird
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
class RWLock
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
private:
|
|
|
|
rwlock_t lock;
|
2004-05-03 01:06:37 +02:00
|
|
|
// Forbid copy constructor
|
|
|
|
RWLock(const RWLock& source);
|
2003-11-21 20:42:06 +01:00
|
|
|
public:
|
2004-02-20 07:43:27 +01:00
|
|
|
RWLock()
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
if (rwlock_init(&lock, USYNC_PROCESS, NULL))
|
|
|
|
{
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rwlock_init");
|
2003-11-21 20:42:06 +01:00
|
|
|
}
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
~RWLock()
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
if (rwlock_destroy(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rwlock_destroy");
|
2003-11-21 20:42:06 +01:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void beginRead()
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
if (rw_rdlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rw_rdlock");
|
2003-11-21 20:42:06 +01:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
bool tryBeginRead()
|
|
|
|
{
|
|
|
|
const int code = rw_tryrdlock(&lock);
|
|
|
|
if (code == EBUSY)
|
|
|
|
return false;
|
|
|
|
if (code)
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rw_tryrdlock");
|
2003-11-21 20:42:06 +01:00
|
|
|
return true;
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void endRead()
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
if (rw_unlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rw_unlock");
|
2003-11-21 20:42:06 +01:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
bool tryBeginWrite()
|
|
|
|
{
|
|
|
|
const int code = rw_trywrlock(&lock);
|
|
|
|
if (code == EBUSY)
|
|
|
|
return false;
|
|
|
|
if (code)
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rw_trywrlock");
|
2003-11-21 20:42:06 +01:00
|
|
|
return true;
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void beginWrite()
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
if (rw_wrlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rw_wrlock");
|
2003-11-21 20:42:06 +01:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void endWrite()
|
|
|
|
{
|
2003-11-21 20:42:06 +01:00
|
|
|
if (rw_unlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("rw_unlock");
|
2003-11-21 20:42:06 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
} // namespace Firebird
|
2003-11-21 20:42:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2003-08-06 18:30:49 +02:00
|
|
|
#include <pthread.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
namespace Firebird
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
class RWLock
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
private:
|
|
|
|
pthread_rwlock_t lock;
|
2004-05-03 01:06:37 +02:00
|
|
|
// Forbid copy constructor
|
|
|
|
RWLock(const RWLock& source);
|
2003-08-06 18:30:49 +02:00
|
|
|
public:
|
|
|
|
RWLock() {
|
|
|
|
#ifdef LINUX
|
|
|
|
pthread_rwlockattr_t attr;
|
2004-03-01 04:35:23 +01:00
|
|
|
if (pthread_rwlockattr_init(&attr))
|
|
|
|
system_call_failed::raise("pthread_rwlockattr_init");
|
|
|
|
if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
|
|
|
|
system_call_failed::raise("pthread_rwlockattr_setkind_np");
|
|
|
|
if (pthread_rwlock_init(&lock, NULL))
|
|
|
|
system_call_failed::raise("pthread_rwlock_init");
|
|
|
|
if (pthread_rwlockattr_destroy(&attr))
|
|
|
|
system_call_failed::raise("pthread_rwlockattr_destroy");
|
2003-08-06 18:30:49 +02:00
|
|
|
#else
|
|
|
|
if (pthread_rwlock_init(&lock, NULL))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_init");
|
2003-08-06 18:30:49 +02:00
|
|
|
#endif
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
~RWLock()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (pthread_rwlock_destroy(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_destroy");
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void beginRead()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (pthread_rwlock_rdlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_rdlock");
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
bool tryBeginRead()
|
|
|
|
{
|
|
|
|
const int code = pthread_rwlock_tryrdlock(&lock);
|
|
|
|
if (code == EBUSY)
|
|
|
|
return false;
|
|
|
|
if (code)
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_tryrdlock");
|
2003-08-06 18:30:49 +02:00
|
|
|
return true;
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void endRead()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (pthread_rwlock_unlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_unlock");
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
bool tryBeginWrite()
|
|
|
|
{
|
|
|
|
const int code = pthread_rwlock_trywrlock(&lock);
|
|
|
|
if (code == EBUSY)
|
|
|
|
return false;
|
|
|
|
if (code)
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_trywrlock");
|
2003-08-06 18:30:49 +02:00
|
|
|
return true;
|
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void beginWrite()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (pthread_rwlock_wrlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_wrlock");
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
2004-02-20 07:43:27 +01:00
|
|
|
void endWrite()
|
|
|
|
{
|
2003-08-06 18:30:49 +02:00
|
|
|
if (pthread_rwlock_unlock(&lock))
|
2004-03-01 04:35:23 +01:00
|
|
|
system_call_failed::raise("pthread_rwlock_unlock");
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
} // namespace Firebird
|
|
|
|
|
2003-11-21 20:42:06 +01:00
|
|
|
#endif /*solaris*/
|
2004-05-03 01:06:37 +02:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
namespace Firebird {
|
|
|
|
|
|
|
|
// Non-threaded version
|
|
|
|
class RWLock
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
// Forbid copy constructor
|
|
|
|
RWLock(const RWLock& source);
|
|
|
|
public:
|
|
|
|
RWLock() {
|
|
|
|
}
|
|
|
|
~RWLock() { }
|
|
|
|
void beginRead() { }
|
|
|
|
bool tryBeginRead() { return true; }
|
|
|
|
void endRead() { }
|
|
|
|
bool tryBeginWrite() { return true; }
|
|
|
|
void beginWrite() { }
|
|
|
|
void endWrite() { }
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-08-10 17:43:23 +02:00
|
|
|
#endif /*MULTI_THREAD*/
|
2003-08-06 18:30:49 +02:00
|
|
|
|
|
|
|
#endif /*!WIN_NT*/
|
|
|
|
|
2004-05-03 01:06:37 +02:00
|
|
|
namespace Firebird {
|
|
|
|
|
|
|
|
// RAII holder of read lock
|
|
|
|
class ReadLockGuard {
|
|
|
|
public:
|
|
|
|
ReadLockGuard(RWLock &alock) : lock(&alock) { lock->beginRead(); }
|
|
|
|
~ReadLockGuard() { lock->endRead(); };
|
|
|
|
private:
|
|
|
|
// Forbid copy constructor
|
|
|
|
ReadLockGuard(const ReadLockGuard& source);
|
|
|
|
RWLock *lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
// RAII holder of write lock
|
|
|
|
class WriteLockGuard {
|
|
|
|
public:
|
|
|
|
WriteLockGuard(RWLock &alock) : lock(&alock) { lock->beginWrite(); }
|
|
|
|
~WriteLockGuard() { lock->endWrite(); };
|
|
|
|
private:
|
|
|
|
// Forbid copy constructor
|
|
|
|
WriteLockGuard(const WriteLockGuard& source);
|
|
|
|
RWLock *lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-03-18 06:56:06 +01:00
|
|
|
#endif // #ifndef CLASSES_RWLOCK_H
|
2004-02-20 07:43:27 +01:00
|
|
|
|