File indexing completed on 2024-05-19 04:38:46

0001 /****************************************************************************
0002 **
0003 ** Copyright (C) 2016 The Qt Company Ltd.
0004 ** Contact: https://www.qt.io/licensing/
0005 **
0006 ** This file is part of Qt Creator.
0007 **
0008 ** Commercial License Usage
0009 ** Licensees holding valid commercial Qt licenses may use this file in
0010 ** accordance with the commercial license agreement provided with the
0011 ** Software or, alternatively, in accordance with the terms contained in
0012 ** a written agreement between you and The Qt Company. For licensing terms
0013 ** and conditions see https://www.qt.io/terms-conditions. For further
0014 ** information use the contact form at https://www.qt.io/contact-us.
0015 **
0016 ** GNU General Public License Usage
0017 ** Alternatively, this file may be used under the terms of the GNU
0018 ** General Public License version 3 as published by the Free Software
0019 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
0020 ** included in the packaging of this file. Please review the following
0021 ** information to ensure the GNU General Public License requirements will
0022 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
0023 **
0024 ****************************************************************************/
0025 
0026 #include "qtlockedfile.h"
0027 
0028 #include <qt_windows.h>
0029 #include <QFileInfo>
0030 
0031 namespace SharedTools {
0032 
0033 #define SEMAPHORE_PREFIX "QtLockedFile semaphore "
0034 #define MUTEX_PREFIX "QtLockedFile mutex "
0035 #define SEMAPHORE_MAX 100
0036 
0037 static QString errorCodeToString(DWORD errorCode)
0038 {
0039     QString result;
0040     char *data = 0;
0041     FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
0042                     0, errorCode, 0,
0043                     (char*)&data, 0, 0);
0044     result = QString::fromLocal8Bit(data);
0045     if (data != 0)
0046         LocalFree(data);
0047 
0048     if (result.endsWith(QLatin1Char('\n')))
0049         result.truncate(result.length() - 1);
0050 
0051     return result;
0052 }
0053 
0054 bool QtLockedFile::lock(LockMode mode, bool block)
0055 {
0056     if (!isOpen()) {
0057         qWarning("QtLockedFile::lock(): file is not opened");
0058         return false;
0059     }
0060 
0061     if (mode == m_lock_mode)
0062         return true;
0063 
0064     if (m_lock_mode != 0)
0065         unlock();
0066 
0067     if (m_semaphore_hnd == 0) {
0068         QFileInfo fi(*this);
0069         QString sem_name = QString::fromLatin1(SEMAPHORE_PREFIX)
0070                            + fi.absoluteFilePath().toLower();
0071 
0072         m_semaphore_hnd = CreateSemaphoreW(0, SEMAPHORE_MAX, SEMAPHORE_MAX,
0073                                            (TCHAR*)sem_name.utf16());
0074 
0075         if (m_semaphore_hnd == 0) {
0076             qWarning("QtLockedFile::lock(): CreateSemaphore: %s",
0077                      errorCodeToString(GetLastError()).toLatin1().constData());
0078             return false;
0079         }
0080     }
0081 
0082     bool gotMutex = false;
0083     int decrement;
0084     if (mode == ReadLock) {
0085         decrement = 1;
0086     } else {
0087         decrement = SEMAPHORE_MAX;
0088         if (m_mutex_hnd == 0) {
0089             QFileInfo fi(*this);
0090             QString mut_name = QString::fromLatin1(MUTEX_PREFIX)
0091                                + fi.absoluteFilePath().toLower();
0092 
0093             m_mutex_hnd = CreateMutexW(NULL, FALSE, (TCHAR*)mut_name.utf16());
0094 
0095             if (m_mutex_hnd == 0) {
0096                 qWarning("QtLockedFile::lock(): CreateMutex: %s",
0097                          errorCodeToString(GetLastError()).toLatin1().constData());
0098                 return false;
0099             }
0100         }
0101         DWORD res = WaitForSingleObject(m_mutex_hnd, block ? INFINITE : 0);
0102         if (res == WAIT_TIMEOUT)
0103             return false;
0104         if (res == WAIT_FAILED) {
0105             qWarning("QtLockedFile::lock(): WaitForSingleObject (mutex): %s",
0106                      errorCodeToString(GetLastError()).toLatin1().constData());
0107             return false;
0108         }
0109         gotMutex = true;
0110     }
0111 
0112     for (int i = 0; i < decrement; ++i) {
0113         DWORD res = WaitForSingleObject(m_semaphore_hnd, block ? INFINITE : 0);
0114         if (res == WAIT_TIMEOUT) {
0115             if (i) {
0116                 // A failed nonblocking rw locking. Undo changes to semaphore.
0117                 if (ReleaseSemaphore(m_semaphore_hnd, i, NULL) == 0) {
0118                     qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
0119                              errorCodeToString(GetLastError()).toLatin1().constData());
0120                     // Fall through
0121                 }
0122             }
0123             if (gotMutex)
0124                 ReleaseMutex(m_mutex_hnd);
0125             return false;
0126     }
0127         if (res != WAIT_OBJECT_0) {
0128             if (gotMutex)
0129                 ReleaseMutex(m_mutex_hnd);
0130             qWarning("QtLockedFile::lock(): WaitForSingleObject (semaphore): %s",
0131                         errorCodeToString(GetLastError()).toLatin1().constData());
0132             return false;
0133         }
0134     }
0135 
0136     m_lock_mode = mode;
0137     if (gotMutex)
0138         ReleaseMutex(m_mutex_hnd);
0139     return true;
0140 }
0141 
0142 bool QtLockedFile::unlock()
0143 {
0144     if (!isOpen()) {
0145         qWarning("QtLockedFile::unlock(): file is not opened");
0146         return false;
0147     }
0148 
0149     if (!isLocked())
0150         return true;
0151 
0152     int increment;
0153     if (m_lock_mode == ReadLock)
0154         increment = 1;
0155     else
0156         increment = SEMAPHORE_MAX;
0157 
0158     DWORD ret = ReleaseSemaphore(m_semaphore_hnd, increment, 0);
0159     if (ret == 0) {
0160         qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
0161                     errorCodeToString(GetLastError()).toLatin1().constData());
0162         return false;
0163     }
0164 
0165     m_lock_mode = QtLockedFile::NoLock;
0166     remove();
0167     return true;
0168 }
0169 
0170 QtLockedFile::~QtLockedFile()
0171 {
0172     if (isOpen())
0173         unlock();
0174     if (m_mutex_hnd != 0) {
0175         DWORD ret = CloseHandle(m_mutex_hnd);
0176         if (ret == 0) {
0177             qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (mutex): %s",
0178                         errorCodeToString(GetLastError()).toLatin1().constData());
0179         }
0180         m_mutex_hnd = 0;
0181     }
0182     if (m_semaphore_hnd != 0) {
0183         DWORD ret = CloseHandle(m_semaphore_hnd);
0184         if (ret == 0) {
0185             qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (semaphore): %s",
0186                         errorCodeToString(GetLastError()).toLatin1().constData());
0187         }
0188         m_semaphore_hnd = 0;
0189     }
0190 }
0191 
0192 } // namespace SharedTools