Fix QMutex can deadlock when calling tryLock
[qt:qt.git] / src / corelib / thread / qmutex_unix.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qplatformdefs.h"
43 #include "qmutex.h"
44 #include "qstring.h"
45
46 #ifndef QT_NO_THREAD
47 #include "qatomic.h"
48 #include "qmutex_p.h"
49
50 #include <errno.h>
51
52 #if defined(Q_OS_VXWORKS) && defined(wakeup)
53 #undef wakeup
54 #endif
55
56 QT_BEGIN_NAMESPACE
57
58 static void report_error(int code, const char *where, const char *what)
59 {
60     if (code != 0)
61         qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code)));
62 }
63
64
65 QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
66     : recursive(mode == QMutex::Recursive), contenders(0), lastSpinCount(0), owner(0), count(0), wakeup(false)
67 {
68     report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init");
69     report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init");
70 }
71
72 QMutexPrivate::~QMutexPrivate()
73 {
74     report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy");
75     report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy");
76 }
77
78 bool QMutexPrivate::wait(int timeout)
79 {
80     report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock");
81     int errorCode = 0;
82     while (!wakeup) {
83         if (timeout < 0) {
84             errorCode = pthread_cond_wait(&cond, &mutex);
85         } else {
86             struct timeval tv;
87             gettimeofday(&tv, 0);
88
89             timespec ti;
90             ti.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000;
91             ti.tv_sec = tv.tv_sec + (timeout / 1000) + (ti.tv_nsec / 1000000000);
92             ti.tv_nsec %= 1000000000;
93
94             errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
95         }
96         if (errorCode) {
97             if (errorCode == ETIMEDOUT) {
98                 if (wakeup)
99                     errorCode = 0;
100                 break;
101             }
102             report_error(errorCode, "QMutex::lock()", "cv wait");
103         }
104     }
105     wakeup = false;
106     report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock");
107     return errorCode == 0;
108 }
109
110 void QMutexPrivate::wakeUp()
111 {
112     report_error(pthread_mutex_lock(&mutex), "QMutex::unlock", "mutex lock");
113     wakeup = true;
114     report_error(pthread_cond_signal(&cond), "QMutex::unlock", "cv signal");
115     report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
116 }
117
118 QT_END_NAMESPACE
119
120 #endif // QT_NO_THREAD