call_once.h 1.22 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
#ifndef CALL_ONCE_H
#define CALL_ONCE_H

#include <QAtomicInt>
#include <QMutex>
#include <QThread>
#include <QThreadStorage>
#include <QWaitCondition>
#include <QtGlobal>

namespace CallOnce {
enum ECallOnce { CO_Request, CO_InProgress, CO_Finished };

Q_GLOBAL_STATIC(QThreadStorage<QAtomicInt *>, once_flag)
} // namespace CallOnce

template <class Function>
inline static void qCallOnce(Function func, QBasicAtomicInt &flag) {
  using namespace CallOnce;

#if QT_VERSION < 0x050000
  int protectFlag = flag.fetchAndStoreAcquire(flag);
#elif QT_VERSION >= 0x050000
  int protectFlag = flag.fetchAndStoreAcquire(flag.load());
#endif

  if (protectFlag == CO_Finished)
    return;
  if (protectFlag == CO_Request &&
      flag.testAndSetRelaxed(protectFlag, CO_InProgress)) {
    func();
    flag.fetchAndStoreRelease(CO_Finished);
  } else {
    do {
      QThread::yieldCurrentThread();
    } while (!flag.testAndSetAcquire(CO_Finished, CO_Finished));
  }
}

template <class Function> inline static void qCallOncePerThread(Function func) {
  using namespace CallOnce;
  if (!once_flag()->hasLocalData()) {
    once_flag()->setLocalData(new QAtomicInt(CO_Request));
    qCallOnce(func, *once_flag()->localData());
  }
}

#endif // CALL_ONCE_H