logging: rename the `DEBUG` log level to `DBG`
[folly.git] / folly / synchronization / CallOnce.h
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
17 #pragma once
18
19 #include <atomic>
20 #include <mutex>
21 #include <utility>
22
23 #include <folly/Likely.h>
24 #include <folly/Portability.h>
25 #include <folly/SharedMutex.h>
26 #include <folly/functional/Invoke.h>
27
28 namespace folly {
29
30 template <typename Mutex, template <typename> class Atom = std::atomic>
31 class basic_once_flag;
32
33 //  call_once
34 //
35 //  Drop-in replacement for std::call_once.
36 //
37 //  The libstdc++ implementation has two flaws:
38 //  * it lacks a fast path, and
39 //  * it deadlocks (in explicit violation of the standard) when invoked twice
40 //    with a given flag, and the callable passed to the first invocation throws.
41 //
42 //  This implementation corrects both flaws.
43 //
44 //  The tradeoff is a slightly larger once_flag struct at 8 bytes, vs 4 bytes
45 //  with libstdc++ on Linux/x64.
46 //
47 //  Does not work with std::once_flag.
48 //
49 //  mimic: std::call_once
50 template <
51     typename Mutex,
52     template <typename> class Atom,
53     typename F,
54     typename... Args>
55 FOLLY_ALWAYS_INLINE void
56 call_once(basic_once_flag<Mutex, Atom>& flag, F&& f, Args&&... args) {
57   flag.call_once(std::forward<F>(f), std::forward<Args>(args)...);
58 }
59
60 //  basic_once_flag
61 //
62 //  The flag template to be used with call_once. Parameterizable by the mutex
63 //  type and atomic template. The mutex type is required to mimic std::mutex and
64 //  the atomic type is required to mimic std::atomic.
65 template <typename Mutex, template <typename> class Atom>
66 class basic_once_flag {
67  public:
68   constexpr basic_once_flag() noexcept = default;
69   basic_once_flag(const basic_once_flag&) = delete;
70   basic_once_flag& operator=(const basic_once_flag&) = delete;
71
72  private:
73   template <
74       typename Mutex_,
75       template <typename> class Atom_,
76       typename F,
77       typename... Args>
78   friend void call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...);
79
80   template <typename F, typename... Args>
81   FOLLY_ALWAYS_INLINE void call_once(F&& f, Args&&... args) {
82     if (LIKELY(called_.load(std::memory_order_acquire))) {
83       return;
84     }
85     call_once_slow(std::forward<F>(f), std::forward<Args>(args)...);
86   }
87
88   template <typename F, typename... Args>
89   FOLLY_NOINLINE void call_once_slow(F&& f, Args&&... args) {
90     std::lock_guard<Mutex> lock(mutex_);
91     if (called_.load(std::memory_order_relaxed)) {
92       return;
93     }
94     invoke(std::forward<F>(f), std::forward<Args>(args)...);
95     called_.store(true, std::memory_order_release);
96   }
97
98   Atom<bool> called_{false};
99   Mutex mutex_;
100 };
101
102 //  once_flag
103 //
104 //  The flag type to be used with call_once. An instance of basic_once_flag.
105 //
106 //  Does not work with sd::call_once.
107 //
108 //  mimic: std::once_flag
109 using once_flag = basic_once_flag<SharedMutex>;
110
111 } // namespace folly