mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 02:03:04 +01:00
Merge branch 'work/7809' into v4.0-release
This commit is contained in:
commit
9abf694a98
@ -178,7 +178,7 @@ public:
|
|||||||
#define USE_FCNTL
|
#define USE_FCNTL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class CountedFd;
|
class SharedFileInfo;
|
||||||
|
|
||||||
class FileLock
|
class FileLock
|
||||||
{
|
{
|
||||||
@ -186,8 +186,8 @@ public:
|
|||||||
enum LockMode {FLM_EXCLUSIVE, FLM_TRY_EXCLUSIVE, FLM_SHARED, FLM_TRY_SHARED};
|
enum LockMode {FLM_EXCLUSIVE, FLM_TRY_EXCLUSIVE, FLM_SHARED, FLM_TRY_SHARED};
|
||||||
|
|
||||||
typedef void InitFunction(int fd);
|
typedef void InitFunction(int fd);
|
||||||
explicit FileLock(const char* fileName, InitFunction* init = NULL); // main ctor
|
|
||||||
FileLock(const FileLock* main, int s); // creates additional lock for existing file
|
explicit FileLock(const char* fileName, InitFunction* init = NULL);
|
||||||
~FileLock();
|
~FileLock();
|
||||||
|
|
||||||
// Main function to lock file
|
// Main function to lock file
|
||||||
@ -196,24 +196,18 @@ public:
|
|||||||
// Alternative locker is using status vector to report errors
|
// Alternative locker is using status vector to report errors
|
||||||
bool setlock(Firebird::CheckStatusWrapper* status, const LockMode mode);
|
bool setlock(Firebird::CheckStatusWrapper* status, const LockMode mode);
|
||||||
|
|
||||||
// unlocking can only put error into log file - we can't throw in dtors
|
// Unlocking can only put error into log file - we can't throw in dtors
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
|
// Obvious access to file descriptor
|
||||||
int getFd();
|
int getFd();
|
||||||
|
|
||||||
private:
|
|
||||||
enum LockLevel {LCK_NONE, LCK_SHARED, LCK_EXCL};
|
enum LockLevel {LCK_NONE, LCK_SHARED, LCK_EXCL};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Firebird::RefPtr<SharedFileInfo> file;
|
||||||
|
InitFunction* initFunction;
|
||||||
LockLevel level;
|
LockLevel level;
|
||||||
CountedFd* oFile;
|
|
||||||
#ifdef USE_FCNTL
|
|
||||||
int lStart;
|
|
||||||
#endif
|
|
||||||
class CountedRWLock* rwcl; // Due to order of init in ctor rwcl must go after fd & start
|
|
||||||
|
|
||||||
Firebird::string getLockId();
|
|
||||||
class CountedRWLock* getRw();
|
|
||||||
void rwUnlock();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UNIX
|
#endif // UNIX
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
#include "../common/classes/GenericMap.h"
|
#include "../common/classes/GenericMap.h"
|
||||||
#include "../common/classes/RefMutex.h"
|
#include "../common/classes/RefMutex.h"
|
||||||
#include "../common/classes/array.h"
|
#include "../common/classes/array.h"
|
||||||
|
#include "../common/classes/condition.h"
|
||||||
#include "../common/StatusHolder.h"
|
#include "../common/StatusHolder.h"
|
||||||
|
|
||||||
static int process_id;
|
static int process_id;
|
||||||
@ -142,87 +143,26 @@ static bool event_blocked(const event_t* event, const SLONG value);
|
|||||||
|
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
|
|
||||||
static GlobalPtr<Mutex> openFdInit;
|
#ifdef FILELOCK_DEBUG
|
||||||
|
|
||||||
class DevNode
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
void DEB_FLOCK(const char* format, ...)
|
||||||
{
|
{
|
||||||
public:
|
va_list params;
|
||||||
DevNode()
|
va_start(params, format);
|
||||||
: f_dev(0), f_ino(0)
|
::vfprintf(stderr, format, params);
|
||||||
{ }
|
va_end(params);
|
||||||
|
}
|
||||||
|
|
||||||
DevNode(dev_t d, ino_t i)
|
#else
|
||||||
: f_dev(d), f_ino(i)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
DevNode(const DevNode& v)
|
void DEB_FLOCK(const char* format, ...) { }
|
||||||
: f_dev(v.f_dev), f_ino(v.f_ino)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
dev_t f_dev;
|
#endif //FILELOCK_DEBUG
|
||||||
ino_t f_ino;
|
|
||||||
|
|
||||||
bool operator==(const DevNode& v) const
|
|
||||||
{
|
|
||||||
return f_dev == v.f_dev && f_ino == v.f_ino;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator>(const DevNode& v) const
|
|
||||||
{
|
|
||||||
return f_dev > v.f_dev ? true :
|
|
||||||
f_dev < v.f_dev ? false :
|
|
||||||
f_ino > v.f_ino;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DevNode& operator=(const DevNode& v)
|
|
||||||
{
|
|
||||||
f_dev = v.f_dev;
|
|
||||||
f_ino = v.f_ino;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Firebird {
|
|
||||||
|
|
||||||
class CountedRWLock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CountedRWLock()
|
|
||||||
: sharedAccessCounter(0)
|
|
||||||
{ }
|
|
||||||
RWLock rwlock;
|
|
||||||
AtomicCounter cnt;
|
|
||||||
Mutex sharedAccessMutex;
|
|
||||||
int sharedAccessCounter;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CountedFd
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit CountedFd(int f)
|
|
||||||
: fd(f), useCount(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~CountedFd()
|
|
||||||
{
|
|
||||||
fb_assert(useCount == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd;
|
|
||||||
int useCount;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CountedFd(const CountedFd&);
|
|
||||||
const CountedFd& operator=(const CountedFd&);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Firebird
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
typedef GenericMap<Pair<Left<string, Firebird::CountedRWLock*> > > RWLocks;
|
|
||||||
GlobalPtr<RWLocks> rwlocks;
|
|
||||||
GlobalPtr<Mutex> rwlocksMutex;
|
|
||||||
#ifdef USE_FCNTL
|
#ifdef USE_FCNTL
|
||||||
const char* NAME = "fcntl";
|
const char* NAME = "fcntl";
|
||||||
#else
|
#else
|
||||||
@ -253,6 +193,44 @@ namespace {
|
|||||||
FileLock* lock;
|
FileLock* lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DevNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DevNode()
|
||||||
|
: f_dev(0), f_ino(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
DevNode(dev_t d, ino_t i)
|
||||||
|
: f_dev(d), f_ino(i)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
DevNode(const DevNode& v)
|
||||||
|
: f_dev(v.f_dev), f_ino(v.f_ino)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
dev_t f_dev;
|
||||||
|
ino_t f_ino;
|
||||||
|
|
||||||
|
bool operator==(const DevNode& v) const
|
||||||
|
{
|
||||||
|
return f_dev == v.f_dev && f_ino == v.f_ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const DevNode& v) const
|
||||||
|
{
|
||||||
|
return f_dev > v.f_dev ? true :
|
||||||
|
f_dev < v.f_dev ? false :
|
||||||
|
f_ino > v.f_ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DevNode& operator=(const DevNode& v)
|
||||||
|
{
|
||||||
|
f_dev = v.f_dev;
|
||||||
|
f_ino = v.f_ino;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
DevNode getNode(const char* name)
|
DevNode getNode(const char* name)
|
||||||
{
|
{
|
||||||
struct STAT statistics;
|
struct STAT statistics;
|
||||||
@ -279,80 +257,231 @@ namespace {
|
|||||||
return DevNode(statistics.st_dev, statistics.st_ino);
|
return DevNode(statistics.st_dev, statistics.st_ino);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlobalPtr<Mutex> openFdInit;
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
typedef GenericMap<Pair<NonPooled<DevNode, Firebird::CountedFd*> > > FdNodes;
|
namespace Firebird {
|
||||||
static GlobalPtr<Mutex> fdNodesMutex;
|
|
||||||
static GlobalPtr<FdNodes> fdNodes;
|
class SharedFileInfo : public RefCounted
|
||||||
|
{
|
||||||
|
SharedFileInfo(int f, const DevNode& id)
|
||||||
|
: counter(0), threadId(0), fd(f), devNode(id)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~SharedFileInfo()
|
||||||
|
{
|
||||||
|
fb_assert(sharedFilesMutex->locked());
|
||||||
|
fb_assert(counter == 0);
|
||||||
|
|
||||||
|
DEB_FLOCK("~ %p\n", this);
|
||||||
|
sharedFiles->remove(devNode);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static SharedFileInfo* get(const char* fileName)
|
||||||
|
{
|
||||||
|
DevNode id(getNode(fileName));
|
||||||
|
|
||||||
|
MutexLockGuard g(sharedFilesMutex, FB_FUNCTION);
|
||||||
|
|
||||||
|
SharedFileInfo* file = nullptr;
|
||||||
|
if (id.f_ino)
|
||||||
|
{
|
||||||
|
SharedFileInfo** got = sharedFiles->get(id);
|
||||||
|
if (got)
|
||||||
|
{
|
||||||
|
file = *got;
|
||||||
|
DEB_FLOCK("'%s': in map %p\n", fileName, file);
|
||||||
|
file->assertNonZero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
int fd = os_utils::openCreateSharedFile(fileName, 0);
|
||||||
|
id = getNode(fd);
|
||||||
|
file = FB_NEW_POOL(*getDefaultMemoryPool()) SharedFileInfo(fd, id);
|
||||||
|
SharedFileInfo** put = sharedFiles->put(id);
|
||||||
|
fb_assert(put);
|
||||||
|
*put = file;
|
||||||
|
DEB_FLOCK("'%s': new %p\n", fileName, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
file->addRef();
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
int release() const override
|
||||||
|
{
|
||||||
|
// Release should be executed under mutex protection
|
||||||
|
// in order to modify reference counter & map atomically
|
||||||
|
MutexLockGuard guard(sharedFilesMutex, FB_FUNCTION);
|
||||||
|
|
||||||
|
return RefCounted::release();
|
||||||
|
}
|
||||||
|
|
||||||
|
int lock(bool shared, bool wait, FileLock::InitFunction* init)
|
||||||
|
{
|
||||||
|
MutexEnsureUnlock guard(mutex, FB_FUNCTION);
|
||||||
|
if (wait)
|
||||||
|
guard.enter();
|
||||||
|
else if (!guard.tryEnter())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
DEB_FLOCK("%d lock %p %c%c\n", Thread::getId(), this, shared ? 's' : 'X', wait ? 'W' : 't');
|
||||||
|
|
||||||
|
while (counter != 0) // file lock belongs to our process
|
||||||
|
{
|
||||||
|
// check for compatible locks
|
||||||
|
if (shared && counter > 0)
|
||||||
|
{
|
||||||
|
// one more shared lock
|
||||||
|
++counter;
|
||||||
|
DEB_FLOCK("%d fast %p c=%d\n", Thread::getId(), this, counter);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((!shared) && counter < 0 && threadId == Thread::getId())
|
||||||
|
{
|
||||||
|
// recursive excl lock
|
||||||
|
--counter;
|
||||||
|
DEB_FLOCK("%d fast %p c=%d\n", Thread::getId(), this, counter);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// non compatible lock needed
|
||||||
|
// wait for another thread to release a lock
|
||||||
|
if (!wait)
|
||||||
|
{
|
||||||
|
DEB_FLOCK("%d failed internally %p c=%d rc -1\n", Thread::getId(), this, counter);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEB_FLOCK("%d wait %p c=%d\n", Thread::getId(), this, counter);
|
||||||
|
waitOn.wait(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take lock on a file
|
||||||
|
fb_assert(counter == 0);
|
||||||
|
#ifdef USE_FCNTL
|
||||||
|
struct FLOCK lock;
|
||||||
|
lock.l_type = shared ? F_RDLCK : F_WRLCK;
|
||||||
|
lock.l_whence = SEEK_SET;
|
||||||
|
lock.l_start = 0;
|
||||||
|
lock.l_len = 1;
|
||||||
|
if (fcntl(fd, wait ? F_SETLKW : F_SETLK, &lock) == -1)
|
||||||
|
{
|
||||||
|
int rc = errno;
|
||||||
|
if (!wait && (rc == EACCES || rc == EAGAIN))
|
||||||
|
rc = -1;
|
||||||
|
#else
|
||||||
|
if (flock(fd, (shared ? LOCK_SH : LOCK_EX) | (wait ? 0 : LOCK_NB)))
|
||||||
|
{
|
||||||
|
int rc = errno;
|
||||||
|
if (!wait && (rc == EWOULDBLOCK))
|
||||||
|
rc = -1;
|
||||||
|
#endif
|
||||||
|
DEB_FLOCK("%d failed on file %p c=%d rc %d\n", Thread::getId(), this, counter, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shared)
|
||||||
|
{
|
||||||
|
threadId = Thread::getId();
|
||||||
|
|
||||||
|
// call init() when needed
|
||||||
|
if (init && !shared)
|
||||||
|
init(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark lock as taken
|
||||||
|
counter = shared ? 1 : -1;
|
||||||
|
DEB_FLOCK("%d filelock %p c=%d\n", Thread::getId(), this, counter);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
fb_assert(counter != 0);
|
||||||
|
|
||||||
|
MutexEnsureUnlock guard(mutex, FB_FUNCTION);
|
||||||
|
guard.enter();
|
||||||
|
|
||||||
|
DEB_FLOCK("%d UNlock %p c=%d\n", Thread::getId(), this, counter);
|
||||||
|
|
||||||
|
if (counter < 0)
|
||||||
|
++counter;
|
||||||
|
else
|
||||||
|
--counter;
|
||||||
|
|
||||||
|
if (counter != 0)
|
||||||
|
{
|
||||||
|
DEB_FLOCK("%d done %p c=%d\n", Thread::getId(), this, counter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release file lock
|
||||||
|
#ifdef USE_FCNTL
|
||||||
|
struct FLOCK lock;
|
||||||
|
lock.l_type = F_UNLCK;
|
||||||
|
lock.l_whence = SEEK_SET;
|
||||||
|
lock.l_start = 0;
|
||||||
|
lock.l_len = 1;
|
||||||
|
|
||||||
|
if (fcntl(fd, F_SETLK, &lock) != 0)
|
||||||
|
{
|
||||||
|
#else
|
||||||
|
if (flock(fd, LOCK_UN) != 0)
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
LocalStatus ls;
|
||||||
|
CheckStatusWrapper local(&ls);
|
||||||
|
error(&local, NAME, errno);
|
||||||
|
iscLogStatus("Unlock error", &local);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEB_FLOCK("%d file-done %p\n", Thread::getId(), this);
|
||||||
|
waitOn.notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getFd()
|
||||||
|
{
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef GenericMap<Pair<NonPooled<DevNode, SharedFileInfo*> > > SharedFiles;
|
||||||
|
static GlobalPtr<SharedFiles> sharedFiles;
|
||||||
|
static GlobalPtr<Mutex> sharedFilesMutex;
|
||||||
|
|
||||||
|
Condition waitOn;
|
||||||
|
Mutex mutex;
|
||||||
|
int counter;
|
||||||
|
ThreadId threadId;
|
||||||
|
int fd;
|
||||||
|
DevNode devNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
GlobalPtr<SharedFileInfo::SharedFiles> SharedFileInfo::sharedFiles;
|
||||||
|
GlobalPtr<Mutex> SharedFileInfo::sharedFilesMutex;
|
||||||
|
|
||||||
|
} // namespace Firebird
|
||||||
|
|
||||||
|
|
||||||
FileLock::FileLock(const char* fileName, InitFunction* init)
|
FileLock::FileLock(const char* fileName, InitFunction* init)
|
||||||
: level(LCK_NONE), oFile(NULL),
|
: file(REF_NO_INCR, SharedFileInfo::get(fileName)), initFunction(init), level(LCK_NONE)
|
||||||
#ifdef USE_FCNTL
|
{ }
|
||||||
lStart(0),
|
|
||||||
#endif
|
|
||||||
rwcl(NULL)
|
|
||||||
{
|
|
||||||
MutexLockGuard g(fdNodesMutex, FB_FUNCTION);
|
|
||||||
|
|
||||||
DevNode id(getNode(fileName));
|
|
||||||
|
|
||||||
if (id.f_ino)
|
|
||||||
{
|
|
||||||
CountedFd** got = fdNodes->get(id);
|
|
||||||
if (got)
|
|
||||||
{
|
|
||||||
oFile = *got;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!oFile)
|
|
||||||
{
|
|
||||||
int fd = os_utils::openCreateSharedFile(fileName, 0);
|
|
||||||
oFile = FB_NEW_POOL(*getDefaultMemoryPool()) CountedFd(fd);
|
|
||||||
CountedFd** put = fdNodes->put(getNode(fd));
|
|
||||||
fb_assert(put);
|
|
||||||
*put = oFile;
|
|
||||||
|
|
||||||
if (init)
|
|
||||||
{
|
|
||||||
init(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rwcl = getRw();
|
|
||||||
++(oFile->useCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FileLock::~FileLock()
|
FileLock::~FileLock()
|
||||||
{
|
{
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
{ // guard scope
|
|
||||||
MutexLockGuard g(rwlocksMutex, FB_FUNCTION);
|
|
||||||
|
|
||||||
if (--(rwcl->cnt) == 0)
|
|
||||||
{
|
|
||||||
rwlocks->remove(getLockId());
|
|
||||||
delete rwcl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ // guard scope
|
|
||||||
MutexLockGuard g(fdNodesMutex, FB_FUNCTION);
|
|
||||||
|
|
||||||
if (--(oFile->useCount) == 0)
|
|
||||||
{
|
|
||||||
fdNodes->remove(getNode(oFile->fd));
|
|
||||||
close(oFile->fd);
|
|
||||||
delete oFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileLock::getFd()
|
int FileLock::getFd()
|
||||||
{
|
{
|
||||||
return oFile->fd;
|
return file->getFd();
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileLock::setlock(const LockMode mode)
|
int FileLock::setlock(const LockMode mode)
|
||||||
@ -383,99 +512,11 @@ int FileLock::setlock(const LockMode mode)
|
|||||||
return wait ? EBUSY : -1;
|
return wait ? EBUSY : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// first take appropriate rwlock to avoid conflicts with other threads in our process
|
int rc = file->lock(shared, wait, initFunction);
|
||||||
bool rc = true;
|
if (rc == 0) // lock taken
|
||||||
try
|
level = newLevel;
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case FLM_TRY_EXCLUSIVE:
|
|
||||||
rc = rwcl->rwlock.tryBeginWrite(FB_FUNCTION);
|
|
||||||
break;
|
|
||||||
case FLM_EXCLUSIVE:
|
|
||||||
rwcl->rwlock.beginWrite(FB_FUNCTION);
|
|
||||||
break;
|
|
||||||
case FLM_TRY_SHARED:
|
|
||||||
rc = rwcl->rwlock.tryBeginRead(FB_FUNCTION);
|
|
||||||
break;
|
|
||||||
case FLM_SHARED:
|
|
||||||
rwcl->rwlock.beginRead(FB_FUNCTION);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const system_call_failed& fail)
|
|
||||||
{
|
|
||||||
return fail.getErrorCode();
|
|
||||||
}
|
|
||||||
if (!rc)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For shared lock we must take into an account reenterability
|
return rc;
|
||||||
MutexEnsureUnlock guard(rwcl->sharedAccessMutex, FB_FUNCTION);
|
|
||||||
if (shared)
|
|
||||||
{
|
|
||||||
if (wait)
|
|
||||||
{
|
|
||||||
guard.enter();
|
|
||||||
}
|
|
||||||
else if (!guard.tryEnter())
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fb_assert(rwcl->sharedAccessCounter >= 0);
|
|
||||||
if (rwcl->sharedAccessCounter++ > 0)
|
|
||||||
{
|
|
||||||
// counter is non-zero - we already have file lock
|
|
||||||
level = LCK_SHARED;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_FCNTL
|
|
||||||
// Take lock on a file
|
|
||||||
struct FLOCK lock;
|
|
||||||
lock.l_type = shared ? F_RDLCK : F_WRLCK;
|
|
||||||
lock.l_whence = SEEK_SET;
|
|
||||||
lock.l_start = lStart;
|
|
||||||
lock.l_len = 1;
|
|
||||||
if (fcntl(oFile->fd, wait ? F_SETLKW : F_SETLK, &lock) == -1)
|
|
||||||
{
|
|
||||||
int rc = errno;
|
|
||||||
if (!wait && (rc == EACCES || rc == EAGAIN))
|
|
||||||
{
|
|
||||||
rc = -1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (flock(oFile->fd, (shared ? LOCK_SH : LOCK_EX) | (wait ? 0 : LOCK_NB)))
|
|
||||||
{
|
|
||||||
int rc = errno;
|
|
||||||
if (!wait && (rc == EWOULDBLOCK))
|
|
||||||
{
|
|
||||||
rc = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (shared)
|
|
||||||
{
|
|
||||||
rwcl->sharedAccessCounter--;
|
|
||||||
rwcl->rwlock.endRead();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
rwcl->rwlock.endWrite();
|
|
||||||
}
|
|
||||||
catch (const Exception&)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
level = newLevel;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileLock::setlock(CheckStatusWrapper* status, const LockMode mode)
|
bool FileLock::setlock(CheckStatusWrapper* status, const LockMode mode)
|
||||||
@ -492,25 +533,6 @@ bool FileLock::setlock(CheckStatusWrapper* status, const LockMode mode)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLock::rwUnlock()
|
|
||||||
{
|
|
||||||
fb_assert(level != LCK_NONE);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (level == LCK_SHARED)
|
|
||||||
rwcl->rwlock.endRead();
|
|
||||||
else
|
|
||||||
rwcl->rwlock.endWrite();
|
|
||||||
}
|
|
||||||
catch (const Exception& ex)
|
|
||||||
{
|
|
||||||
iscLogException("rwlock end-operation error", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
level = LCK_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLock::unlock()
|
void FileLock::unlock()
|
||||||
{
|
{
|
||||||
if (level == LCK_NONE)
|
if (level == LCK_NONE)
|
||||||
@ -518,97 +540,8 @@ void FileLock::unlock()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For shared lock we must take into an account reenterability
|
file->unlock();
|
||||||
MutexEnsureUnlock guard(rwcl->sharedAccessMutex, FB_FUNCTION);
|
level = LCK_NONE;
|
||||||
if (level == LCK_SHARED)
|
|
||||||
{
|
|
||||||
guard.enter();
|
|
||||||
|
|
||||||
fb_assert(rwcl->sharedAccessCounter > 0);
|
|
||||||
if (--(rwcl->sharedAccessCounter) > 0)
|
|
||||||
{
|
|
||||||
// counter is non-zero - we must keep file lock
|
|
||||||
rwUnlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_FCNTL
|
|
||||||
struct FLOCK lock;
|
|
||||||
lock.l_type = F_UNLCK;
|
|
||||||
lock.l_whence = SEEK_SET;
|
|
||||||
lock.l_start = lStart;
|
|
||||||
lock.l_len = 1;
|
|
||||||
|
|
||||||
if (fcntl(oFile->fd, F_SETLK, &lock) != 0)
|
|
||||||
{
|
|
||||||
#else
|
|
||||||
if (flock(oFile->fd, LOCK_UN) != 0)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
LocalStatus ls;
|
|
||||||
CheckStatusWrapper local(&ls);
|
|
||||||
error(&local, NAME, errno);
|
|
||||||
iscLogStatus("Unlock error", &local);
|
|
||||||
}
|
|
||||||
|
|
||||||
rwUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
string FileLock::getLockId()
|
|
||||||
{
|
|
||||||
fb_assert(oFile);
|
|
||||||
|
|
||||||
DevNode id(getNode(oFile->fd));
|
|
||||||
|
|
||||||
const size_t len1 = sizeof(id.f_dev);
|
|
||||||
const size_t len2 = sizeof(id.f_ino);
|
|
||||||
#ifdef USE_FCNTL
|
|
||||||
const size_t len3 = sizeof(int);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
string rc(len1 + len2
|
|
||||||
#ifdef USE_FCNTL
|
|
||||||
+ len3
|
|
||||||
#endif
|
|
||||||
, ' ');
|
|
||||||
char* p = rc.begin();
|
|
||||||
|
|
||||||
memcpy(p, &id.f_dev, len1);
|
|
||||||
p += len1;
|
|
||||||
memcpy(p, &id.f_ino, len2);
|
|
||||||
#ifdef USE_FCNTL
|
|
||||||
p += len2;
|
|
||||||
memcpy(p, &lStart, len3);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountedRWLock* FileLock::getRw()
|
|
||||||
{
|
|
||||||
string id = getLockId();
|
|
||||||
CountedRWLock* rc = NULL;
|
|
||||||
|
|
||||||
MutexLockGuard g(rwlocksMutex, FB_FUNCTION);
|
|
||||||
|
|
||||||
CountedRWLock** got = rwlocks->get(id);
|
|
||||||
if (got)
|
|
||||||
{
|
|
||||||
rc = *got;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rc)
|
|
||||||
{
|
|
||||||
rc = FB_NEW_POOL(*getDefaultMemoryPool()) CountedRWLock;
|
|
||||||
CountedRWLock** put = rwlocks->put(id);
|
|
||||||
fb_assert(put);
|
|
||||||
*put = rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
++(rc->cnt);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // UNIX
|
#endif // UNIX
|
||||||
|
Loading…
Reference in New Issue
Block a user