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