/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** 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.0, 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <windows.h>
#include "qwaitcondition.h"
#include "qmutex.h"
#include "qinternallist.h"
//***********************************************************************
// QWaitConditionPrivate
// **********************************************************************
class QWaitConditionEvent
{
public:
QWaitConditionEvent() : priority(0), wokenUp(false)
{
event = CreateEvent(NULL, TRUE, FALSE, NULL);
}
~QWaitConditionEvent() { CloseHandle(event); }
int priority;
bool wokenUp;
HANDLE event;
};
class EventQueue : public QInternalList<QWaitConditionEvent>
{
public:
EventQueue() { setAutoDelete(TRUE); }
~EventQueue() {}
};
class QWaitConditionPrivate
{
public:
QMutex mtx;
EventQueue queue;
EventQueue freeQueue;
QWaitConditionEvent *pre();
void wait(QWaitConditionEvent *wce);
void post(QWaitConditionEvent *wce);
};
QWaitConditionEvent *QWaitConditionPrivate::pre()
{
mtx.lock();
QWaitConditionEvent *wce =
freeQueue.isEmpty() ? new QWaitConditionEvent : freeQueue.take(0);
wce->priority = GetThreadPriority(GetCurrentThread());
wce->wokenUp = FALSE;
// insert 'wce' into the queue (sorted by priority)
uint index = 0;
for (; index < queue.count(); ++index)
{
QWaitConditionEvent *current = queue.at(index);
if (current->priority < wce->priority)
break;
}
queue.insert(index, wce);
mtx.unlock();
return wce;
}
void QWaitConditionPrivate::wait(QWaitConditionEvent *wce)
{
WaitForSingleObject(wce->event, INFINITE);
}
void QWaitConditionPrivate::post(QWaitConditionEvent *wce)
{
mtx.lock();
// remove 'wce' from the queue
int idx = queue.find(wce);
ASSERT(idx!=-1);
queue.take(idx);
ResetEvent(wce->event);
freeQueue.append(wce);
// wakeups delivered after the timeout should be forwarded to the next waiter
if (wce->wokenUp && !queue.isEmpty())
{
QWaitConditionEvent *other = queue.getFirst();
SetEvent(other->event);
other->wokenUp = TRUE;
}
mtx.unlock();
}
//***********************************************************************
// QWaitCondition implementation
//***********************************************************************
QWaitCondition::QWaitCondition()
{
d = new QWaitConditionPrivate;
}
QWaitCondition::~QWaitCondition()
{
if (!d->queue.isEmpty())
{
qWarning("QWaitCondition: Destroyed while threads are still waiting");
}
delete d;
}
void QWaitCondition::wait(QMutex *mutex)
{
if (!mutex) return;
QWaitConditionEvent *wce = d->pre();
mutex->unlock();
d->wait(wce);
mutex->lock();
d->post(wce);
}
void QWaitCondition::wakeOne()
{
// wake up the first waiting thread in the queue
QMutexLocker locker(&d->mtx);
for (uint i = 0; i < d->queue.count(); ++i)
{
QWaitConditionEvent *current = d->queue.at(i);
if (current->wokenUp) continue;
SetEvent(current->event);
current->wokenUp = TRUE;
break;
}
}
void QWaitCondition::wakeAll()
{
// wake up the all threads in the queue
QMutexLocker locker(&d->mtx);
for (uint i = 0; i < d->queue.count(); ++i)
{
QWaitConditionEvent *current = d->queue.at(i);
SetEvent(current->event);
current->wokenUp = TRUE;
}
}