Make symbolic names for UntypedFunction's inline storage size
Because duplicating constant values in lots of places is bad. Bug: webrtc:11943 Change-Id: I00107718444bb0433d5ecd860ac0902f8afe2cb0 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/186842 Commit-Queue: Lahiru Ginnaliya Gamathige <glahiru@webrtc.org> Reviewed-by: Lahiru Ginnaliya Gamathige <glahiru@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32334}
This commit is contained in:
parent
3e3e16682d
commit
84ba18ab14
@ -135,15 +135,14 @@ struct LargeNonTrivial {
|
|||||||
TEST(RoboCaller, LargeNonTrivialTest) {
|
TEST(RoboCaller, LargeNonTrivialTest) {
|
||||||
RoboCaller<int&> c;
|
RoboCaller<int&> c;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
static_assert(sizeof(LargeNonTrivial) > 16, "");
|
static_assert(sizeof(LargeNonTrivial) > UntypedFunction::kInlineStorageSize,
|
||||||
|
"");
|
||||||
c.AddReceiver(LargeNonTrivial());
|
c.AddReceiver(LargeNonTrivial());
|
||||||
c.Send(i);
|
c.Send(i);
|
||||||
|
|
||||||
EXPECT_EQ(i, 1);
|
EXPECT_EQ(i, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sizeof(LargeTrivial) = 20bytes which is greater than
|
|
||||||
* the size check (16bytes) of the CSC library */
|
|
||||||
struct LargeTrivial {
|
struct LargeTrivial {
|
||||||
int a[17];
|
int a[17];
|
||||||
void operator()(int& x) { x = 1; }
|
void operator()(int& x) { x = 1; }
|
||||||
@ -154,7 +153,7 @@ TEST(RoboCaller, LargeTrivial) {
|
|||||||
LargeTrivial lt;
|
LargeTrivial lt;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
static_assert(sizeof(lt) > 16, "");
|
static_assert(sizeof(lt) > UntypedFunction::kInlineStorageSize, "");
|
||||||
c.AddReceiver(lt);
|
c.AddReceiver(lt);
|
||||||
c.Send(i);
|
c.Send(i);
|
||||||
|
|
||||||
|
|||||||
@ -24,10 +24,14 @@ namespace webrtc_function_impl {
|
|||||||
|
|
||||||
using FunVoid = void();
|
using FunVoid = void();
|
||||||
|
|
||||||
|
// Inline storage size is this many machine words.
|
||||||
|
enum : size_t { kInlineStorageWords = 4 };
|
||||||
|
|
||||||
union VoidUnion {
|
union VoidUnion {
|
||||||
void* void_ptr;
|
void* void_ptr;
|
||||||
FunVoid* fun_ptr;
|
FunVoid* fun_ptr;
|
||||||
typename std::aligned_storage<4 * sizeof(uintptr_t)>::type inline_storage;
|
typename std::aligned_storage<kInlineStorageWords * sizeof(uintptr_t)>::type
|
||||||
|
inline_storage;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the number of elements of the `inline_storage` array required to
|
// Returns the number of elements of the `inline_storage` array required to
|
||||||
@ -74,6 +78,16 @@ struct CallHelpers<RetT(ArgT...)> {
|
|||||||
// size.
|
// size.
|
||||||
class UntypedFunction final {
|
class UntypedFunction final {
|
||||||
public:
|
public:
|
||||||
|
// Callables of at most this size can be stored inline, if they are trivial.
|
||||||
|
// (Useful in tests and benchmarks; avoid using this in production code.)
|
||||||
|
enum : size_t {
|
||||||
|
kInlineStorageSize = sizeof(webrtc_function_impl::VoidUnion::inline_storage)
|
||||||
|
};
|
||||||
|
static_assert(kInlineStorageSize ==
|
||||||
|
webrtc_function_impl::kInlineStorageWords *
|
||||||
|
sizeof(uintptr_t),
|
||||||
|
"");
|
||||||
|
|
||||||
// The *UntypedFunctionArgs structs are used to transfer arguments from
|
// The *UntypedFunctionArgs structs are used to transfer arguments from
|
||||||
// PrepareArgs() to Create(). They are trivial, but may own heap allocations,
|
// PrepareArgs() to Create(). They are trivial, but may own heap allocations,
|
||||||
// so make sure to pass them to Create() exactly once!
|
// so make sure to pass them to Create() exactly once!
|
||||||
@ -85,6 +99,8 @@ class UntypedFunction final {
|
|||||||
// other.
|
// other.
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
struct TrivialUntypedFunctionArgs {
|
struct TrivialUntypedFunctionArgs {
|
||||||
|
static_assert(N >= 1, "");
|
||||||
|
static_assert(N <= webrtc_function_impl::kInlineStorageWords, "");
|
||||||
// We use an uintptr_t array here instead of std::aligned_storage, because
|
// We use an uintptr_t array here instead of std::aligned_storage, because
|
||||||
// the former can be efficiently passed in registers when using
|
// the former can be efficiently passed in registers when using
|
||||||
// TrivialUntypedFunctionArgs as a function argument. (We can't do the same
|
// TrivialUntypedFunctionArgs as a function argument. (We can't do the same
|
||||||
@ -125,13 +141,10 @@ class UntypedFunction final {
|
|||||||
!std::is_same<UntypedFunction,
|
!std::is_same<UntypedFunction,
|
||||||
typename std::remove_cv<F_deref>::type>::value &&
|
typename std::remove_cv<F_deref>::type>::value &&
|
||||||
|
|
||||||
// Only for trivial callables that will fit in
|
// Only for trivial callables that will fit in inline storage.
|
||||||
// VoidUnion::inline_storage.
|
|
||||||
std::is_trivially_move_constructible<F_deref>::value &&
|
std::is_trivially_move_constructible<F_deref>::value &&
|
||||||
std::is_trivially_destructible<F_deref>::value &&
|
std::is_trivially_destructible<F_deref>::value &&
|
||||||
sizeof(F_deref) <=
|
sizeof(F_deref) <= kInlineStorageSize>::type* = nullptr,
|
||||||
sizeof(webrtc_function_impl::VoidUnion::inline_storage)>::type* =
|
|
||||||
nullptr,
|
|
||||||
size_t InlineSize = webrtc_function_impl::InlineStorageSize<F_deref>()>
|
size_t InlineSize = webrtc_function_impl::InlineStorageSize<F_deref>()>
|
||||||
static TrivialUntypedFunctionArgs<InlineSize> PrepareArgs(F&& f) {
|
static TrivialUntypedFunctionArgs<InlineSize> PrepareArgs(F&& f) {
|
||||||
// The callable is trivial and small enough, so we just store its bytes
|
// The callable is trivial and small enough, so we just store its bytes
|
||||||
@ -154,12 +167,12 @@ class UntypedFunction final {
|
|||||||
// Create function for lambdas and other callables that are nontrivial or
|
// Create function for lambdas and other callables that are nontrivial or
|
||||||
// large; it accepts every type of argument except those noted in its
|
// large; it accepts every type of argument except those noted in its
|
||||||
// enable_if call.
|
// enable_if call.
|
||||||
template <
|
template <typename Signature,
|
||||||
typename Signature,
|
|
||||||
typename F,
|
typename F,
|
||||||
typename F_deref = typename std::remove_reference<F>::type,
|
typename F_deref = typename std::remove_reference<F>::type,
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
// Not for function pointers; we have another overload for that below.
|
// Not for function pointers; we have another overload for that
|
||||||
|
// below.
|
||||||
!std::is_function<
|
!std::is_function<
|
||||||
typename std::remove_pointer<F_deref>::type>::value &&
|
typename std::remove_pointer<F_deref>::type>::value &&
|
||||||
|
|
||||||
@ -173,11 +186,10 @@ class UntypedFunction final {
|
|||||||
typename std::remove_cv<F_deref>::type>::value &&
|
typename std::remove_cv<F_deref>::type>::value &&
|
||||||
|
|
||||||
// Only for nontrivial callables, or callables that won't fit in
|
// Only for nontrivial callables, or callables that won't fit in
|
||||||
// VoidUnion::inline_storage.
|
// inline storage.
|
||||||
!(std::is_trivially_move_constructible<F_deref>::value &&
|
!(std::is_trivially_move_constructible<F_deref>::value &&
|
||||||
std::is_trivially_destructible<F_deref>::value &&
|
std::is_trivially_destructible<F_deref>::value &&
|
||||||
sizeof(F_deref) <= sizeof(webrtc_function_impl::VoidUnion::
|
sizeof(F_deref) <= kInlineStorageSize)>::type* = nullptr>
|
||||||
inline_storage))>::type* = nullptr>
|
|
||||||
static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) {
|
static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) {
|
||||||
// The callable is either nontrivial or too large, so we can't keep it
|
// The callable is either nontrivial or too large, so we can't keep it
|
||||||
// in the inline storage; use the heap instead.
|
// in the inline storage; use the heap instead.
|
||||||
|
|||||||
@ -163,6 +163,7 @@ TEST(UntypedFunction, CallFunctionPointerWithRvalueReference) {
|
|||||||
|
|
||||||
TEST(UntypedFunction, CallTrivialWithNoArgs) {
|
TEST(UntypedFunction, CallTrivialWithNoArgs) {
|
||||||
int arr[] = {1, 2, 3};
|
int arr[] = {1, 2, 3};
|
||||||
|
static_assert(sizeof(arr) <= UntypedFunction::kInlineStorageSize, "");
|
||||||
auto uf = UntypedFunction::Create<int()>([arr] { return arr[1]; });
|
auto uf = UntypedFunction::Create<int()>([arr] { return arr[1]; });
|
||||||
EXPECT_TRUE(uf);
|
EXPECT_TRUE(uf);
|
||||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||||
@ -171,6 +172,7 @@ TEST(UntypedFunction, CallTrivialWithNoArgs) {
|
|||||||
|
|
||||||
TEST(UntypedFunction, CallLargeTrivialWithNoArgs) {
|
TEST(UntypedFunction, CallLargeTrivialWithNoArgs) {
|
||||||
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
|
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
|
||||||
|
static_assert(sizeof(arr) > UntypedFunction::kInlineStorageSize, "");
|
||||||
auto uf = UntypedFunction::Create<int()>([arr] { return arr[4]; });
|
auto uf = UntypedFunction::Create<int()>([arr] { return arr[4]; });
|
||||||
EXPECT_TRUE(uf);
|
EXPECT_TRUE(uf);
|
||||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user