Ok, the long awaited config wizard is here (at least in a very basic state). There...
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / core / qxtfilelock_unix.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtCore module of the Qt eXTension library
6 **
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
9 ** IBM.
10 **
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.
15 **
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.
20 **
21 ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24 #include "qxtfilelock.h"
25 #include "qxtfilelock_p.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 /*include pthread to make errno threadsafe*/
32 #include <pthread.h>
33 #include <errno.h>
34 #include <QPointer>
35 #include <QMutableLinkedListIterator>
36 #include <QDebug>
37
38 /*!
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
40  */
41 class QxtFileLockRegistry
42 {
43 public:
44     bool registerLock(QxtFileLock *lock );
45     bool removeLock(QxtFileLock *lock );
46     static QxtFileLockRegistry& instance();
47
48 private:
49     QLinkedList < QPointer<QxtFileLock> > procLocks;
50     QMutex registryMutex;
51     QxtFileLockRegistry();
52 };
53
54 QxtFileLockRegistry::QxtFileLockRegistry()
55 {}
56
57 QxtFileLockRegistry& QxtFileLockRegistry::instance()
58 {
59     static QxtFileLockRegistry instance;
60     return instance;
61 }
62
63 /*!
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
67  */
68 bool QxtFileLockRegistry::registerLock(QxtFileLock * lock )
69 {
70     QMutexLocker locker(&this->registryMutex);
71
72     QFile *fileToLock = lock ->file();
73
74     if (fileToLock)
75     {
76         struct stat fileInfo;
77         if ( fstat(fileToLock->handle(),&fileInfo) < 0 )
78             return false;
79
80         int newLockStart = lock ->offset();
81         int newLockEnd = lock ->offset()+lock ->length();
82
83         QMutableLinkedListIterator< QPointer<QxtFileLock> >iterator(this->procLocks);
84
85         while (iterator.hasNext())
86         {
87             QPointer<QxtFileLock> currLock = iterator.next();
88             if (currLock && currLock->file() && currLock->file()->isOpen())
89             {
90                 struct stat currFileInfo;
91
92                 /*first check if the current lock is on the same file*/
93                 if ( fstat(currLock->file()->handle(),&currFileInfo) < 0 )
94                 {
95                     /*that should never happen because a closing file should remove all locks*/
96                     Q_ASSERT(false);
97                     continue;
98                 }
99
100                 if (currFileInfo.st_dev == fileInfo.st_dev && currFileInfo.st_ino == fileInfo.st_ino)
101                 {
102                     /*same file, check if our locks are in conflict*/
103                     int currLockStart = currLock->offset();
104                     int currLockEnd = currLock->offset()+currLock->length();
105
106                     /*do we have to check for threads here?*/
107                     if (newLockEnd >= currLockStart  && newLockStart <= currLockEnd)
108                     {
109                         qDebug()<<"we may have a collision";
110                         qDebug()<<newLockEnd<<" >= "<<currLockStart<<"  &&  "<<newLockStart<<" <= "<<currLockEnd;
111
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)
115                             {
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*/
118
119                                 qDebug()<<"! ("<<lock ->thread()<<" == "<<currLock->thread()<<" && "<<lock ->file()->handle()<<" == "<<currLock->file()->handle()<<")";
120
121                                 if (! (lock ->thread() == currLock->thread() && lock ->file()->handle() == currLock->file()->handle()))
122                                     {
123                                         qDebug()<<"Collision";
124                                         return false;
125                                     }
126                             }
127                     }
128                 }
129             }
130             else //remove dead locks
131                 iterator.remove();
132         }
133         qDebug()<<"The lock is okay";
134         /*here we can insert the lock into the list and return*/
135         procLocks.append(QPointer<QxtFileLock>(lock ));
136         return true;
137
138     }
139
140     return false;
141 }
142
143 bool QxtFileLockRegistry::removeLock(QxtFileLock * lock )
144 {
145     QMutexLocker locker(&this->registryMutex);
146     procLocks.removeAll(lock );
147     return true;
148 }
149
150 bool QxtFileLock::unlock()
151 {
152     if (file() && file()->isOpen() && isActive())
153     {
154         /*first remove real lock*/
155         int lockmode,  locktype;
156         int result = -1;
157         struct  flock lockDesc;
158
159         lockmode = F_SETLK;
160         locktype = F_UNLCK;
161
162         errno = 0;
163         do
164         {
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;
169             lockDesc.l_pid = 0;
170             result = fcntl (this->file()->handle(), lockmode, &lockDesc);
171         }
172         while (result && errno == EINTR);
173
174         QxtFileLockRegistry::instance().removeLock(this);
175         qxt_d().isLocked = false;
176         return true;
177     }
178     return false;
179 }
180
181 bool QxtFileLock::lock ()
182 {
183     if (file() && file()->isOpen() && !isActive())
184     {
185         /*this has to block if we can get no lock*/
186
187         bool locked = false;
188
189         while (1)
190         {
191             locked = QxtFileLockRegistry::instance().registerLock(this);
192             if (locked)
193                 break;
194             else
195             {
196                 if (qxt_d().mode == ReadLockWait || qxt_d().mode == WriteLockWait)
197                     usleep(1000 * 5);
198                 else
199                     return false;
200             }
201         }
202
203         /*now get real lock*/
204         int lockmode,
205         locktype;
206
207         int result = -1;
208
209         struct  flock lockDesc;
210
211         switch (qxt_d().mode)
212         {
213         case    ReadLock:
214             lockmode = F_SETLK;
215             locktype = F_RDLCK;
216             break;
217
218         case    ReadLockWait:
219             lockmode = F_SETLKW;
220             locktype = F_RDLCK;
221             break;
222
223         case    WriteLock:
224             lockmode = F_SETLK;
225             locktype = F_WRLCK;
226             break;
227
228         case    WriteLockWait:
229             lockmode = F_SETLKW;
230             locktype = F_WRLCK;
231             break;
232
233         default:
234             QxtFileLockRegistry::instance().removeLock(this);
235             return (false);
236             break;
237         }
238
239         errno = 0;
240         do
241         {
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;
246             lockDesc.l_pid = 0;
247             result = fcntl (this->file()->handle(), lockmode, &lockDesc);
248         }
249         while (result && errno == EINTR);
250
251         /*we dot get the lock unregister from lockregistry and return*/
252         if (result == -1)
253         {
254             QxtFileLockRegistry::instance().removeLock(this);
255             return false;
256         }
257
258         qxt_d().isLocked = true;
259         return true;
260     }
261     return false;
262 }
263