Absolute File Name: | /home/qt/qt5_coco/qt5/qtbase/src/corelib/thread/qthreadpool.cpp |
Switch to Source code | Preprocessed file |
Line | Source | Count | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | - | |||||||||||||
2 | - | |||||||||||||
3 | - | |||||||||||||
4 | - | |||||||||||||
5 | - | |||||||||||||
6 | - | |||||||||||||
7 | - | |||||||||||||
8 | namespace { namespace Q_QGS_theInstance { typedef QThreadPool Type; QBasicAtomicInt guard = { QtGlobalStatic::Uninitialized }; __attribute__((visibility("hidden"))) inline Type *innerFunction() { struct HolderBase { ~HolderBase() noexcept { if (guard.load() == QtGlobalStatic::Initialized) guard.store(QtGlobalStatic::Destroyed); } }; static struct Holder : public HolderBase { Type value; Holder() noexcept(noexcept(Type ())) : value () { guard.store(QtGlobalStatic::Initialized); } } holder; return &holder.value; } } } static QGlobalStatic<QThreadPool, Q_QGS_theInstance::innerFunction, Q_QGS_theInstance::guard> theInstance; | - | ||||||||||||
9 | - | |||||||||||||
10 | - | |||||||||||||
11 | - | |||||||||||||
12 | - | |||||||||||||
13 | class QThreadPoolThread : public QThread | - | ||||||||||||
14 | { | - | ||||||||||||
15 | public: | - | ||||||||||||
16 | QThreadPoolThread(QThreadPoolPrivate *manager); | - | ||||||||||||
17 | void run() override; | - | ||||||||||||
18 | void registerThreadInactive(); | - | ||||||||||||
19 | - | |||||||||||||
20 | QWaitCondition runnableReady; | - | ||||||||||||
21 | QThreadPoolPrivate *manager; | - | ||||||||||||
22 | QRunnable *runnable; | - | ||||||||||||
23 | }; | - | ||||||||||||
24 | QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager) | - | ||||||||||||
25 | :manager(manager), runnable(0) | - | ||||||||||||
26 | { } | - | ||||||||||||
27 | - | |||||||||||||
28 | - | |||||||||||||
29 | - | |||||||||||||
30 | - | |||||||||||||
31 | void QThreadPoolThread::run() | - | ||||||||||||
32 | { | - | ||||||||||||
33 | QMutexLocker locker(&manager->mutex); | - | ||||||||||||
34 | for(;;) { | - | ||||||||||||
35 | QRunnable *r = runnable; | - | ||||||||||||
36 | runnable = 0; | - | ||||||||||||
37 | - | |||||||||||||
38 | do { | - | ||||||||||||
39 | if (r) { | - | ||||||||||||
40 | const bool autoDelete = r->autoDelete(); | - | ||||||||||||
41 | - | |||||||||||||
42 | - | |||||||||||||
43 | - | |||||||||||||
44 | locker.unlock(); | - | ||||||||||||
45 | - | |||||||||||||
46 | try { | - | ||||||||||||
47 | - | |||||||||||||
48 | r->run(); | - | ||||||||||||
49 | - | |||||||||||||
50 | } catch (...) { | - | ||||||||||||
51 | QMessageLogger(__FILE__, 96102, __PRETTY_FUNCTION__).warning("Qt Concurrent has caught an exception thrown from a worker thread.\n" | - | ||||||||||||
52 | "This is not supported, exceptions thrown in worker threads must be\n" | - | ||||||||||||
53 | "caught before control returns to Qt Concurrent."); | - | ||||||||||||
54 | registerThreadInactive(); | - | ||||||||||||
55 | throw; | - | ||||||||||||
56 | } | - | ||||||||||||
57 | - | |||||||||||||
58 | locker.relock(); | - | ||||||||||||
59 | - | |||||||||||||
60 | if (autoDelete && !--r->ref) | - | ||||||||||||
61 | delete r; | - | ||||||||||||
62 | } | - | ||||||||||||
63 | - | |||||||||||||
64 | - | |||||||||||||
65 | if (manager->tooManyThreadsActive()) | - | ||||||||||||
66 | break; | - | ||||||||||||
67 | - | |||||||||||||
68 | r = !manager->queue.isEmpty() ? manager->queue.takeFirst().first : 0; | - | ||||||||||||
69 | } while (r != 0); | - | ||||||||||||
70 | - | |||||||||||||
71 | if (manager->isExiting) { | - | ||||||||||||
72 | registerThreadInactive(); | - | ||||||||||||
73 | break; | - | ||||||||||||
74 | } | - | ||||||||||||
75 | - | |||||||||||||
76 | - | |||||||||||||
77 | bool expired = manager->tooManyThreadsActive(); | - | ||||||||||||
78 | if (!expired) { | - | ||||||||||||
79 | manager->waitingThreads.enqueue(this); | - | ||||||||||||
80 | registerThreadInactive(); | - | ||||||||||||
81 | - | |||||||||||||
82 | runnableReady.wait(locker.mutex(), manager->expiryTimeout); | - | ||||||||||||
83 | ++manager->activeThreads; | - | ||||||||||||
84 | if (manager->waitingThreads.removeOne(this)) | - | ||||||||||||
85 | expired = true; | - | ||||||||||||
86 | } | - | ||||||||||||
87 | if (expired) { | - | ||||||||||||
88 | manager->expiredThreads.enqueue(this); | - | ||||||||||||
89 | registerThreadInactive(); | - | ||||||||||||
90 | break; | - | ||||||||||||
91 | } | - | ||||||||||||
92 | } | - | ||||||||||||
93 | } | - | ||||||||||||
94 | - | |||||||||||||
95 | void QThreadPoolThread::registerThreadInactive() | - | ||||||||||||
96 | { | - | ||||||||||||
97 | if (--manager->activeThreads == 0) | - | ||||||||||||
98 | manager->noActiveThreads.wakeAll(); | - | ||||||||||||
99 | } | - | ||||||||||||
100 | - | |||||||||||||
101 | - | |||||||||||||
102 | - | |||||||||||||
103 | - | |||||||||||||
104 | - | |||||||||||||
105 | QThreadPoolPrivate:: QThreadPoolPrivate() | - | ||||||||||||
106 | : isExiting(false), | - | ||||||||||||
107 | expiryTimeout(30000), | - | ||||||||||||
108 | maxThreadCount(qAbs(QThread::idealThreadCount())), | - | ||||||||||||
109 | reservedThreads(0), | - | ||||||||||||
110 | activeThreads(0) | - | ||||||||||||
111 | { } | - | ||||||||||||
112 | - | |||||||||||||
113 | bool QThreadPoolPrivate::tryStart(QRunnable *task) | - | ||||||||||||
114 | { | - | ||||||||||||
115 | if (allThreads.isEmpty()) { | - | ||||||||||||
116 | - | |||||||||||||
117 | startThread(task); | - | ||||||||||||
118 | return true; | - | ||||||||||||
119 | } | - | ||||||||||||
120 | - | |||||||||||||
121 | - | |||||||||||||
122 | if (activeThreadCount() >= maxThreadCount) | - | ||||||||||||
123 | return false; | - | ||||||||||||
124 | - | |||||||||||||
125 | if (waitingThreads.count() > 0) { | - | ||||||||||||
126 | - | |||||||||||||
127 | enqueueTask(task); | - | ||||||||||||
128 | waitingThreads.takeFirst()->runnableReady.wakeOne(); | - | ||||||||||||
129 | return true; | - | ||||||||||||
130 | } | - | ||||||||||||
131 | - | |||||||||||||
132 | if (!expiredThreads.isEmpty()) { | - | ||||||||||||
133 | - | |||||||||||||
134 | QThreadPoolThread *thread = expiredThreads.dequeue(); | - | ||||||||||||
135 | ((!(thread->runnable == 0)) ? qt_assert("thread->runnable == 0",__FILE__,180186) : qt_noop()); | - | ||||||||||||
136 | - | |||||||||||||
137 | ++activeThreads; | - | ||||||||||||
138 | - | |||||||||||||
139 | if (task->autoDelete()) | - | ||||||||||||
140 | ++task->ref; | - | ||||||||||||
141 | thread->runnable = task; | - | ||||||||||||
142 | thread->start(); | - | ||||||||||||
143 | return true; | - | ||||||||||||
144 | } | - | ||||||||||||
145 | - | |||||||||||||
146 | - | |||||||||||||
147 | startThread(task); | - | ||||||||||||
148 | return true; | - | ||||||||||||
149 | } | - | ||||||||||||
150 | - | |||||||||||||
151 | inline bool operator<(int priority, const QPair<QRunnable *, int> &p) | - | ||||||||||||
152 | { return p.second < priority; } | - | ||||||||||||
153 | inline bool operator<(const QPair<QRunnable *, int> &p, int priority) | - | ||||||||||||
154 | { return priority < p.second; } | - | ||||||||||||
155 | - | |||||||||||||
156 | void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority) | - | ||||||||||||
157 | { | - | ||||||||||||
158 | if (runnable->autoDelete()) | - | ||||||||||||
159 | ++runnable->ref; | - | ||||||||||||
160 | - | |||||||||||||
161 | - | |||||||||||||
162 | QVector<QPair<QRunnable *, int> >::const_iterator begin = queue.constBegin(); | - | ||||||||||||
163 | QVector<QPair<QRunnable *, int> >::const_iterator it = queue.constEnd(); | - | ||||||||||||
164 | if (it != begin && priority > (*(it - 1)).second) | - | ||||||||||||
165 | it = std::upper_bound(begin, --it, priority); | - | ||||||||||||
166 | queue.insert(it - begin, qMakePair(runnable, priority)); | - | ||||||||||||
167 | } | - | ||||||||||||
168 | - | |||||||||||||
169 | int QThreadPoolPrivate::activeThreadCount() const | - | ||||||||||||
170 | { | - | ||||||||||||
171 | return (allThreads.count() | - | ||||||||||||
172 | - expiredThreads.count() | - | ||||||||||||
173 | - waitingThreads.count() | - | ||||||||||||
174 | + reservedThreads); | - | ||||||||||||
175 | } | - | ||||||||||||
176 | - | |||||||||||||
177 | void QThreadPoolPrivate::tryToStartMoreThreads() | - | ||||||||||||
178 | { | - | ||||||||||||
179 | - | |||||||||||||
180 | while (!queue.isEmpty()
| 2-49801 | ||||||||||||
181 | queue.removeFirst(); executed 9 times by 1 test: queue.removeFirst(); Executed by:
| 9 | ||||||||||||
182 | } executed 49803 times by 31 tests: end of block Executed by:
| 49803 | ||||||||||||
183 | - | |||||||||||||
184 | bool QThreadPoolPrivate::tooManyThreadsActive() const | - | ||||||||||||
185 | { | - | ||||||||||||
186 | const int activeThreadCount = this->activeThreadCount(); | - | ||||||||||||
187 | return activeThreadCount > maxThreadCount && (activeThreadCount - reservedThreads) > 1; | - | ||||||||||||
188 | } | - | ||||||||||||
189 | - | |||||||||||||
190 | - | |||||||||||||
191 | - | |||||||||||||
192 | - | |||||||||||||
193 | void QThreadPoolPrivate::startThread(QRunnable *runnable) | - | ||||||||||||
194 | { | - | ||||||||||||
195 | QScopedPointer <QThreadPoolThread> thread(new QThreadPoolThread(this)); | - | ||||||||||||
196 | thread->setObjectName(QLatin1String("Thread (pooled)")); | - | ||||||||||||
197 | allThreads.insert(thread.data()); | - | ||||||||||||
198 | ++activeThreads; | - | ||||||||||||
199 | - | |||||||||||||
200 | if (runnable->autoDelete()) | - | ||||||||||||
201 | ++runnable->ref; | - | ||||||||||||
202 | thread->runnable = runnable; | - | ||||||||||||
203 | thread.take()->start(); | - | ||||||||||||
204 | } | - | ||||||||||||
205 | - | |||||||||||||
206 | - | |||||||||||||
207 | - | |||||||||||||
208 | - | |||||||||||||
209 | - | |||||||||||||
210 | void QThreadPoolPrivate::reset() | - | ||||||||||||
211 | { | - | ||||||||||||
212 | QMutexLocker locker(&mutex); | - | ||||||||||||
213 | isExiting = true; | - | ||||||||||||
214 | - | |||||||||||||
215 | while (!allThreads.empty()
| 957-4780 | ||||||||||||
216 | - | |||||||||||||
217 | QSet<QThreadPoolThread *> allThreadsCopy; | - | ||||||||||||
218 | allThreadsCopy.swap(allThreads); | - | ||||||||||||
219 | locker.unlock(); | - | ||||||||||||
220 | - | |||||||||||||
221 | for (QForeachContainer<typename QtPrivate::remove_reference<decltype(allThreadsCopy)>::type> _container_((allThreadsCopy)); _container_.control && _container_.i != _container_.e; ++_container_.i, _container_.control ^= 1)for (QThreadPoolThread *thread = *_container_.i; _container_.control; _container_.control = 0): qAsConst(allThreadsCopy)) { | - | ||||||||||||
222 | thread->runnableReady.wakeAll(); | - | ||||||||||||
223 | thread->wait(); | - | ||||||||||||
224 | delete thread; | - | ||||||||||||
225 | } executed 1182 times by 37 tests: end of block Executed by:
| 1182 | ||||||||||||
226 | - | |||||||||||||
227 | locker.relock(); | - | ||||||||||||
228 | - | |||||||||||||
229 | } executed 957 times by 37 tests: end of block Executed by:
| 957 | ||||||||||||
230 | - | |||||||||||||
231 | waitingThreads.clear(); | - | ||||||||||||
232 | expiredThreads.clear(); | - | ||||||||||||
233 | - | |||||||||||||
234 | isExiting = false; | - | ||||||||||||
235 | } executed 4780 times by 440 tests: end of block Executed by:
| 4780 | ||||||||||||
236 | - | |||||||||||||
237 | bool QThreadPoolPrivate::waitForDone(int msecs) | - | ||||||||||||
238 | { | - | ||||||||||||
239 | QMutexLocker locker(&mutex); | - | ||||||||||||
240 | if (msecs < 0) { | - | ||||||||||||
241 | while (!(queue.isEmpty() && activeThreads == 0)) | - | ||||||||||||
242 | noActiveThreads.wait(locker.mutex()); | - | ||||||||||||
243 | } else { | - | ||||||||||||
244 | QElapsedTimer timer; | - | ||||||||||||
245 | timer.start(); | - | ||||||||||||
246 | int t; | - | ||||||||||||
247 | while (!(queue.isEmpty() && activeThreads == 0) && | - | ||||||||||||
248 | ((t = msecs - timer.elapsed()) > 0)) | - | ||||||||||||
249 | noActiveThreads.wait(locker.mutex(), t); | - | ||||||||||||
250 | } | - | ||||||||||||
251 | return queue.isEmpty() && activeThreads == 0; | - | ||||||||||||
252 | } | - | ||||||||||||
253 | - | |||||||||||||
254 | void QThreadPoolPrivate::clear() | - | ||||||||||||
255 | { | - | ||||||||||||
256 | QMutexLocker locker(&mutex); | - | ||||||||||||
257 | for (QVector<QPair<QRunnable *, int> >::const_iterator it = queue.constBegin(); | - | ||||||||||||
258 | it != queue.constEnd(); ++it) { | - | ||||||||||||
259 | QRunnable* r = it->first; | - | ||||||||||||
260 | if (r->autoDelete() && !--r->ref) | - | ||||||||||||
261 | delete r; | - | ||||||||||||
262 | } | - | ||||||||||||
263 | queue.clear(); | - | ||||||||||||
264 | } | - | ||||||||||||
265 | - | |||||||||||||
266 | - | |||||||||||||
267 | - | |||||||||||||
268 | - | |||||||||||||
269 | - | |||||||||||||
270 | - | |||||||||||||
271 | bool QThreadPoolPrivate::stealRunnable(QRunnable *runnable) | - | ||||||||||||
272 | { | - | ||||||||||||
273 | if (runnable == 0) | - | ||||||||||||
274 | return false; | - | ||||||||||||
275 | { | - | ||||||||||||
276 | QMutexLocker locker(&mutex); | - | ||||||||||||
277 | QVector<QPair<QRunnable *, int> >::iterator it = queue.begin(); | - | ||||||||||||
278 | QVector<QPair<QRunnable *, int> >::iterator end = queue.end(); | - | ||||||||||||
279 | - | |||||||||||||
280 | while (it != end) { | - | ||||||||||||
281 | if (it->first == runnable) { | - | ||||||||||||
282 | queue.erase(it); | - | ||||||||||||
283 | return true; | - | ||||||||||||
284 | } | - | ||||||||||||
285 | ++it; | - | ||||||||||||
286 | } | - | ||||||||||||
287 | } | - | ||||||||||||
288 | - | |||||||||||||
289 | return false; | - | ||||||||||||
290 | } | - | ||||||||||||
291 | - | |||||||||||||
292 | - | |||||||||||||
293 | - | |||||||||||||
294 | - | |||||||||||||
295 | - | |||||||||||||
296 | - | |||||||||||||
297 | - | |||||||||||||
298 | void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable) | - | ||||||||||||
299 | { | - | ||||||||||||
300 | if (!stealRunnable(runnable)) | - | ||||||||||||
301 | return; | - | ||||||||||||
302 | const bool autoDelete = runnable->autoDelete(); | - | ||||||||||||
303 | bool del = autoDelete && !--runnable->ref; | - | ||||||||||||
304 | - | |||||||||||||
305 | runnable->run(); | - | ||||||||||||
306 | - | |||||||||||||
307 | if (del) { | - | ||||||||||||
308 | delete runnable; | - | ||||||||||||
309 | } | - | ||||||||||||
310 | } | - | ||||||||||||
311 | QThreadPool::QThreadPool(QObject *parent) | - | ||||||||||||
312 | : QObject(*new QThreadPoolPrivate, parent) | - | ||||||||||||
313 | { } | - | ||||||||||||
314 | - | |||||||||||||
315 | - | |||||||||||||
316 | - | |||||||||||||
317 | - | |||||||||||||
318 | - | |||||||||||||
319 | QThreadPool::~QThreadPool() | - | ||||||||||||
320 | { | - | ||||||||||||
321 | waitForDone(); | - | ||||||||||||
322 | } | - | ||||||||||||
323 | - | |||||||||||||
324 | - | |||||||||||||
325 | - | |||||||||||||
326 | - | |||||||||||||
327 | QThreadPool *QThreadPool::globalInstance() | - | ||||||||||||
328 | { | - | ||||||||||||
329 | return theInstance(); | - | ||||||||||||
330 | } | - | ||||||||||||
331 | void QThreadPool::start(QRunnable *runnable, int priority) | - | ||||||||||||
332 | { | - | ||||||||||||
333 | if (!runnable) | - | ||||||||||||
334 | return; | - | ||||||||||||
335 | - | |||||||||||||
336 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
337 | QMutexLocker locker(&d->mutex); | - | ||||||||||||
338 | if (!d->tryStart(runnable)) { | - | ||||||||||||
339 | d->enqueueTask(runnable, priority); | - | ||||||||||||
340 | - | |||||||||||||
341 | if (!d->waitingThreads.isEmpty()) | - | ||||||||||||
342 | d->waitingThreads.takeFirst()->runnableReady.wakeOne(); | - | ||||||||||||
343 | } | - | ||||||||||||
344 | } | - | ||||||||||||
345 | bool QThreadPool::tryStart(QRunnable *runnable) | - | ||||||||||||
346 | { | - | ||||||||||||
347 | if (!runnable) | - | ||||||||||||
348 | return false; | - | ||||||||||||
349 | - | |||||||||||||
350 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
351 | - | |||||||||||||
352 | QMutexLocker locker(&d->mutex); | - | ||||||||||||
353 | - | |||||||||||||
354 | if (d->allThreads.isEmpty() == false && d->activeThreadCount() >= d->maxThreadCount) | - | ||||||||||||
355 | return false; | - | ||||||||||||
356 | - | |||||||||||||
357 | return d->tryStart(runnable); | - | ||||||||||||
358 | } | - | ||||||||||||
359 | int QThreadPool::expiryTimeout() const | - | ||||||||||||
360 | { | - | ||||||||||||
361 | const QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
362 | return d->expiryTimeout; | - | ||||||||||||
363 | } | - | ||||||||||||
364 | - | |||||||||||||
365 | void QThreadPool::setExpiryTimeout(int expiryTimeout) | - | ||||||||||||
366 | { | - | ||||||||||||
367 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
368 | if (d->expiryTimeout == expiryTimeout) | - | ||||||||||||
369 | return; | - | ||||||||||||
370 | d->expiryTimeout = expiryTimeout; | - | ||||||||||||
371 | } | - | ||||||||||||
372 | int QThreadPool::maxThreadCount() const | - | ||||||||||||
373 | { | - | ||||||||||||
374 | const QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
375 | return d->maxThreadCount; | - | ||||||||||||
376 | } | - | ||||||||||||
377 | - | |||||||||||||
378 | void QThreadPool::setMaxThreadCount(int maxThreadCount) | - | ||||||||||||
379 | { | - | ||||||||||||
380 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
381 | QMutexLocker locker(&d->mutex); | - | ||||||||||||
382 | - | |||||||||||||
383 | if (maxThreadCount == d->maxThreadCount) | - | ||||||||||||
384 | return; | - | ||||||||||||
385 | - | |||||||||||||
386 | d->maxThreadCount = maxThreadCount; | - | ||||||||||||
387 | d->tryToStartMoreThreads(); | - | ||||||||||||
388 | } | - | ||||||||||||
389 | int QThreadPool::activeThreadCount() const | - | ||||||||||||
390 | { | - | ||||||||||||
391 | const QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
392 | QMutexLocker locker(&d->mutex); | - | ||||||||||||
393 | return d->activeThreadCount(); | - | ||||||||||||
394 | } | - | ||||||||||||
395 | void QThreadPool::reserveThread() | - | ||||||||||||
396 | { | - | ||||||||||||
397 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
398 | QMutexLocker locker(&d->mutex); | - | ||||||||||||
399 | ++d->reservedThreads; | - | ||||||||||||
400 | } | - | ||||||||||||
401 | void QThreadPool::releaseThread() | - | ||||||||||||
402 | { | - | ||||||||||||
403 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
404 | QMutexLocker locker(&d->mutex); | - | ||||||||||||
405 | --d->reservedThreads; | - | ||||||||||||
406 | d->tryToStartMoreThreads(); | - | ||||||||||||
407 | } | - | ||||||||||||
408 | - | |||||||||||||
409 | - | |||||||||||||
410 | - | |||||||||||||
411 | - | |||||||||||||
412 | - | |||||||||||||
413 | - | |||||||||||||
414 | - | |||||||||||||
415 | bool QThreadPool::waitForDone(int msecs) | - | ||||||||||||
416 | { | - | ||||||||||||
417 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
418 | bool rc = d->waitForDone(msecs); | - | ||||||||||||
419 | if (rc) | - | ||||||||||||
420 | d->reset(); | - | ||||||||||||
421 | return rc; | - | ||||||||||||
422 | } | - | ||||||||||||
423 | void QThreadPool::clear() | - | ||||||||||||
424 | { | - | ||||||||||||
425 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
426 | d->clear(); | - | ||||||||||||
427 | } | - | ||||||||||||
428 | void QThreadPool::cancel(QRunnable *runnable) | - | ||||||||||||
429 | { | - | ||||||||||||
430 | QThreadPoolPrivate * const d = d_func(); | - | ||||||||||||
431 | if (!d->stealRunnable(runnable)) | - | ||||||||||||
432 | return; | - | ||||||||||||
433 | if (runnable->autoDelete() && !--runnable->ref) { | - | ||||||||||||
434 | delete runnable; | - | ||||||||||||
435 | } | - | ||||||||||||
436 | } | - | ||||||||||||
437 | - | |||||||||||||
438 | - | |||||||||||||
Switch to Source code | Preprocessed file |