--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) Qxt Foundation. Some rights reserved.
+**
+** This file is part of the QxtCore module of the Qt eXTension library
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of th Common Public License, version 1.0, as published by
+** IBM.
+**
+** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
+** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
+** FITNESS FOR A PARTICULAR PURPOSE.
+**
+** You should have received a copy of the CPL along with this file.
+** See the LICENSE file and the cpl1.0.txt file included with the source
+** distribution for more information. If you did not receive a copy of the
+** license, contact the Qxt Foundation.
+**
+** <http://libqxt.sourceforge.net> <foundation@libqxt.org>
+**
+****************************************************************************/
+#include "qxtfilelock.h"
+#include "qxtfilelock_p.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+/*include pthread to make errno threadsafe*/
+#include <pthread.h>
+#include <errno.h>
+#include <QPointer>
+#include <QMutableLinkedListIterator>
+#include <QDebug>
+
+/*!
+ * \internal this class is used on *nix to register all locks created by a process and to let locks on *nix act like locks on windows
+ */
+class QxtFileLockRegistry
+{
+public:
+ bool registerLock(QxtFileLock *lock );
+ bool removeLock(QxtFileLock *lock );
+ static QxtFileLockRegistry& instance();
+
+private:
+ QLinkedList < QPointer<QxtFileLock> > procLocks;
+ QMutex registryMutex;
+ QxtFileLockRegistry();
+};
+
+QxtFileLockRegistry::QxtFileLockRegistry()
+{}
+
+QxtFileLockRegistry& QxtFileLockRegistry::instance()
+{
+ static QxtFileLockRegistry instance;
+ return instance;
+}
+
+/*!
+ * \internal this function locks the lockregistry and checks if there is a collision between the process locks
+ * \internal if there is no collision it inserts the lock into the registry and returns
+ * \internal return true for success
+ */
+bool QxtFileLockRegistry::registerLock(QxtFileLock * lock )
+{
+ QMutexLocker locker(&this->registryMutex);
+
+ QFile *fileToLock = lock ->file();
+
+ if (fileToLock)
+ {
+ struct stat fileInfo;
+ if ( fstat(fileToLock->handle(),&fileInfo) < 0 )
+ return false;
+
+ int newLockStart = lock ->offset();
+ int newLockEnd = lock ->offset()+lock ->length();
+
+ QMutableLinkedListIterator< QPointer<QxtFileLock> >iterator(this->procLocks);
+
+ while (iterator.hasNext())
+ {
+ QPointer<QxtFileLock> currLock = iterator.next();
+ if (currLock && currLock->file() && currLock->file()->isOpen())
+ {
+ struct stat currFileInfo;
+
+ /*first check if the current lock is on the same file*/
+ if ( fstat(currLock->file()->handle(),&currFileInfo) < 0 )
+ {
+ /*that should never happen because a closing file should remove all locks*/
+ Q_ASSERT(false);
+ continue;
+ }
+
+ if (currFileInfo.st_dev == fileInfo.st_dev && currFileInfo.st_ino == fileInfo.st_ino)
+ {
+ /*same file, check if our locks are in conflict*/
+ int currLockStart = currLock->offset();
+ int currLockEnd = currLock->offset()+currLock->length();
+
+ /*do we have to check for threads here?*/
+ if (newLockEnd >= currLockStart && newLockStart <= currLockEnd)
+ {
+ qDebug()<<"we may have a collision";
+ qDebug()<<newLockEnd<<" >= "<<currLockStart<<" && "<<newLockStart<<" <= "<<currLockEnd;
+
+ /*same lock region if one of both locks are exclusive we have a collision*/
+ if (lock ->mode() == QxtFileLock::WriteLockWait || lock ->mode() == QxtFileLock::WriteLock ||
+ currLock->mode() == QxtFileLock::WriteLockWait || currLock->mode() == QxtFileLock::WriteLock)
+ {
+ qDebug()<<"Okay if this is not the same thread using the same handle there is a collision";
+ /*the same thread can lock the same region with the same handle*/
+
+ qDebug()<<"! ("<<lock ->thread()<<" == "<<currLock->thread()<<" && "<<lock ->file()->handle()<<" == "<<currLock->file()->handle()<<")";
+
+ if (! (lock ->thread() == currLock->thread() && lock ->file()->handle() == currLock->file()->handle()))
+ {
+ qDebug()<<"Collision";
+ return false;
+ }
+ }
+ }
+ }
+ }
+ else //remove dead locks
+ iterator.remove();
+ }
+ qDebug()<<"The lock is okay";
+ /*here we can insert the lock into the list and return*/
+ procLocks.append(QPointer<QxtFileLock>(lock ));
+ return true;
+
+ }
+
+ return false;
+}
+
+bool QxtFileLockRegistry::removeLock(QxtFileLock * lock )
+{
+ QMutexLocker locker(&this->registryMutex);
+ procLocks.removeAll(lock );
+ return true;
+}
+
+bool QxtFileLock::unlock()
+{
+ if (file() && file()->isOpen() && isActive())
+ {
+ /*first remove real lock*/
+ int lockmode, locktype;
+ int result = -1;
+ struct flock lockDesc;
+
+ lockmode = F_SETLK;
+ locktype = F_UNLCK;
+
+ errno = 0;
+ do
+ {
+ lockDesc.l_type = locktype;
+ lockDesc.l_whence = SEEK_SET;
+ lockDesc.l_start = qxt_d().offset;
+ lockDesc.l_len = qxt_d().length;
+ lockDesc.l_pid = 0;
+ result = fcntl (this->file()->handle(), lockmode, &lockDesc);
+ }
+ while (result && errno == EINTR);
+
+ QxtFileLockRegistry::instance().removeLock(this);
+ qxt_d().isLocked = false;
+ return true;
+ }
+ return false;
+}
+
+bool QxtFileLock::lock ()
+{
+ if (file() && file()->isOpen() && !isActive())
+ {
+ /*this has to block if we can get no lock*/
+
+ bool locked = false;
+
+ while (1)
+ {
+ locked = QxtFileLockRegistry::instance().registerLock(this);
+ if (locked)
+ break;
+ else
+ {
+ if (qxt_d().mode == ReadLockWait || qxt_d().mode == WriteLockWait)
+ usleep(1000 * 5);
+ else
+ return false;
+ }
+ }
+
+ /*now get real lock*/
+ int lockmode,
+ locktype;
+
+ int result = -1;
+
+ struct flock lockDesc;
+
+ switch (qxt_d().mode)
+ {
+ case ReadLock:
+ lockmode = F_SETLK;
+ locktype = F_RDLCK;
+ break;
+
+ case ReadLockWait:
+ lockmode = F_SETLKW;
+ locktype = F_RDLCK;
+ break;
+
+ case WriteLock:
+ lockmode = F_SETLK;
+ locktype = F_WRLCK;
+ break;
+
+ case WriteLockWait:
+ lockmode = F_SETLKW;
+ locktype = F_WRLCK;
+ break;
+
+ default:
+ QxtFileLockRegistry::instance().removeLock(this);
+ return (false);
+ break;
+ }
+
+ errno = 0;
+ do
+ {
+ lockDesc.l_type = locktype;
+ lockDesc.l_whence = SEEK_SET;
+ lockDesc.l_start = qxt_d().offset;
+ lockDesc.l_len = qxt_d().length;
+ lockDesc.l_pid = 0;
+ result = fcntl (this->file()->handle(), lockmode, &lockDesc);
+ }
+ while (result && errno == EINTR);
+
+ /*we dot get the lock unregister from lockregistry and return*/
+ if (result == -1)
+ {
+ QxtFileLockRegistry::instance().removeLock(this);
+ return false;
+ }
+
+ qxt_d().isLocked = true;
+ return true;
+ }
+ return false;
+}
+