blob: 4dd06941a25bb3f584f644f988738d51e9588f3f [file] [log] [blame]
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Drop-in replacement for std::call_once() with a fast path, which the GCC
* implementation lacks. The tradeoff is a slightly larger `once_flag' struct
* (8 bytes vs 4 bytes with GCC on Linux/x64).
*
* $ call_once_test --benchmark --bm_min_iters=100000000 --threads=16
* ============================================================================
* folly/test/CallOnceTest.cpp relative time/iter iters/s
* ============================================================================
* StdCallOnceBench 3.54ns 282.82M
* FollyCallOnceBench 698.48ps 1.43G
* ============================================================================
*/
#pragma once
#include <atomic>
#include <mutex>
#include <utility>
#include <folly/Likely.h>
#include <folly/Portability.h>
#include <folly/SharedMutex.h>
namespace folly {
class once_flag {
public:
constexpr once_flag() noexcept = default;
once_flag(const once_flag&) = delete;
once_flag& operator=(const once_flag&) = delete;
template <typename Callable, class... Args>
friend void call_once(once_flag& flag, Callable&& f, Args&&... args);
template <typename Callable, class... Args>
friend void call_once_impl_no_inline(once_flag& flag,
Callable&& f,
Args&&... args);
private:
std::atomic<bool> called_{false};
folly::SharedMutex mutex_;
};
template <class Callable, class... Args>
void FOLLY_ALWAYS_INLINE
call_once(once_flag& flag, Callable&& f, Args&&... args) {
if (LIKELY(flag.called_.load(std::memory_order_acquire))) {
return;
}
call_once_impl_no_inline(
flag, std::forward<Callable>(f), std::forward<Args>(args)...);
}
// Implementation detail: out-of-line slow path
template <class Callable, class... Args>
void FOLLY_NOINLINE
call_once_impl_no_inline(once_flag& flag, Callable&& f, Args&&... args) {
std::lock_guard<folly::SharedMutex> lg(flag.mutex_);
if (flag.called_) {
return;
}
std::forward<Callable>(f)(std::forward<Args>(args)...);
flag.called_.store(true, std::memory_order_release);
}
}