call_once.h 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. /*
  2. * Copyright (C) 2016 Alexander Makarov
  3. *
  4. * Source:
  5. * https://wiki.qt.io/Qt_thread-safe_singleton
  6. *
  7. * This file is a part of Breakpad-qt library.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2.1 of the License, or (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. */
  20. #ifndef CALL_ONCE
  21. #define CALL_ONCE
  22. #include <QtGlobal>
  23. #include <QAtomicInt>
  24. #include <QMutex>
  25. #include <QWaitCondition>
  26. #include <QThreadStorage>
  27. #include <QThread>
  28. namespace CallOnce {
  29. enum ECallOnce {
  30. CO_Request,
  31. CO_InProgress,
  32. CO_Finished
  33. };
  34. Q_GLOBAL_STATIC(QThreadStorage<QAtomicInt*>, once_flag)
  35. }
  36. template <class Function>
  37. inline static void qCallOnce(Function func, QBasicAtomicInt& flag)
  38. {
  39. using namespace CallOnce;
  40. #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  41. int protectFlag = flag.fetchAndStoreAcquire(flag);
  42. #elif QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
  43. int protectFlag = flag.fetchAndStoreAcquire(flag.loadRelaxed());
  44. #else
  45. int protectFlag = flag.fetchAndStoreAcquire(flag.load());
  46. #endif
  47. if (protectFlag == CO_Finished)
  48. return;
  49. if (protectFlag == CO_Request && flag.testAndSetRelaxed(protectFlag,
  50. CO_InProgress)) {
  51. func();
  52. flag.fetchAndStoreRelease(CO_Finished);
  53. }
  54. else {
  55. do {
  56. QThread::yieldCurrentThread();
  57. }
  58. while (!flag.testAndSetAcquire(CO_Finished, CO_Finished));
  59. }
  60. }
  61. template <class Function>
  62. inline static void qCallOncePerThread(Function func)
  63. {
  64. using namespace CallOnce;
  65. if (!once_flag()->hasLocalData()) {
  66. once_flag()->setLocalData(new QAtomicInt(CO_Request));
  67. qCallOnce(func, *once_flag()->localData());
  68. }
  69. }
  70. #endif // CALL_ONCE