source: trunk/src/corelib/kernel/qabstracteventdispatcher.cpp@ 967

Last change on this file since 967 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qabstracteventdispatcher.h"
43#include "qabstracteventdispatcher_p.h"
44
45#include "qthread.h"
46#include <private/qthread_p.h>
47#include <private/qcoreapplication_p.h>
48
49QT_BEGIN_NAMESPACE
50
51// we allow for 2^24 = 8^8 = 16777216 simultaneously running timers
52enum { NumberOfBuckets = 8, FirstBucketSize = 8 };
53
54static const int BucketSize[NumberOfBuckets] =
55 { 8, 64, 512, 4096, 32768, 262144, 2097152, 16777216 - 2396744 };
56static const int BucketOffset[NumberOfBuckets] =
57 { 0, 8, 72, 584, 4680, 37448, 299592, 2396744 };
58
59static int FirstBucket[FirstBucketSize] = { 1, 2, 3, 4, 5, 6, 7, 8 };
60static QBasicAtomicPointer<int> timerIds[NumberOfBuckets] =
61 { Q_BASIC_ATOMIC_INITIALIZER(FirstBucket),
62 Q_BASIC_ATOMIC_INITIALIZER(0),
63 Q_BASIC_ATOMIC_INITIALIZER(0),
64 Q_BASIC_ATOMIC_INITIALIZER(0),
65 Q_BASIC_ATOMIC_INITIALIZER(0),
66 Q_BASIC_ATOMIC_INITIALIZER(0),
67 Q_BASIC_ATOMIC_INITIALIZER(0),
68 Q_BASIC_ATOMIC_INITIALIZER(0) };
69
70static void timerIdsDestructorFunction()
71{
72 // start at one, the first bucket is pre-allocated
73 for (int i = 1; i < NumberOfBuckets; ++i)
74 delete [] static_cast<int *>(timerIds[i]);
75}
76Q_DESTRUCTOR_FUNCTION(timerIdsDestructorFunction)
77
78static QBasicAtomicInt nextFreeTimerId = Q_BASIC_ATOMIC_INITIALIZER(1);
79
80static const int TimerIdMask = 0x00ffffff;
81static const int TimerSerialMask = ~TimerIdMask & ~0x80000000;
82static const int TimerSerialCounter = TimerIdMask + 1;
83
84// avoid the ABA-problem by using 7 of the top 8 bits of the timerId as a serial number
85static inline int prepareNewValueWithSerialNumber(int oldId, int newId)
86{
87 return (newId & TimerIdMask) | ((oldId + TimerSerialCounter) & TimerSerialMask);
88}
89
90static inline int bucketOffset(int timerId)
91{
92 for (int i = 0; i < NumberOfBuckets; ++i) {
93 if (timerId < BucketSize[i])
94 return i;
95 timerId -= BucketSize[i];
96 }
97 qFatal("QAbstractEventDispatcher: INTERNAL ERROR, timer ID %d is too large", timerId);
98 return -1;
99}
100
101static inline int bucketIndex(int bucket, int timerId)
102{
103 return timerId - BucketOffset[bucket];
104}
105
106static inline int *allocateBucket(int bucket)
107{
108 // allocate a new bucket
109 const int size = BucketSize[bucket];
110 const int offset = BucketOffset[bucket];
111 int *b = new int[size];
112 for (int i = 0; i != size; ++i)
113 b[i] = offset + i + 1;
114 return b;
115}
116
117void QAbstractEventDispatcherPrivate::init()
118{
119 Q_Q(QAbstractEventDispatcher);
120 if (threadData->eventDispatcher != 0) {
121 qWarning("QAbstractEventDispatcher: An event dispatcher has already been created for this thread");
122 } else {
123 threadData->eventDispatcher = q;
124 }
125}
126
127// Timer IDs are implemented using a free-list;
128// there's a vector initialized with:
129// X[i] = i + 1
130// and nextFreeTimerId starts with 1.
131//
132// Allocating a timer ID involves taking the ID from
133// X[nextFreeTimerId]
134// updating nextFreeTimerId to this value and returning the old value
135//
136// When the timer ID is allocated, its cell in the vector is unused (it's a
137// free list). As an added protection, we use the cell to store an invalid
138// (negative) value that we can later check for integrity.
139//
140// (continues below).
141int QAbstractEventDispatcherPrivate::allocateTimerId()
142{
143 int timerId, newTimerId;
144 int at, *b;
145 do {
146 timerId = nextFreeTimerId; //.loadAcquire(); // ### FIXME Proper memory ordering semantics
147
148 // which bucket are we looking in?
149 int which = timerId & TimerIdMask;
150 int bucket = bucketOffset(which);
151 at = bucketIndex(bucket, which);
152 b = timerIds[bucket];
153
154 if (!b) {
155 // allocate a new bucket
156 b = allocateBucket(bucket);
157 if (!timerIds[bucket].testAndSetRelease(0, b)) {
158 // another thread won the race to allocate the bucket
159 delete [] b;
160 b = timerIds[bucket];
161 }
162 }
163
164 newTimerId = prepareNewValueWithSerialNumber(timerId, b[at]);
165 } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId));
166
167 b[at] = -timerId;
168
169 return timerId;
170}
171
172// Releasing a timer ID requires putting the current ID back in the vector;
173// we do it by setting:
174// X[timerId] = nextFreeTimerId;
175// then we update nextFreeTimerId to the timer we've just released
176//
177// The extra code in allocateTimerId and releaseTimerId are ABA prevention
178// and bucket memory. The buckets are simply to make sure we allocate only
179// the necessary number of timers. See above.
180//
181// ABA prevention simply adds a value to 7 of the top 8 bits when resetting
182// nextFreeTimerId.
183void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId)
184{
185 int which = timerId & TimerIdMask;
186 int bucket = bucketOffset(which);
187 int at = bucketIndex(bucket, which);
188 int *b = timerIds[bucket];
189
190 Q_ASSERT(b[at] == -timerId);
191
192 int freeId, newTimerId;
193 do {
194 freeId = nextFreeTimerId;//.loadAcquire(); // ### FIXME Proper memory ordering semantics
195 b[at] = freeId & TimerIdMask;
196
197 newTimerId = prepareNewValueWithSerialNumber(freeId, timerId);
198 } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId));
199}
200
201/*!
202 \class QAbstractEventDispatcher
203 \brief The QAbstractEventDispatcher class provides an interface to manage Qt's event queue.
204
205 \ingroup events
206
207 An event dispatcher receives events from the window system and other
208 sources. It then sends them to the QCoreApplication or QApplication
209 instance for processing and delivery. QAbstractEventDispatcher provides
210 fine-grained control over event delivery.
211
212 For simple control of event processing use
213 QCoreApplication::processEvents().
214
215 For finer control of the application's event loop, call
216 instance() and call functions on the QAbstractEventDispatcher
217 object that is returned. If you want to use your own instance of
218 QAbstractEventDispatcher or of a QAbstractEventDispatcher
219 subclass, you must create your instance \e before you create the
220 QApplication object.
221
222 The main event loop is started by calling
223 QCoreApplication::exec(), and stopped by calling
224 QCoreApplication::exit(). Local event loops can be created using
225 QEventLoop.
226
227 Programs that perform long operations can call processEvents()
228 with a bitwise OR combination of various QEventLoop::ProcessEventsFlag
229 values to control which events should be delivered.
230
231 QAbstractEventDispatcher also allows the integration of an
232 external event loop with the Qt event loop. For example, the
233 \l{Qt Solutions}{Motif Extension Qt Solution} includes a
234 reimplementation of QAbstractEventDispatcher that merges Qt and
235 Motif events together.
236
237 \sa QEventLoop, QCoreApplication
238*/
239
240/*!
241 Constructs a new event dispatcher with the given \a parent.
242*/
243QAbstractEventDispatcher::QAbstractEventDispatcher(QObject *parent)
244 : QObject(*new QAbstractEventDispatcherPrivate, parent)
245{
246 Q_D(QAbstractEventDispatcher);
247 d->init();
248}
249
250/*!
251 \internal
252*/
253QAbstractEventDispatcher::QAbstractEventDispatcher(QAbstractEventDispatcherPrivate &dd,
254 QObject *parent)
255 : QObject(dd, parent)
256{
257 Q_D(QAbstractEventDispatcher);
258 d->init();
259}
260
261/*!
262 Destroys the event dispatcher.
263*/
264QAbstractEventDispatcher::~QAbstractEventDispatcher()
265{ }
266
267/*!
268 Returns a pointer to the event dispatcher object for the specified
269 \a thread. If \a thread is zero, the current thread is used. If no
270 event dispatcher exists for the specified thread, this function
271 returns 0.
272
273 \bold{Note:} If Qt is built without thread support, the \a thread
274 argument is ignored.
275 */
276QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread)
277{
278 QThreadData *data = thread ? QThreadData::get2(thread) : QThreadData::current();