1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 **
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of a Qt Solutions component.
9 **
10 ** You may use this file under the terms of the BSD license as follows:
11 **
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
14 ** met:
15 **   * Redistributions of source code must retain the above copyright
16 **     notice, this list of conditions and the following disclaimer.
17 **   * Redistributions in binary form must reproduce the above copyright
18 **     notice, this list of conditions and the following disclaimer in
19 **     the documentation and/or other materials provided with the
20 **     distribution.
21 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 **     the names of its contributors may be used to endorse or promote
23 **     products derived from this software without specific prior written
24 **     permission.
25 **
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 **
38 ****************************************************************************/
39 
40 #include "qtlockedfile.h"
41 #include <qt_windows.h>
42 #include <QtCore/QFileInfo>
43 
44 #define MUTEX_PREFIX "QtLockedFile mutex "
45 // Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
46 #define MAX_READERS MAXIMUM_WAIT_OBJECTS
47 
getMutexHandle(int idx,bool doCreate)48 Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
49 {
50     if (mutexname.isEmpty()) {
51         QFileInfo fi(*this);
52         mutexname = QString::fromLatin1(MUTEX_PREFIX)
53                     + fi.absoluteFilePath().toLower();
54     }
55     QString mname(mutexname);
56     if (idx >= 0)
57         mname += QString::number(idx);
58 
59     Qt::HANDLE mutex;
60     if (doCreate) {
61         QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); },
62                { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } );
63         if (!mutex) {
64             qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
65             return 0;
66         }
67     }
68     else {
69         QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); },
70                { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } );
71         if (!mutex) {
72             if (GetLastError() != ERROR_FILE_NOT_FOUND)
73                 qErrnoWarning("QtLockedFile::lock(): OpenMutex failed");
74             return 0;
75         }
76     }
77     return mutex;
78 }
79 
waitMutex(Qt::HANDLE mutex,bool doBlock)80 bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock)
81 {
82     Q_ASSERT(mutex);
83     DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0);
84     switch (res) {
85     case WAIT_OBJECT_0:
86     case WAIT_ABANDONED:
87         return true;
88         break;
89     case WAIT_TIMEOUT:
90         break;
91     default:
92         qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed");
93     }
94     return false;
95 }
96 
97 
98 
lock(LockMode mode,bool block)99 bool QtLockedFile::lock(LockMode mode, bool block)
100 {
101     if (!isOpen()) {
102         qWarning("QtLockedFile::lock(): file is not opened");
103         return false;
104     }
105 
106     if (mode == NoLock)
107         return unlock();
108 
109     if (mode == m_lock_mode)
110         return true;
111 
112     if (m_lock_mode != NoLock)
113         unlock();
114 
115     if (!wmutex && !(wmutex = getMutexHandle(-1, true)))
116         return false;
117 
118     if (!waitMutex(wmutex, block))
119         return false;
120 
121     if (mode == ReadLock) {
122         int idx = 0;
123         for (; idx < MAX_READERS; idx++) {
124             rmutex = getMutexHandle(idx, false);
125             if (!rmutex || waitMutex(rmutex, false))
126                 break;
127             CloseHandle(rmutex);
128         }
129         bool ok = true;
130         if (idx >= MAX_READERS) {
131             qWarning("QtLockedFile::lock(): too many readers");
132             rmutex = 0;
133             ok = false;
134         }
135         else if (!rmutex) {
136             rmutex = getMutexHandle(idx, true);
137             if (!rmutex || !waitMutex(rmutex, false))
138                 ok = false;
139         }
140         if (!ok && rmutex) {
141             CloseHandle(rmutex);
142             rmutex = 0;
143         }
144         ReleaseMutex(wmutex);
145         if (!ok)
146             return false;
147     }
148     else {
149         Q_ASSERT(rmutexes.isEmpty());
150         for (int i = 0; i < MAX_READERS; i++) {
151             Qt::HANDLE mutex = getMutexHandle(i, false);
152             if (mutex)
153                 rmutexes.append(mutex);
154         }
155         if (rmutexes.size()) {
156             DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(),
157                                                TRUE, block ? INFINITE : 0);
158             if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
159                 if (res != WAIT_TIMEOUT)
160                     qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed");
161                 m_lock_mode = WriteLock;  // trick unlock() to clean up - semiyucky
162                 unlock();
163                 return false;
164             }
165         }
166     }
167 
168     m_lock_mode = mode;
169     return true;
170 }
171 
unlock()172 bool QtLockedFile::unlock()
173 {
174     if (!isOpen()) {
175         qWarning("QtLockedFile::unlock(): file is not opened");
176         return false;
177     }
178 
179     if (!isLocked())
180         return true;
181 
182     if (m_lock_mode == ReadLock) {
183         ReleaseMutex(rmutex);
184         CloseHandle(rmutex);
185         rmutex = 0;
186     }
187     else {
188         foreach(Qt::HANDLE mutex, rmutexes) {
189             ReleaseMutex(mutex);
190             CloseHandle(mutex);
191         }
192         rmutexes.clear();
193         ReleaseMutex(wmutex);
194     }
195 
196     m_lock_mode = QtLockedFile::NoLock;
197     return true;
198 }
199 
~QtLockedFile()200 QtLockedFile::~QtLockedFile()
201 {
202     if (isOpen())
203         unlock();
204     if (wmutex)
205         CloseHandle(wmutex);
206 }
207