We now have a current svn snapshot of libqxt in our contrib dir, and
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / core / qxtfilelock_unix.cpp
diff --git a/src/contrib/libqxt-2007-10-24/src/core/qxtfilelock_unix.cpp b/src/contrib/libqxt-2007-10-24/src/core/qxtfilelock_unix.cpp
new file mode 100644 (file)
index 0000000..c884768
--- /dev/null
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** 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;
+}
+