text: /****************************************************************************
  ** 
  ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
  ** All rights reserved.
  ** Contact: Nokia Corporation (qt-info@nokia.com)
  ** 
  ** This file is part of a Qt Solutions component.
  **
  ** Commercial Usage  
  ** Licensees holding valid Qt Commercial licenses may use this file in
  ** accordance with the Qt Solutions Commercial License Agreement provided
  ** with the Software or, alternatively, in accordance with the terms
  ** contained in a written agreement between you and Nokia.
  ** 
  ** GNU Lesser General Public License Usage
  ** Alternatively, this file may be used under the terms of the GNU Lesser
  ** General Public License version 2.1 as published by the Free Software
  ** Foundation and appearing in the file LICENSE.LGPL included in the
  ** packaging of this file.  Please review the following information to
  ** ensure the GNU Lesser General Public License version 2.1 requirements
  ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  ** 
  ** In addition, as a special exception, Nokia gives you certain
  ** additional rights. These rights are described in the Nokia Qt LGPL
  ** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this
  ** package.
  ** 
  ** GNU General Public License Usage 
  ** Alternatively, this file may be used under the terms of the GNU
  ** General Public License version 3.0 as published by the Free Software
  ** Foundation and appearing in the file LICENSE.GPL included in the
  ** packaging of this file.  Please review the following information to
  ** ensure the GNU General Public License version 3.0 requirements will be
  ** met: http://www.gnu.org/copyleft/gpl.html.
  ** 
  ** Please note Third Party Software included with Qt Solutions may impose
  ** additional restrictions and it is the user's responsibility to ensure
  ** that they have met the licensing requirements of the GPL, LGPL, or Qt
  ** Solutions Commercial license and the relevant license of the Third
  ** Party Software they are using.
  ** 
  ** If you are unsure which license is appropriate for your use, please
  ** contact Nokia at qt-info@nokia.com.
  ** 
  ****************************************************************************/
  
  #include "qtlockedfile.h"
  #include <qt_windows.h>
  #include <QtCore/QFileInfo>
  
  #define MUTEX_PREFIX "QtLockedFile mutex "
  // Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
  #define MAX_READERS MAXIMUM_WAIT_OBJECTS
  
  Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
  {
      if (mutexname.isEmpty()) {
          QFileInfo fi(*this);
          mutexname = QString::fromLatin1(MUTEX_PREFIX)
                      + fi.absoluteFilePath().toLower();
      }
      QString mname(mutexname);
      if (idx >= 0)
          mname += QString::number(idx);
  
      Qt::HANDLE mutex;
      if (doCreate) {
          QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); },
                 { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } );
          if (!mutex) {
              qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
              return 0;
          }
      }
      else {
          QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); },
                 { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } );
          if (!mutex) {
              if (GetLastError() != ERROR_FILE_NOT_FOUND)
                  qErrnoWarning("QtLockedFile::lock(): OpenMutex failed");
              return 0;
          }
      }
      return mutex;
  }
  
  bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock)
  {
      Q_ASSERT(mutex);
      DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0);
      switch (res) {
      case WAIT_OBJECT_0:
      case WAIT_ABANDONED:
          return true;
          break;
      case WAIT_TIMEOUT:
          break;
      default:
          qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed");
      }
      return false;
  }
  
  
  
  bool QtLockedFile::lock(LockMode mode, bool block)
  {
      if (!isOpen()) {
          qWarning("QtLockedFile::lock(): file is not opened");
          return false;
      }
  
      if (mode == NoLock)
          return unlock();
  
      if (mode == m_lock_mode)
          return true;
  
      if (m_lock_mode != NoLock)
          unlock();
  
      if (!wmutex && !(wmutex = getMutexHandle(-1, true)))
          return false;
  
      if (!waitMutex(wmutex, block))
          return false;
  
      if (mode == ReadLock) {
          int idx = 0;
          for (; idx < MAX_READERS; idx++) {
              rmutex = getMutexHandle(idx, false);
              if (!rmutex || waitMutex(rmutex, false))
                  break;
              CloseHandle(rmutex);
          }
          bool ok = true;
          if (idx >= MAX_READERS) {
              qWarning("QtLockedFile::lock(): too many readers");
              rmutex = 0;
              ok = false;
          }
          else if (!rmutex) {
              rmutex = getMutexHandle(idx, true);
              if (!rmutex || !waitMutex(rmutex, false))
                  ok = false;
          }
          if (!ok && rmutex) {
              CloseHandle(rmutex);
              rmutex = 0;
          }
          ReleaseMutex(wmutex);
          if (!ok)
              return false;
      }
      else {
          Q_ASSERT(rmutexes.isEmpty());
          for (int i = 0; i < MAX_READERS; i++) {
              Qt::HANDLE mutex = getMutexHandle(i, false);
              if (mutex)
                  rmutexes.append(mutex);
          }
          if (rmutexes.size()) {
              DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(),
                                                 TRUE, block ? INFINITE : 0);
              if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
                  if (res != WAIT_TIMEOUT)
                      qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed");
                  m_lock_mode = WriteLock;  // trick unlock() to clean up - semiyucky
                  unlock();
                  return false;
              }
          }
      }
  
      m_lock_mode = mode;
      return true;
  }
  
  bool QtLockedFile::unlock()
  {
      if (!isOpen()) {
          qWarning("QtLockedFile::unlock(): file is not opened");
          return false;
      }
  
      if (!isLocked())
          return true;
  
      if (m_lock_mode == ReadLock) {
          ReleaseMutex(rmutex);
          CloseHandle(rmutex);
          rmutex = 0;
      }
      else {
          foreach(Qt::HANDLE mutex, rmutexes) {
              ReleaseMutex(mutex);
              CloseHandle(mutex);
          }
          rmutexes.clear();
          ReleaseMutex(wmutex);
      }
  
      m_lock_mode = QtLockedFile::NoLock;
      return true;
  }
  
  QtLockedFile::~QtLockedFile()
  {
      if (isOpen())
          unlock();
      if (wmutex)
          CloseHandle(wmutex);
  }
  