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