QtBase  v6.3.1
qthread_win.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qthread.h"
41 #include "qthread_p.h"
42 #include "qthreadstorage.h"
43 #include "qmutex.h"
44 
45 #include <qcoreapplication.h>
46 #include <qpointer.h>
47 
48 #include <private/qcoreapplication_p.h>
49 #include <private/qeventdispatcher_win_p.h>
50 
51 #include <qt_windows.h>
52 
53 #ifndef _MT
54 # define _MT
55 #endif // _MT
56 #include <process.h>
57 
59 
60 #if QT_CONFIG(thread)
61 
62 void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread);
63 DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID);
64 
65 static DWORD qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
66 void qt_create_tls()
67 {
68  if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
69  return;
70  static QBasicMutex mutex;
71  QMutexLocker locker(&mutex);
72  if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
73  return;
74  qt_current_thread_data_tls_index = TlsAlloc();
75 }
76 
77 static void qt_free_tls()
78 {
79  if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) {
80  TlsFree(qt_current_thread_data_tls_index);
81  qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
82  }
83 }
84 Q_DESTRUCTOR_FUNCTION(qt_free_tls)
85 
86 /*
87  QThreadData
88 */
90 {
91  TlsSetValue(qt_current_thread_data_tls_index, 0);
92 }
93 
94 QThreadData *QThreadData::current(bool createIfNecessary)
95 {
96  qt_create_tls();
97  QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
98  if (!threadData && createIfNecessary) {
99  threadData = new QThreadData;
100  // This needs to be called prior to new AdoptedThread() to
101  // avoid recursion.
102  TlsSetValue(qt_current_thread_data_tls_index, threadData);
103  QT_TRY {
104  threadData->thread = new QAdoptedThread(threadData);
105  } QT_CATCH(...) {
106  TlsSetValue(qt_current_thread_data_tls_index, 0);
107  threadData->deref();
108  threadData = 0;
109  QT_RETHROW;
110  }
111  threadData->deref();
112  threadData->isAdopted = true;
113  threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
114 
117  } else {
118  HANDLE realHandle = INVALID_HANDLE_VALUE;
119  DuplicateHandle(GetCurrentProcess(),
120  GetCurrentThread(),
121  GetCurrentProcess(),
122  &realHandle,
123  0,
124  FALSE,
125  DUPLICATE_SAME_ACCESS);
126  qt_watch_adopted_thread(realHandle, threadData->thread);
127  }
128  }
129  return threadData;
130 }
131 
133 {
134  d_func()->handle = GetCurrentThread();
135  d_func()->id = GetCurrentThreadId();
136 }
137 
138 static QList<HANDLE> qt_adopted_thread_handles;
139 static QList<QThread *> qt_adopted_qthreads;
140 static QBasicMutex qt_adopted_thread_watcher_mutex;
141 static DWORD qt_adopted_thread_watcher_id = 0;
142 static HANDLE qt_adopted_thread_wakeup = 0;
143 
150 void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread)
151 {
152  QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
153 
154  if (GetCurrentThreadId() == qt_adopted_thread_watcher_id) {
155  CloseHandle(adoptedThreadHandle);
156  return;
157  }
158 
159  qt_adopted_thread_handles.append(adoptedThreadHandle);
160  qt_adopted_qthreads.append(qthread);
161 
162  // Start watcher thread if it is not already running.
163  if (qt_adopted_thread_watcher_id == 0) {
164  if (qt_adopted_thread_wakeup == 0) {
165  qt_adopted_thread_wakeup = CreateEvent(0, false, false, 0);
166  qt_adopted_thread_handles.prepend(qt_adopted_thread_wakeup);
167  }
168 
169  CloseHandle(CreateThread(0, 0, qt_adopted_thread_watcher_function, 0, 0, &qt_adopted_thread_watcher_id));
170  } else {
171  SetEvent(qt_adopted_thread_wakeup);
172  }
173 }
174 
175 /*
176  This function loops and waits for native adopted threads to finish.
177  When this happens it derefs the QThreadData for the adopted thread
178  to make sure it gets cleaned up properly.
179 */
180 DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID)
181 {
182  forever {
183  qt_adopted_thread_watcher_mutex.lock();
184 
185  if (qt_adopted_thread_handles.count() == 1) {
186  qt_adopted_thread_watcher_id = 0;
187  qt_adopted_thread_watcher_mutex.unlock();
188  break;
189  }
190 
191  QList<HANDLE> handlesCopy = qt_adopted_thread_handles;
192  qt_adopted_thread_watcher_mutex.unlock();
193 
194  DWORD ret = WAIT_TIMEOUT;
195  int count;
196  int offset;
197  int loops = handlesCopy.size() / MAXIMUM_WAIT_OBJECTS;
198  if (handlesCopy.size() % MAXIMUM_WAIT_OBJECTS)
199  ++loops;
200  if (loops == 1) {
201  // no need to loop, no timeout
202  offset = 0;
203  count = handlesCopy.count();
204  ret = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
205  } else {
206  int loop = 0;
207  do {
208  offset = loop * MAXIMUM_WAIT_OBJECTS;
209  count = qMin(handlesCopy.count() - offset, MAXIMUM_WAIT_OBJECTS);
210  ret = WaitForMultipleObjects(count, handlesCopy.constData() + offset, false, 100);
211  loop = (loop + 1) % loops;
212  } while (ret == WAIT_TIMEOUT);
213  }
214 
215  if (ret == WAIT_FAILED || ret >= WAIT_OBJECT_0 + uint(count)) {
216  qWarning("QThread internal error while waiting for adopted threads: %d", int(GetLastError()));
217  continue;
218  }
219 
220  const int handleIndex = offset + ret - WAIT_OBJECT_0;
221  if (handleIndex == 0) // New handle to watch was added.
222  continue;
223  const int qthreadIndex = handleIndex - 1;
224 
225  qt_adopted_thread_watcher_mutex.lock();
226  QThreadData *data = QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex));
227  qt_adopted_thread_watcher_mutex.unlock();
228  if (data->isAdopted) {
229  QThread *thread = data->thread;
230  Q_ASSERT(thread);
231  auto thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
232  Q_UNUSED(thread_p);
233  Q_ASSERT(!thread_p->finished);
234  QThreadPrivate::finish(thread);
235  }
236  data->deref();
237 
238  QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
239  CloseHandle(qt_adopted_thread_handles.at(handleIndex));
240  qt_adopted_thread_handles.remove(handleIndex);
241  qt_adopted_qthreads.remove(qthreadIndex);
242  }
243 
244  QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
245  if (threadData)
246  threadData->deref();
247 
248  return 0;
249 }
250 
251 #if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
252 
253 #ifndef Q_OS_WIN64
254 # define ULONG_PTR DWORD
255 #endif
256 
257 typedef struct tagTHREADNAME_INFO
258 {
259  DWORD dwType; // must be 0x1000
260  LPCSTR szName; // pointer to name (in user addr space)
261  HANDLE dwThreadID; // thread ID (-1=caller thread)
262  DWORD dwFlags; // reserved for future use, must be zero
263 } THREADNAME_INFO;
264 
265 void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
266 {
267  THREADNAME_INFO info;
268  info.dwType = 0x1000;
269  info.szName = threadName;
270  info.dwThreadID = threadId;
271  info.dwFlags = 0;
272 
273  __try
274  {
275  RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD),
276  reinterpret_cast<const ULONG_PTR*>(&info));
277  }
278  __except (EXCEPTION_CONTINUE_EXECUTION)
279  {
280  }
281 }
282 #endif // !QT_NO_DEBUG && Q_CC_MSVC
283 
284 /**************************************************************************
285  ** QThreadPrivate
286  *************************************************************************/
287 
288 #endif // QT_CONFIG(thread)
289 
291 {
292  Q_UNUSED(data);
293  return new QEventDispatcherWin32;
294 }
295 
296 #if QT_CONFIG(thread)
297 
298 unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
299 {
300  QThread *thr = reinterpret_cast<QThread *>(arg);
302 
303  qt_create_tls();
304  TlsSetValue(qt_current_thread_data_tls_index, data);
305  data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
306 
308 
309  {
310  QMutexLocker locker(&thr->d_func()->mutex);
311  data->quitNow = thr->d_func()->exited;
312  }
313 
314  data->ensureEventDispatcher();
315  data->eventDispatcher.loadRelaxed()->startingUp();
316 
317 #if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
318  // sets the name of the current thread.
319  qt_set_thread_name(HANDLE(-1), thr->d_func()->objectName.isEmpty()
320  ? thr->metaObject()->className()
321  : std::exchange(thr->d_func()->objectName, {}).toLocal8Bit().constData());
322 #endif
323 
324  emit thr->started(QThread::QPrivateSignal());
326  thr->run();
327 
328  finish(arg);
329  return 0;
330 }
331 
332 void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
333 {
334  QThread *thr = reinterpret_cast<QThread *>(arg);
335  QThreadPrivate *d = thr->d_func();
336 
337  QMutexLocker locker(lockAnyway ? &d->mutex : 0);
338  d->isInFinish = true;
339  d->priority = QThread::InheritPriority;
340  void **tls_data = reinterpret_cast<void **>(&d->data->tls);
341  locker.unlock();
342  emit thr->finished(QThread::QPrivateSignal());
344  QThreadStorageData::finish(tls_data);
345  locker.relock();
346 
347  QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
348  if (eventDispatcher) {
349  d->data->eventDispatcher = 0;
350  locker.unlock();
351  eventDispatcher->closingDown();
352  delete eventDispatcher;
353  locker.relock();
354  }
355 
356  d->running = false;
357  d->finished = true;
358  d->isInFinish = false;
359  d->interruptionRequested = false;
360 
361  if (!d->waiters) {
362  CloseHandle(d->handle);
363  d->handle = 0;
364  }
365 
366  d->id = 0;
367 }
368 
369 /**************************************************************************
370  ** QThread
371  *************************************************************************/
372 
373 Qt::HANDLE QThread::currentThreadIdImpl() noexcept
374 {
375  return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
376 }
377 
378 int QThread::idealThreadCount() noexcept
379 {
380  SYSTEM_INFO sysinfo;
381  GetSystemInfo(&sysinfo);
382  return sysinfo.dwNumberOfProcessors;
383 }
384 
386 {
387  SwitchToThread();
388 }
389 
390 #endif // QT_CONFIG(thread)
391 
392 void QThread::sleep(unsigned long secs)
393 {
394  ::Sleep(secs * 1000);
395 }
396 
397 void QThread::msleep(unsigned long msecs)
398 {
399  ::Sleep(msecs);
400 }
401 
402 void QThread::usleep(unsigned long usecs)
403 {
404  ::Sleep((usecs / 1000) + 1);
405 }
406 
407 #if QT_CONFIG(thread)
408 
409 void QThread::start(Priority priority)
410 {
411  Q_D(QThread);
412  QMutexLocker locker(&d->mutex);
413 
414  if (d->isInFinish) {
415  locker.unlock();
416  wait();
417  locker.relock();
418  }
419 
420  if (d->running)
421  return;
422 
423  // avoid interacting with the binding system
424  d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
425  : QString();
426  d->running = true;
427  d->finished = false;
428  d->exited = false;
429  d->returnCode = 0;
430  d->interruptionRequested = false;
431 
432  /*
433  NOTE: we create the thread in the suspended state, set the
434  priority and then resume the thread.
435 
436  since threads are created with normal priority by default, we
437  could get into a case where a thread (with priority less than
438  NormalPriority) tries to create a new thread (also with priority
439  less than NormalPriority), but the newly created thread preempts
440  its 'parent' and runs at normal priority.
441  */
442 #if defined(Q_CC_MSVC) && !defined(_DLL)
443  // MSVC -MT or -MTd build
444  d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
445  this, CREATE_SUSPENDED, &(d->id));
446 #else
447  // MSVC -MD or -MDd or MinGW build
448  d->handle = CreateThread(nullptr, d->stackSize,
449  reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
450  this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
451 #endif
452 
453  if (!d->handle) {
454  qErrnoWarning("QThread::start: Failed to create thread");
455  d->running = false;
456  d->finished = true;
457  return;
458  }
459 
460  int prio;
461  d->priority = priority;
462  switch (priority) {
463  case IdlePriority:
464  prio = THREAD_PRIORITY_IDLE;
465  break;
466 
467  case LowestPriority:
468  prio = THREAD_PRIORITY_LOWEST;
469  break;
470 
471  case LowPriority:
472  prio = THREAD_PRIORITY_BELOW_NORMAL;
473  break;
474 
475  case NormalPriority:
476  prio = THREAD_PRIORITY_NORMAL;
477  break;
478 
479  case HighPriority:
480  prio = THREAD_PRIORITY_ABOVE_NORMAL;
481  break;
482 
483  case HighestPriority:
484  prio = THREAD_PRIORITY_HIGHEST;
485  break;
486 
488  prio = THREAD_PRIORITY_TIME_CRITICAL;
489  break;
490 
491  case InheritPriority:
492  default:
493  prio = GetThreadPriority(GetCurrentThread());
494  break;
495  }
496 
497  if (!SetThreadPriority(d->handle, prio)) {
498  qErrnoWarning("QThread::start: Failed to set thread priority");
499  }
500 
501  if (ResumeThread(d->handle) == (DWORD) -1) {
502  qErrnoWarning("QThread::start: Failed to resume new thread");
503  }
504 }
505 
506 void QThread::terminate()
507 {
508  Q_D(QThread);
509  QMutexLocker locker(&d->mutex);
510  if (!d->running)
511  return;
512  if (!d->terminationEnabled) {
513  d->terminatePending = true;
514  return;
515  }
516 
517  TerminateThread(d->handle, 0);
518  QThreadPrivate::finish(this, false);
519 }
520 
522 {
523  Q_D(QThread);
524  QMutexLocker locker(&d->mutex);
525 
526  if (d->id == GetCurrentThreadId()) {
527  qWarning("QThread::wait: Thread tried to wait on itself");
528  return false;
529  }
530  if (d->finished || !d->running)
531  return true;
532 
533  ++d->waiters;
534  locker.mutex()->unlock();
535 
536  bool ret = false;
537  switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
538  case WAIT_OBJECT_0:
539  ret = true;
540  break;
541  case WAIT_FAILED:
542  qErrnoWarning("QThread::wait: Thread wait failure");
543  break;
544  case WAIT_ABANDONED:
545  case WAIT_TIMEOUT:
546  default:
547  break;
548  }
549 
550  locker.mutex()->lock();
551  --d->waiters;
552 
553  if (ret && !d->finished) {
554  // thread was terminated by someone else
555 
556  QThreadPrivate::finish(this, false);
557  }
558 
559  if (d->finished && !d->waiters) {
560  CloseHandle(d->handle);
561  d->handle = 0;
562  }
563 
564  return ret;
565 }
566 
568 {
569  QThread *thr = currentThread();
570  Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
571  "Current thread was not started with QThread.");
572  QThreadPrivate *d = thr->d_func();
573  QMutexLocker locker(&d->mutex);
574  d->terminationEnabled = enabled;
575  if (enabled && d->terminatePending) {
576  QThreadPrivate::finish(thr, false);
577  locker.unlock(); // don't leave the mutex locked!
578  _endthreadex(0);
579  }
580 }
581 
582 // Caller must hold the mutex
584 {
585  // copied from start() with a few modifications:
586 
587  int prio;
588  priority = threadPriority;
589  switch (threadPriority) {
591  prio = THREAD_PRIORITY_IDLE;
592  break;
593 
595  prio = THREAD_PRIORITY_LOWEST;
596  break;
597 
599  prio = THREAD_PRIORITY_BELOW_NORMAL;
600  break;
601 
603  prio = THREAD_PRIORITY_NORMAL;
604  break;
605 
607  prio = THREAD_PRIORITY_ABOVE_NORMAL;
608  break;
609 
611  prio = THREAD_PRIORITY_HIGHEST;
612  break;
613 
615  prio = THREAD_PRIORITY_TIME_CRITICAL;
616  break;
617 
618  default:
619  return;
620  }
621 
622  if (!SetThreadPriority(handle, prio)) {
623  qErrnoWarning("QThread::setPriority: Failed to set thread priority");
624  }
625 }
626 
627 #endif // QT_CONFIG(thread)
628 
The QAbstractEventDispatcher class provides an interface to manage Qt's event queue.
Type loadRelaxed() const noexcept
Definition: qbasicatomic.h:226
void storeRelaxed(Type newValue) noexcept
Definition: qbasicatomic.h:227
static void sendPostedEvents(QObject *receiver=nullptr, int event_type=0)
static QBasicAtomicPointer< QThread > theMainThread
The QDeadlineTimer class marks a deadline in the future.
qint64 remainingTime() const noexcept
@ DeferredDelete
Definition: qcoreevent.h:113
qsizetype size() const noexcept
Definition: qlist.h:414
const_pointer constData() const noexcept
Definition: qlist.h:444
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
void remove(qsizetype i, qsizetype n=1)
Definition: qlist.h:798
qsizetype count() const noexcept
Definition: qlist.h:415
void prepend(rvalue_ref t)
Definition: qlist.h:484
void append(parameter_type t)
Definition: qlist.h:469
The QMutex class provides access serialization between threads.
Definition: qmutex.h:285
void unlock() noexcept
Definition: qmutex.h:293
void lock() noexcept
Definition: qmutex.h:290
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes.
Definition: qmutex.h:317
QString objectName
the name of this object
Definition: qobject.h:129
QThread * thread() const
Definition: qobject.cpp:1527
static QObjectPrivate * get(QObject *o)
Definition: qobject_p.h:368
bool isEmpty() const
Definition: qstring.h:1216
static Q_AUTOTEST_EXPORT QThreadData * current(bool createIfNecessary=true)
Definition: qthread.cpp:908
void deref()
Definition: qthread.cpp:116
QThreadData(int initialRefCount=1)
Definition: qthread.cpp:60
static QThreadData * get2(QThread *thread)
Definition: qthread_p.h:244
static void clearCurrentThreadData()
Definition: qthread.cpp:922
bool isAdopted
Definition: qthread_p.h:304
QAtomicPointer< void > threadId
Definition: qthread_p.h:297
QAtomicPointer< QThread > thread
Definition: qthread_p.h:296
void terminate()
Definition: qthread.cpp:843
void start(Priority=InheritPriority)
Definition: qthread.cpp:836
QAbstractEventDispatcher * eventDispatcher() const
Definition: qthread.cpp:967
static int idealThreadCount() noexcept
Definition: qthread.cpp:884
static void setTerminationEnabled(bool enabled=true)
virtual void run()
Definition: qthread.cpp:826
static QThread * currentThread()
Definition: qthread.cpp:879
Priority
Definition: qthread.h:76
@ LowPriority
Definition: qthread.h:80
@ LowestPriority
Definition: qthread.h:79
@ TimeCriticalPriority
Definition: qthread.h:85
@ HighestPriority
Definition: qthread.h:83
@ InheritPriority
Definition: qthread.h:87
@ IdlePriority
Definition: qthread.h:77
@ NormalPriority
Definition: qthread.h:81
@ HighPriority
Definition: qthread.h:82
static void yieldCurrentThread()
Definition: qthread.cpp:889
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition: qthread.cpp:863
static void usleep(unsigned long)
Priority priority() const
void finished(QPrivateSignal)
static void msleep(unsigned long)
static void sleep(unsigned long)
void started(QPrivateSignal)
static QAbstractEventDispatcher * createEventDispatcher(QThreadData *data)
myinstance setPriority(MyClass::VeryHigh)
#define NULL
Definition: ftobjs.h:61
#define FALSE
Definition: ftobjs.h:57
#define forever
Definition: ftrandom.c:53
backing_store_ptr info
[4]
Definition: jmemsys.h:161
void * HANDLE
Definition: qnamespace.h:1561
#define QString()
Definition: parse-defines.h:51
size_t quintptr
Definition: qglobal.h:310
unsigned int uint
Definition: qglobal.h:334
Q_CORE_EXPORT Q_DECL_COLD_FUNCTION void qErrnoWarning(int code, const char *msg,...)
#define qWarning
Definition: qlogging.h:179
GLuint64 GLenum void * handle
GLenum GLenum GLsizei count
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define Q_ASSERT_X(cond, x, msg)
Definition: qrandom.cpp:85
SSL_CTX int(*) void arg)
#define emit
Definition: qtmetamacros.h:85
#define enabled
Definition: qopenglext.h:3098
Q_UNUSED(salary)
[21]
QDeadlineTimer deadline(30s)
QReadWriteLock lock
[0]
QMutex mutex