CallOnce.h 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /*
  2. * Copyright 2016-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <atomic>
  18. #include <mutex>
  19. #include <utility>
  20. #include <folly/Likely.h>
  21. #include <folly/Portability.h>
  22. #include <folly/SharedMutex.h>
  23. #include <folly/functional/Invoke.h>
  24. namespace folly {
  25. template <typename Mutex, template <typename> class Atom = std::atomic>
  26. class basic_once_flag;
  27. // call_once
  28. //
  29. // Drop-in replacement for std::call_once.
  30. //
  31. // The libstdc++ implementation has two flaws:
  32. // * it lacks a fast path, and
  33. // * it deadlocks (in explicit violation of the standard) when invoked twice
  34. // with a given flag, and the callable passed to the first invocation throws.
  35. //
  36. // This implementation corrects both flaws.
  37. //
  38. // The tradeoff is a slightly larger once_flag struct at 8 bytes, vs 4 bytes
  39. // with libstdc++ on Linux/x64.
  40. //
  41. // Does not work with std::once_flag.
  42. //
  43. // mimic: std::call_once
  44. template <
  45. typename Mutex,
  46. template <typename> class Atom,
  47. typename F,
  48. typename... Args>
  49. FOLLY_ALWAYS_INLINE void
  50. call_once(basic_once_flag<Mutex, Atom>& flag, F&& f, Args&&... args) {
  51. flag.call_once(std::forward<F>(f), std::forward<Args>(args)...);
  52. }
  53. // basic_once_flag
  54. //
  55. // The flag template to be used with call_once. Parameterizable by the mutex
  56. // type and atomic template. The mutex type is required to mimic std::mutex and
  57. // the atomic type is required to mimic std::atomic.
  58. template <typename Mutex, template <typename> class Atom>
  59. class basic_once_flag {
  60. public:
  61. constexpr basic_once_flag() noexcept = default;
  62. basic_once_flag(const basic_once_flag&) = delete;
  63. basic_once_flag& operator=(const basic_once_flag&) = delete;
  64. private:
  65. template <
  66. typename Mutex_,
  67. template <typename> class Atom_,
  68. typename F,
  69. typename... Args>
  70. friend void call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...);
  71. template <typename F, typename... Args>
  72. FOLLY_ALWAYS_INLINE void call_once(F&& f, Args&&... args) {
  73. if (LIKELY(called_.load(std::memory_order_acquire))) {
  74. return;
  75. }
  76. call_once_slow(std::forward<F>(f), std::forward<Args>(args)...);
  77. }
  78. template <typename F, typename... Args>
  79. FOLLY_NOINLINE void call_once_slow(F&& f, Args&&... args) {
  80. std::lock_guard<Mutex> lock(mutex_);
  81. if (called_.load(std::memory_order_relaxed)) {
  82. return;
  83. }
  84. invoke(std::forward<F>(f), std::forward<Args>(args)...);
  85. called_.store(true, std::memory_order_release);
  86. }
  87. Atom<bool> called_{false};
  88. Mutex mutex_;
  89. };
  90. // once_flag
  91. //
  92. // The flag type to be used with call_once. An instance of basic_once_flag.
  93. //
  94. // Does not work with sd::call_once.
  95. //
  96. // mimic: std::once_flag
  97. using once_flag = basic_once_flag<SharedMutex>;
  98. } // namespace folly