2 * Copyright 2016-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <folly/Likely.h>
24 #include <folly/Portability.h>
25 #include <folly/SharedMutex.h>
26 #include <folly/functional/Invoke.h>
30 template <typename Mutex, template <typename> class Atom = std::atomic>
31 class basic_once_flag;
35 // Drop-in replacement for std::call_once.
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.
42 // This implementation corrects both flaws.
44 // The tradeoff is a slightly larger once_flag struct at 8 bytes, vs 4 bytes
45 // with libstdc++ on Linux/x64.
47 // Does not work with std::once_flag.
49 // mimic: std::call_once
52 template <typename> class Atom,
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)...);
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 {
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;
75 template <typename> class Atom_,
78 friend void call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...);
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))) {
85 call_once_slow(std::forward<F>(f), std::forward<Args>(args)...);
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)) {
94 invoke(std::forward<F>(f), std::forward<Args>(args)...);
95 called_.store(true, std::memory_order_release);
98 Atom<bool> called_{false};
104 // The flag type to be used with call_once. An instance of basic_once_flag.
106 // Does not work with sd::call_once.
108 // mimic: std::once_flag
109 using once_flag = basic_once_flag<SharedMutex>;