1 /****************************************************************************
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
5 ** This file is part of the QxtCore module of the Qt eXTension library
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
21 ** <http://libqxt.sourceforge.net> <foundation@libqxt.org>
23 ****************************************************************************/
24 #include "qxtfilelock.h"
25 #include "qxtfilelock_p.h"
27 #include <sys/types.h>
31 /*include pthread to make errno threadsafe*/
35 #include <QMutableLinkedListIterator>
39 * \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
41 class QxtFileLockRegistry
44 bool registerLock(QxtFileLock *lock );
45 bool removeLock(QxtFileLock *lock );
46 static QxtFileLockRegistry& instance();
49 QLinkedList < QPointer<QxtFileLock> > procLocks;
51 QxtFileLockRegistry();
54 QxtFileLockRegistry::QxtFileLockRegistry()
57 QxtFileLockRegistry& QxtFileLockRegistry::instance()
59 static QxtFileLockRegistry instance;
64 * \internal this function locks the lockregistry and checks if there is a collision between the process locks
65 * \internal if there is no collision it inserts the lock into the registry and returns
66 * \internal return true for success
68 bool QxtFileLockRegistry::registerLock(QxtFileLock * lock )
70 QMutexLocker locker(&this->registryMutex);
72 QFile *fileToLock = lock ->file();
77 if ( fstat(fileToLock->handle(),&fileInfo) < 0 )
80 int newLockStart = lock ->offset();
81 int newLockEnd = lock ->offset()+lock ->length();
83 QMutableLinkedListIterator< QPointer<QxtFileLock> >iterator(this->procLocks);
85 while (iterator.hasNext())
87 QPointer<QxtFileLock> currLock = iterator.next();
88 if (currLock && currLock->file() && currLock->file()->isOpen())
90 struct stat currFileInfo;
92 /*first check if the current lock is on the same file*/
93 if ( fstat(currLock->file()->handle(),&currFileInfo) < 0 )
95 /*that should never happen because a closing file should remove all locks*/
100 if (currFileInfo.st_dev == fileInfo.st_dev && currFileInfo.st_ino == fileInfo.st_ino)
102 /*same file, check if our locks are in conflict*/
103 int currLockStart = currLock->offset();
104 int currLockEnd = currLock->offset()+currLock->length();
106 /*do we have to check for threads here?*/
107 if (newLockEnd >= currLockStart && newLockStart <= currLockEnd)
109 qDebug()<<"we may have a collision";
110 qDebug()<<newLockEnd<<" >= "<<currLockStart<<" && "<<newLockStart<<" <= "<<currLockEnd;
112 /*same lock region if one of both locks are exclusive we have a collision*/
113 if (lock ->mode() == QxtFileLock::WriteLockWait || lock ->mode() == QxtFileLock::WriteLock ||
114 currLock->mode() == QxtFileLock::WriteLockWait || currLock->mode() == QxtFileLock::WriteLock)
116 qDebug()<<"Okay if this is not the same thread using the same handle there is a collision";
117 /*the same thread can lock the same region with the same handle*/
119 qDebug()<<"! ("<<lock ->thread()<<" == "<<currLock->thread()<<" && "<<lock ->file()->handle()<<" == "<<currLock->file()->handle()<<")";
121 if (! (lock ->thread() == currLock->thread() && lock ->file()->handle() == currLock->file()->handle()))
123 qDebug()<<"Collision";
130 else //remove dead locks
133 qDebug()<<"The lock is okay";
134 /*here we can insert the lock into the list and return*/
135 procLocks.append(QPointer<QxtFileLock>(lock ));
143 bool QxtFileLockRegistry::removeLock(QxtFileLock * lock )
145 QMutexLocker locker(&this->registryMutex);
146 procLocks.removeAll(lock );
150 bool QxtFileLock::unlock()
152 if (file() && file()->isOpen() && isActive())
154 /*first remove real lock*/
155 int lockmode, locktype;
157 struct flock lockDesc;
165 lockDesc.l_type = locktype;
166 lockDesc.l_whence = SEEK_SET;
167 lockDesc.l_start = qxt_d().offset;
168 lockDesc.l_len = qxt_d().length;
170 result = fcntl (this->file()->handle(), lockmode, &lockDesc);
172 while (result && errno == EINTR);
174 QxtFileLockRegistry::instance().removeLock(this);
175 qxt_d().isLocked = false;
181 bool QxtFileLock::lock ()
183 if (file() && file()->isOpen() && !isActive())
185 /*this has to block if we can get no lock*/
191 locked = QxtFileLockRegistry::instance().registerLock(this);
196 if (qxt_d().mode == ReadLockWait || qxt_d().mode == WriteLockWait)
203 /*now get real lock*/
209 struct flock lockDesc;
211 switch (qxt_d().mode)
234 QxtFileLockRegistry::instance().removeLock(this);
242 lockDesc.l_type = locktype;
243 lockDesc.l_whence = SEEK_SET;
244 lockDesc.l_start = qxt_d().offset;
245 lockDesc.l_len = qxt_d().length;
247 result = fcntl (this->file()->handle(), lockmode, &lockDesc);
249 while (result && errno == EINTR);
251 /*we dot get the lock unregister from lockregistry and return*/
254 QxtFileLockRegistry::instance().removeLock(this);
258 qxt_d().isLocked = true;