]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/third-party/folly/folly/synchronization/detail/InlineFunctionRef.h
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
11 #include <folly/Traits.h>
12 #include <folly/Utility.h>
13 #include <folly/functional/Invoke.h>
14 #include <folly/lang/Launder.h>
20 * InlineFunctionRef is similar to folly::FunctionRef but has the additional
21 * benefit of being able to store the function it was instantiated with inline
22 * in a buffer of the given capacity. Inline storage is only used if the
23 * function object and a pointer (for type-erasure) are small enough to fit in
24 * the templated size. If there is not enough in-situ capacity for the
25 * callable, this just stores a reference to the function object like
28 * This helps give a perf boost in the case where the data gets separated from
29 * the point of invocation. If, for example, at the point of invocation, the
30 * InlineFunctionRef object is not cached, a remote memory/cache read might be
31 * required to invoke the original callable. Customizable inline storage
32 * helps tune storage so we can store a type-erased callable with better
33 * performance and locality. A real-life example of this might be a
34 * folly::FunctionRef with a function pointer. The folly::FunctionRef would
35 * point to the function pointer object in a remote location. This causes a
36 * double-indirection at the point of invocation, and if that memory is dirty,
37 * or not cached, it would cause additional cache misses. On the other hand
38 * with InlineFunctionRef, inline storage would store the value of the
39 * function pointer, avoiding the need to do a remote lookup to fetch the
40 * value of the function pointer.
42 * To prevent misuse, InlineFunctionRef disallows construction from an lvalue
43 * callable. This is to prevent usage where a user relies on the callable's
44 * state after invocation through InlineFunctionRef. This has the potential
45 * to copy the callable into inline storage when the callable is small, so we
46 * might not use the same function when invoking, but rather a copy of it.
48 * Also note that InlineFunctionRef will always invoke the const qualified
49 * version of the call operator for any callable that is passed. Regardless
50 * of whether it has a non-const version. This is done to enforce the logical
51 * constraint of function state being immutable.
53 * This class is always trivially-copyable (and therefore
54 * trivially-destructible), making it suitable for use in a union without
55 * requiring manual destruction.
57 template <typename FunctionType
, std::size_t Size
>
58 class InlineFunctionRef
;
60 template <typename ReturnType
, typename
... Args
, std::size_t Size
>
61 class InlineFunctionRef
<ReturnType(Args
...), Size
> {
63 _t
<std::aligned_storage
<Size
- sizeof(uintptr_t), sizeof(uintptr_t)>>;
64 using Call
= ReturnType (*)(const Storage
&, Args
&&...);
70 (Size
% sizeof(uintptr_t)) == 0,
71 "Size has to be a multiple of sizeof(uintptr_t)");
72 static_assert(Size
>= 2 * sizeof(uintptr_t), "This doesn't work");
73 static_assert(alignof(Call
) == alignof(Storage
), "Mismatching alignments");
75 // This defines a mode tag that is used in the construction of
76 // InlineFunctionRef to determine the storage and indirection method for the
79 // This requires that the we pass in a type that is not ref-qualified.
80 template <typename Func
>
81 using ConstructMode
= _t
<std::conditional
<
82 folly::is_trivially_copyable
<Func
>{} &&
83 (sizeof(Func
) <= sizeof(Storage
)) &&
84 (alignof(Func
) <= alignof(Storage
)),
90 * InlineFunctionRef can be constructed from a nullptr, callable or another
91 * InlineFunctionRef with the same size. These are the constructors that
92 * don't take a callable.
94 * InlineFunctionRef is meant to be trivially copyable so we default the
95 * constructors and assignment operators.
97 InlineFunctionRef(std::nullptr_t
) : call_
{nullptr} {}
98 InlineFunctionRef() : call_
{nullptr} {}
99 InlineFunctionRef(const InlineFunctionRef
& other
) = default;
100 InlineFunctionRef(InlineFunctionRef
&&) = default;
101 InlineFunctionRef
& operator=(const InlineFunctionRef
&) = default;
102 InlineFunctionRef
& operator=(InlineFunctionRef
&&) = default;
105 * Constructors from callables.
107 * If all of the following conditions are satisfied, then we store the
108 * callable in the inline storage:
110 * 1) The function has been passed as an rvalue, meaning that there is no
111 * use of the original in the user's code after it has been passed to
113 * 2) Size of the callable is less than the size of the inline storage
115 * 3) The callable is trivially constructible and destructible.
117 * If any one of the above conditions is not satisfied, we fall back to
118 * reference semantics and store the function as a pointer, and add a level
119 * of indirection through type erasure.
124 !std::is_same
<_t
<std::decay
<Func
>>, InlineFunctionRef
>{} &&
125 !std::is_reference
<Func
>{} &&
127 decltype(std::declval
<Func
&&>()(std::declval
<Args
&&>()...)),
128 ReturnType
>{}>>* = nullptr>
129 InlineFunctionRef(Func
&& func
) {
130 // We disallow construction from lvalues, so assert that this is not a
131 // reference type. When invoked with an lvalue, Func is a lvalue
132 // reference type, when invoked with an rvalue, Func is not ref-qualified.
134 !std::is_reference
<Func
>{},
135 "InlineFunctionRef cannot be used with lvalues");
136 static_assert(std::is_rvalue_reference
<Func
&&>{}, "");
137 construct(ConstructMode
<Func
>{}, folly::as_const(func
));
141 * The call operator uses the function pointer and a reference to the
142 * storage to do the dispatch. The function pointer takes care of the
143 * appropriate casting.
145 ReturnType
operator()(Args
... args
) const {
146 return call_(storage_
, static_cast<Args
&&>(args
)...);
150 * We have a function engaged if the call function points to anything other
153 operator bool() const noexcept
{
158 friend class InlineFunctionRefTest
;
161 * Inline storage constructor implementation.
163 template <typename Func
>
164 void construct(InSituTag
, Func
& func
) {
165 using Value
= _t
<std::remove_reference
<Func
>>;
167 // Assert that the following two assumptions are valid
168 // 1) fit in the storage space we have and match alignments, and
169 // 2) be invocable in a const context, it does not make sense to copy a
170 // callable into inline storage if it makes state local
172 static_assert(alignof(Value
) <= alignof(Storage
), "");
173 static_assert(is_invocable
<const _t
<std::decay
<Func
>>, Args
&&...>{}, "");
174 static_assert(folly::is_trivially_copyable
<Value
>{}, "");
176 new (&storage_
) Value
{func
};
177 call_
= &callInline
<Value
>;
181 * Ref storage constructor implementation. This is identical to
182 * folly::FunctionRef.
184 template <typename Func
>
185 void construct(RefTag
, Func
& func
) {
186 // store a pointer to the function
187 using Pointer
= _t
<std::add_pointer
<_t
<std::remove_reference
<Func
>>>>;
188 new (&storage_
) Pointer
{&func
};
189 call_
= &callPointer
<Pointer
>;
192 template <typename Func
>
193 static ReturnType
callInline(const Storage
& object
, Args
&&... args
) {
194 // The only type of pointer allowed is a function pointer, no other
195 // pointer types are invocable.
197 !std::is_pointer
<Func
>::value
||
198 std::is_function
<_t
<std::remove_pointer
<Func
>>>::value
,
200 return (*folly::launder(reinterpret_cast<const Func
*>(&object
)))(
201 static_cast<Args
&&>(args
)...);
204 template <typename Func
>
205 static ReturnType
callPointer(const Storage
& object
, Args
&&... args
) {
206 // When the function we were instantiated with was not trivial, the given
207 // pointer points to a pointer, which pointers to the callable. So we
208 // cast to a pointer and then to the pointee.
209 static_assert(std::is_pointer
<Func
>::value
, "");
210 return (**folly::launder(reinterpret_cast<const Func
*>(&object
)))(
211 static_cast<Args
&&>(args
)...);
218 } // namespace detail