Commit 88396607 authored by Etienne Pierre-doray's avatar Etienne Pierre-doray Committed by Commit Bot

[Task Scheduler]: Make task traits more robust and simple.

Main issue: TaskTraits constructor is not robust; any
signature is part of the overload set, even if it may not compile.
Because of that, it may not possible to hold TaskTrait in a tuple (depending on implementation).
Fix: Expose a single constructor that checks if all traits are valid,
or if an extension can accept all traits.
Caveat: An extension has to redefine base TaskTrait::ValidTraits as valid
(through inheritance).
This CL also simplifies querying traits thanks to C++14 relaxed constant expressions.
This reduces compile time complexity of TaskTraits constructor from O(n^3) to O(n^2).


Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I73f993f6be837f1ac665b4b09c78d9d7272435a8
Reviewed-on: https://chromium-review.googlesource.com/c/1194687Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596341}
parent be4ea77f
...@@ -123,15 +123,24 @@ struct WithBaseSyncPrimitives {}; ...@@ -123,15 +123,24 @@ struct WithBaseSyncPrimitives {};
// Describes immutable metadata for a single task or a group of tasks. // Describes immutable metadata for a single task or a group of tasks.
class BASE_EXPORT TaskTraits { class BASE_EXPORT TaskTraits {
private: private:
using TaskPriorityFilter =
trait_helpers::EnumTraitFilter<TaskPriority, TaskPriority::USER_VISIBLE>;
using MayBlockFilter = trait_helpers::BooleanTraitFilter<MayBlock>;
using TaskShutdownBehaviorFilter =
trait_helpers::EnumTraitFilter<TaskShutdownBehavior,
TaskShutdownBehavior::SKIP_ON_SHUTDOWN>;
using WithBaseSyncPrimitivesFilter =
trait_helpers::BooleanTraitFilter<WithBaseSyncPrimitives>;
public:
// ValidTrait ensures TaskTraits' constructor only accepts appropriate types. // ValidTrait ensures TaskTraits' constructor only accepts appropriate types.
struct ValidTrait { struct ValidTrait {
ValidTrait(TaskPriority) {} ValidTrait(TaskPriority);
ValidTrait(TaskShutdownBehavior) {} ValidTrait(TaskShutdownBehavior);
ValidTrait(MayBlock) {} ValidTrait(MayBlock);
ValidTrait(WithBaseSyncPrimitives) {} ValidTrait(WithBaseSyncPrimitives);
}; };
public:
// Invoking this constructor without arguments produces TaskTraits that are // Invoking this constructor without arguments produces TaskTraits that are
// appropriate for tasks that // appropriate for tasks that
// (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()), // (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()),
...@@ -153,50 +162,27 @@ class BASE_EXPORT TaskTraits { ...@@ -153,50 +162,27 @@ class BASE_EXPORT TaskTraits {
// constexpr base::TaskTraits other_user_visible_may_block_traits = { // constexpr base::TaskTraits other_user_visible_may_block_traits = {
// base::MayBlock(), base::TaskPriority::USER_VISIBLE}; // base::MayBlock(), base::TaskPriority::USER_VISIBLE};
template <class... ArgTypes, template <class... ArgTypes,
class CheckArgumentsAreValidBaseTraits = std::enable_if_t< class CheckArgumentsAreValid = std::enable_if_t<
trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value ||
trait_helpers::AreValidTraitsForExtension<ArgTypes...>::value>>
constexpr TaskTraits(ArgTypes... args) constexpr TaskTraits(ArgTypes... args)
: priority_(trait_helpers::GetValueFromArgList( : extension_(trait_helpers::GetTaskTraitsExtension(
trait_helpers::EnumArgGetter<TaskPriority, trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>{},
TaskPriority::USER_VISIBLE>(),
args...)), args...)),
shutdown_behavior_(trait_helpers::GetValueFromArgList( priority_(
trait_helpers::EnumArgGetter< trait_helpers::GetTraitFromArgList<TaskPriorityFilter>(args...)),
TaskShutdownBehavior, shutdown_behavior_(
TaskShutdownBehavior::SKIP_ON_SHUTDOWN>(), trait_helpers::GetTraitFromArgList<TaskShutdownBehaviorFilter>(
args...)), args...)),
priority_set_explicitly_( priority_set_explicitly_(
trait_helpers::HasArgOfType<TaskPriority, ArgTypes...>::value), trait_helpers::TraitIsDefined<TaskPriorityFilter>(args...)),
shutdown_behavior_set_explicitly_( shutdown_behavior_set_explicitly_(
trait_helpers::HasArgOfType<TaskShutdownBehavior, trait_helpers::TraitIsDefined<TaskShutdownBehaviorFilter>(args...)),
ArgTypes...>::value), may_block_(trait_helpers::GetTraitFromArgList<MayBlockFilter>(args...)),
may_block_(trait_helpers::GetValueFromArgList( with_base_sync_primitives_(
trait_helpers::BooleanArgGetter<MayBlock>(), trait_helpers::GetTraitFromArgList<WithBaseSyncPrimitivesFilter>(
args...)),
with_base_sync_primitives_(trait_helpers::GetValueFromArgList(
trait_helpers::BooleanArgGetter<WithBaseSyncPrimitives>(),
args...)) {} args...)) {}
// Construct TaskTraits with extension traits. See task_traits_extension.h.
template <class... ArgTypes,
class AvoidConstructorRedeclaration = void,
class CheckArgsContainNonBaseTrait = std::enable_if_t<
!trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
constexpr TaskTraits(ArgTypes... args)
// Select those arguments that are valid base TaskTraits and forward them
// to the above constructor via a helper constructor.
: TaskTraits(std::forward_as_tuple(args...),
trait_helpers::SelectIndices<
trait_helpers::ValidTraitTester<ValidTrait>::IsValid,
ArgTypes...>{}) {
// Select all other arguments and try to create an extension with them.
extension_ = MakeTaskTraitsExtensionHelper(
std::forward_as_tuple(args...),
trait_helpers::SelectIndices<
trait_helpers::ValidTraitTester<ValidTrait>::IsInvalid,
ArgTypes...>{});
}
constexpr TaskTraits(const TaskTraits& other) = default; constexpr TaskTraits(const TaskTraits& other) = default;
TaskTraits& operator=(const TaskTraits& other) = default; TaskTraits& operator=(const TaskTraits& other) = default;
...@@ -279,16 +265,6 @@ class BASE_EXPORT TaskTraits { ...@@ -279,16 +265,6 @@ class BASE_EXPORT TaskTraits {
with_base_sync_primitives_(left.with_base_sync_primitives_ || with_base_sync_primitives_(left.with_base_sync_primitives_ ||
right.with_base_sync_primitives_) {} right.with_base_sync_primitives_) {}
// Helper constructor which selects those arguments from |args| that are
// indicated by the index_sequence and forwards them to the public
// constructor. Due to filtering, the indices may be non-contiguous.
template <class... ArgTypes, std::size_t... Indices>
constexpr TaskTraits(std::tuple<ArgTypes...> args,
std::index_sequence<Indices...>)
: TaskTraits(
std::get<Indices>(std::forward<std::tuple<ArgTypes...>>(args))...) {
}
// Ordered for packing. // Ordered for packing.
TaskTraitsExtensionStorage extension_; TaskTraitsExtensionStorage extension_;
TaskPriority priority_; TaskPriority priority_;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef BASE_TASK_TASK_TRAITS_DETAILS_H_ #ifndef BASE_TASK_TASK_TRAITS_DETAILS_H_
#define BASE_TASK_TASK_TRAITS_DETAILS_H_ #define BASE_TASK_TASK_TRAITS_DETAILS_H_
#include <initializer_list>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
...@@ -12,116 +13,147 @@ ...@@ -12,116 +13,147 @@
namespace base { namespace base {
namespace trait_helpers { namespace trait_helpers {
// HasArgOfType<CheckedType, ArgTypes...>::value is true iff a type in ArgTypes // Checks if any of the elements in |ilist| is true.
// matches CheckedType. // Similar to std::any_of for the case of constexpr initializer_list.
template <class...> inline constexpr bool any_of(std::initializer_list<bool> ilist) {
struct HasArgOfType : std::false_type {}; for (auto c : ilist) {
template <class CheckedType, class FirstArgType, class... ArgTypes> if (c)
struct HasArgOfType<CheckedType, FirstArgType, ArgTypes...> return true;
: std::conditional<std::is_same<CheckedType, FirstArgType>::value, }
std::true_type, return false;
HasArgOfType<CheckedType, ArgTypes...>>::type {}; }
// When the following call is made: // Checks if all of the elements in |ilist| are true.
// GetValueFromArgListImpl(CallFirstTag(), GetterType(), args...); // Similar to std::any_of for the case of constexpr initializer_list.
// If |args| is empty, the compiler selects the first overload. This overload inline constexpr bool all_of(std::initializer_list<bool> ilist) {
// returns getter.GetDefaultValue(). If |args| is not empty, the compiler for (auto c : ilist) {
// prefers using the second overload because the type of the first argument if (!c)
// matches exactly. This overload returns getter.GetValueFromArg(first_arg), return false;
// where |first_arg| is the first element in |args|. If }
// getter.GetValueFromArg(first_arg) isn't defined, the compiler uses the third return true;
// overload instead. This overload discards the first argument in |args| and }
// makes a recursive call to GetValueFromArgListImpl() with CallFirstTag() as
// first argument. // Counts the elements in |ilist| that are equal to |value|.
// Similar to std::count for the case of constexpr initializer_list.
// Tag dispatching. template <class T>
inline constexpr size_t count(std::initializer_list<T> ilist, T value) {
size_t c = 0;
for (const auto& v : ilist) {
c += (v == value);
}
return c;
}
// CallFirstTag is an argument tag that helps to avoid ambiguous overloaded
// functions. When the following call is made:
// func(CallFirstTag(), arg...);
// the compiler will give precedence to an overload candidate that directly
// takes CallFirstTag. Another overload that takes CallSecondTag will be
// considered iff the preferred overload candidates were all invalids and
// therefore discarded.
struct CallSecondTag {}; struct CallSecondTag {};
struct CallFirstTag : CallSecondTag {}; struct CallFirstTag : CallSecondTag {};
// Overload 1: Default value. // A trait filter class |TraitFilterType| implements the protocol to get a value
template <class GetterType> // of type |ArgType| from an argument list and convert it to a value of type
constexpr typename GetterType::ValueType GetValueFromArgListImpl( // |TraitType|. If the argument list contains an argument of type |ArgType|, the
CallFirstTag, // filter class will be instantiated with that argument. If the argument list
GetterType getter) { // contains no argument of type |ArgType|, the filter class will be instantiated
return getter.GetDefaultValue(); // using the default constructor if available; a compile error is issued
// otherwise. The filter class must have the conversion operator TraitType()
// which returns a value of type TraitType.
// |InvalidTrait| is used to return from GetTraitFromArg when the argument is
// not compatible with the desired trait.
struct InvalidTrait {};
// Returns an object of type |TraitFilterType| constructed from |arg| if
// compatible, or |InvalidTrait| otherwise.
template <class TraitFilterType,
class ArgType,
class CheckArgumentIsCompatible = std::enable_if_t<
std::is_constructible<TraitFilterType, ArgType>::value>>
constexpr TraitFilterType GetTraitFromArg(CallFirstTag, ArgType arg) {
return TraitFilterType(arg);
} }
// Overload 2: Get value from first argument. Check that no argument in |args| template <class TraitFilterType, class ArgType>
// has the same type as |first_arg|. constexpr InvalidTrait GetTraitFromArg(CallSecondTag, ArgType arg) {
template <class GetterType, return InvalidTrait();
class FirstArgType, }
// Returns an object of type |TraitFilterType| constructed from a compatible
// argument in |args...|, or default constructed if none of the arguments are
// compatible. This is the implementation of GetTraitFromArgList() with a
// disambiguation tag.
template <class TraitFilterType,
class... ArgTypes, class... ArgTypes,
class TestGetValueFromArgDefined = class TestCompatibleArgument = std::enable_if_t<any_of(
decltype(std::declval<GetterType>().GetValueFromArg( {std::is_constructible<TraitFilterType, ArgTypes>::value...})>>
std::declval<FirstArgType>()))> constexpr TraitFilterType GetTraitFromArgListImpl(CallFirstTag,
constexpr typename GetterType::ValueType GetValueFromArgListImpl( ArgTypes... args) {
CallFirstTag, return std::get<TraitFilterType>(std::make_tuple(
GetterType getter, GetTraitFromArg<TraitFilterType>(CallFirstTag(), args)...));
const FirstArgType& first_arg, }
const ArgTypes&... args) {
static_assert(!HasArgOfType<FirstArgType, ArgTypes...>::value, template <class TraitFilterType, class... ArgTypes>
constexpr TraitFilterType GetTraitFromArgListImpl(CallSecondTag,
ArgTypes... args) {
static_assert(std::is_constructible<TraitFilterType>::value,
"TaskTraits contains a Trait that must be explicity "
"initialized in its constructor.");
return TraitFilterType();
}
// Constructs an object of type |TraitFilterType| from a compatible argument in
// |args...|, or using the default constructor, and returns its associated trait
// value using conversion to |TraitFilterType::ValueType|. If there are more
// than one compatible argument in |args|, generates a compile-time error.
template <class TraitFilterType, class... ArgTypes>
constexpr typename TraitFilterType::ValueType GetTraitFromArgList(
ArgTypes... args) {
static_assert(
count({std::is_constructible<TraitFilterType, ArgTypes>::value...},
true) <= 1,
"Multiple arguments of the same type were provided to the " "Multiple arguments of the same type were provided to the "
"constructor of TaskTraits."); "constructor of TaskTraits.");
return getter.GetValueFromArg(first_arg); return GetTraitFromArgListImpl<TraitFilterType>(CallFirstTag(), args...);
} }
// Overload 3: Discard first argument. // Returns true if this trait is explicitly defined in an argument list, i.e.
template <class GetterType, class FirstArgType, class... ArgTypes> // there is an argument compatible with this trait in |args...|.
constexpr typename GetterType::ValueType GetValueFromArgListImpl( template <class TraitFilterType, class... ArgTypes>
CallSecondTag, constexpr bool TraitIsDefined(ArgTypes... args) {
GetterType getter, return any_of({std::is_constructible<TraitFilterType, ArgTypes>::value...});
const FirstArgType&,
const ArgTypes&... args) {
return GetValueFromArgListImpl(CallFirstTag(), getter, args...);
} }
// If there is an argument |arg_of_type| of type Getter::ArgType in |args|, // Helper class to implemnent a |TraitFilterType|.
// returns getter.GetValueFromArg(arg_of_type). If there are more than one template <typename T>
// argument of type Getter::ArgType in |args|, generates a compile-time error. struct BasicTraitFilter {
// Otherwise, returns getter.GetDefaultValue(). using ValueType = T;
//
// |getter| must provide: constexpr operator ValueType() const { return value; }
// ValueType:
// The return type of GetValueFromArgListImpl(). ValueType value = {};
// };
// ArgType:
// The type of the argument from which GetValueFromArgListImpl() derives
// its return value.
//
// ValueType GetValueFromArg(ArgType):
// Converts an argument of type ArgType into a value returned by
// GetValueFromArgListImpl().
//
// |getter| may provide:
// ValueType GetDefaultValue():
// Returns the value returned by GetValueFromArgListImpl() if none of
// its arguments is of type ArgType. If this method is not provided,
// compilation will fail when no argument of type ArgType is provided.
template <class GetterType, class... ArgTypes>
constexpr typename GetterType::ValueType GetValueFromArgList(
GetterType getter,
const ArgTypes&... args) {
return GetValueFromArgListImpl(CallFirstTag(), getter, args...);
}
template <typename ArgType> template <typename ArgType>
struct BooleanArgGetter { struct BooleanTraitFilter : public BasicTraitFilter<bool> {
using ValueType = bool; constexpr BooleanTraitFilter() { this->value = false; }
constexpr ValueType GetValueFromArg(ArgType) const { return true; } constexpr BooleanTraitFilter(ArgType) { this->value = true; }
constexpr ValueType GetDefaultValue() const { return false; }
}; };
template <typename ArgType, ArgType DefaultValue> template <typename ArgType, ArgType DefaultValue>
struct EnumArgGetter { struct EnumTraitFilter : public BasicTraitFilter<ArgType> {
using ValueType = ArgType; constexpr EnumTraitFilter() { this->value = DefaultValue; }
constexpr ValueType GetValueFromArg(ArgType arg) const { return arg; } constexpr EnumTraitFilter(ArgType arg) { this->value = arg; }
constexpr ValueType GetDefaultValue() const { return DefaultValue; }
}; };
// Tests whether multiple given argtument types are all valid traits according
// to the provided ValidTraits. To use, define a ValidTraits
template <typename ArgType> template <typename ArgType>
struct RequiredEnumArgGetter { struct RequiredEnumTraitFilter : public BasicTraitFilter<ArgType> {
using ValueType = ArgType; constexpr RequiredEnumTraitFilter(ArgType arg) { this->value = arg; }
constexpr ValueType GetValueFromArg(ArgType arg) const { return arg; }
}; };
// Tests whether a given trait type is valid or invalid by testing whether it is // Tests whether a given trait type is valid or invalid by testing whether it is
...@@ -129,82 +161,14 @@ struct RequiredEnumArgGetter { ...@@ -129,82 +161,14 @@ struct RequiredEnumArgGetter {
// type like this: // type like this:
// //
// struct ValidTraits { // struct ValidTraits {
// ValidTraits(MyTrait) {} // ValidTraits(MyTrait);
// ... // ...
// }; // };
template <class ValidTraits> template <class ValidTraits, class... ArgTypes>
struct ValidTraitTester { struct AreValidTraits
template <class TraitType> : std::integral_constant<
struct IsValid : std::is_convertible<TraitType, ValidTraits> {}; bool,
all_of({std::is_convertible<ArgTypes, ValidTraits>::value...})> {};
template <class TraitType>
struct IsInvalid
: std::conditional_t<std::is_convertible<TraitType, ValidTraits>::value,
std::false_type,
std::true_type> {};
};
// Tests if a given trait type is valid according to the provided ValidTraits.
template <class ValidTraits, class TraitType>
struct IsValidTrait
: ValidTraitTester<ValidTraits>::template IsValid<TraitType> {};
// Tests whether multiple given traits types are all valid according to the
// provided ValidTraits.
template <class ValidTraits, class...>
struct AreValidTraits : std::true_type {};
template <class ValidTraits, class NextType, class... Rest>
struct AreValidTraits<ValidTraits, NextType, Rest...>
: std::conditional<IsValidTrait<ValidTraits, NextType>::value,
AreValidTraits<ValidTraits, Rest...>,
std::false_type>::type {};
// Helper struct that recursively builds up an index_sequence containing all
// those indexes of elements in Args for which the |Predicate<Arg>::value| is
// true.
template <template <class> class Predicate,
std::size_t CurrentIndex,
class Output,
class... Args>
struct SelectIndicesHelper;
template <template <class> class Predicate,
std::size_t CurrentIndex,
std::size_t... Indices,
class First,
class... Rest>
struct SelectIndicesHelper<Predicate,
CurrentIndex,
std::index_sequence<Indices...>,
First,
Rest...>
: std::conditional_t<
Predicate<First>::value,
// Push the index into the sequence and recurse.
SelectIndicesHelper<Predicate,
CurrentIndex + 1,
std::index_sequence<Indices..., CurrentIndex>,
Rest...>,
// Skip the index and recurse.
SelectIndicesHelper<Predicate,
CurrentIndex + 1,
std::index_sequence<Indices...>,
Rest...>> {};
template <template <class> class Predicate,
std::size_t CurrentIndex,
class Sequence>
struct SelectIndicesHelper<Predicate, CurrentIndex, Sequence> {
using type = Sequence;
};
// Selects the indices of elements in the |Args| list for which
// |Predicate<Arg>::value| is |true|.
template <template <class> class Predicate, class... Args>
using SelectIndices =
typename SelectIndicesHelper<Predicate, 0, std::index_sequence<>, Args...>::
type;
} // namespace trait_helpers } // namespace trait_helpers
} // namespace base } // namespace base
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <utility> #include <utility>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/task/task_traits_details.h"
namespace base { namespace base {
...@@ -41,10 +42,14 @@ namespace base { ...@@ -41,10 +42,14 @@ namespace base {
// as its extension traits: // as its extension traits:
// (3) -- template <...> // (3) -- template <...>
// -- constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension( // -- constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension(
// -- ArgTypes&&... args). // -- ArgTypes... args).
// Constructs and serializes an extension with the given arguments into // Constructs and serializes an extension with the given arguments into
// a TaskTraitsExtensionStorage and returns it. Should only accept valid // a TaskTraitsExtensionStorage and returns it. When the extension is used,
// arguments for the extension. // all traits, including the base ones, are passed to this function in
// order make sure TaskTraits constructor only participates in overload
// resolution if all traits are valid. As such, this function should only
// accept valid task traits recognised by the extension and the base task
// traits.
// //
// EXAMPLE (see also base/task/test_task_traits_extension.h): // EXAMPLE (see also base/task/test_task_traits_extension.h):
// -------- // --------
...@@ -57,19 +62,23 @@ namespace base { ...@@ -57,19 +62,23 @@ namespace base {
// static constexpr uint8_t kExtensionId = // static constexpr uint8_t kExtensionId =
// TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; // TaskTraitsExtensionStorage::kFirstEmbedderExtensionId;
// //
// struct ValidTrait { // struct ValidTrait : public TaskTraits::ValidTrait {
// ValidTrait(MyExtensionTrait) {} // // Accept base traits in MakeTaskTraitsExtension (see above).
// using TaskTraits::ValidTrait::ValidTrait;
//
// ValidTrait(MyExtensionTrait);
// }; // };
// //
// using MyExtensionTraitFilter =
// trait_helpers::EnumTraitFilter<MyExtensionTrait, MyExtensionTrait::kA>;
//
// // Constructor that accepts only valid traits as specified by ValidTraits. // // Constructor that accepts only valid traits as specified by ValidTraits.
// template <class... ArgTypes, // template <class... ArgTypes,
// class CheckArgumentsAreValid = std::enable_if_t< // class CheckArgumentsAreValid = std::enable_if_t<
// base::trait_helpers::AreValidTraits< // base::trait_helpers::AreValidTraits<
// ValidTrait, ArgTypes...>::value>> // ValidTrait, ArgTypes...>::value>>
// constexpr MyTaskTraitsExtension(ArgTypes... args) // constexpr MyTaskTraitsExtension(ArgTypes... args)
// : my_trait_(base::trait_helpers::GetValueFromArgList( // : my_trait_(trait_helpers::GetTraitFromArgList<MyExtensionTraitFilter>(
// base::trait_helpers::EnumArgGetter<MyExtensionTrait,
// MyExtensionTrait::kValue1>(),
// args...)) {} // args...)) {}
// //
// // Serializes MyTaskTraitsExtension into a storage object and returns it. // // Serializes MyTaskTraitsExtension into a storage object and returns it.
...@@ -100,8 +109,8 @@ namespace base { ...@@ -100,8 +109,8 @@ namespace base {
// base::trait_helpers::AreValidTraits< // base::trait_helpers::AreValidTraits<
// MyTaskTraitsExtension::ValidTrait, ArgTypes...>::value>> // MyTaskTraitsExtension::ValidTrait, ArgTypes...>::value>>
// constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension( // constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension(
// ArgTypes&&... args) { // ArgTypes... args) {
// return MyTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize(); // return MyTaskTraitsExtension(args...).Serialize();
// } // }
// } // namespace my_embedder // } // namespace my_embedder
// //
...@@ -171,6 +180,44 @@ inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage( ...@@ -171,6 +180,44 @@ inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage(
inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage( inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage(
const TaskTraitsExtensionStorage& other) = default; const TaskTraitsExtensionStorage& other) = default;
namespace trait_helpers {
// Helper class whose constructor tests if an extension accepts a list of
// argument types.
struct TaskTraitsExtension {
template <class... ArgTypes,
class CheckCanMakeExtension =
decltype(MakeTaskTraitsExtension(std::declval<ArgTypes>()...))>
constexpr TaskTraitsExtension(ArgTypes... args) {}
};
// Tests that that a trait extension accepts all |ArgsTypes...|.
template <class... ArgTypes>
struct AreValidTraitsForExtension
: std::integral_constant<
bool,
std::is_constructible<TaskTraitsExtension, ArgTypes...>::value> {};
// Helper function that returns the TaskTraitsExtensionStorage of a
// serialized extension created with |args...| if there are arguments that are
// not valid base traits, or a default constructed TaskTraitsExtensionStorage
// otherwise.
template <class... ArgTypes>
constexpr TaskTraitsExtensionStorage GetTaskTraitsExtension(
std::true_type base_traits,
ArgTypes... args) {
return TaskTraitsExtensionStorage();
}
template <class... ArgTypes>
constexpr TaskTraitsExtensionStorage GetTaskTraitsExtension(
std::false_type base_traits,
ArgTypes... args) {
return MakeTaskTraitsExtension(args...);
}
} // namespace trait_helpers
// TODO(eseckler): Default the comparison operator once C++20 arrives. // TODO(eseckler): Default the comparison operator once C++20 arrives.
inline bool TaskTraitsExtensionStorage::operator==( inline bool TaskTraitsExtensionStorage::operator==(
const TaskTraitsExtensionStorage& other) const { const TaskTraitsExtensionStorage& other) const {
...@@ -180,25 +227,6 @@ inline bool TaskTraitsExtensionStorage::operator==( ...@@ -180,25 +227,6 @@ inline bool TaskTraitsExtensionStorage::operator==(
return extension_id == other.extension_id && data == other.data; return extension_id == other.extension_id && data == other.data;
} }
// Default implementation of MakeTaskTraitsExtension template function, which
// doesn't accept any traits and does not create an extension.
template <class Unused = void>
constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension(
TaskTraitsExtensionStorage& storage) {
return TaskTraitsExtensionStorage();
}
// Forwards those arguments from |args| that are indicated by the index_sequence
// to a MakeTaskTraitsExtension() template function, which is provided by the
// embedder in an unknown namespace; its resolution relies on argument-dependent
// lookup. Due to filtering, the provided indices may be non-contiguous.
template <class... ArgTypes, std::size_t... Indices>
constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtensionHelper(
std::tuple<ArgTypes...> args,
std::index_sequence<Indices...>) {
return MakeTaskTraitsExtension(
std::get<Indices>(std::forward<std::tuple<ArgTypes...>>(args))...);
}
} // namespace base } // namespace base
......
...@@ -9,6 +9,13 @@ ...@@ -9,6 +9,13 @@
namespace base { namespace base {
TEST(TaskTraitsExtensionTest, NoExtension) {
constexpr TaskTraits traits = {};
EXPECT_EQ(traits.extension_id(),
TaskTraitsExtensionStorage::kInvalidExtensionId);
}
TEST(TaskTraitsExtensionTest, CreateWithOneExtensionTrait) { TEST(TaskTraitsExtensionTest, CreateWithOneExtensionTrait) {
constexpr TaskTraits traits = {TestExtensionEnumTrait::kB}; constexpr TaskTraits traits = {TestExtensionEnumTrait::kB};
......
...@@ -15,7 +15,7 @@ namespace base { ...@@ -15,7 +15,7 @@ namespace base {
constexpr TaskTraits traits = {MayBlock(), MayBlock()}; constexpr TaskTraits traits = {MayBlock(), MayBlock()};
#elif defined(NCTEST_TASK_TRAITS_EXTENSION_MULTIPLE_EXTENSION_TRAITS) // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."] #elif defined(NCTEST_TASK_TRAITS_EXTENSION_MULTIPLE_EXTENSION_TRAITS) // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."]
constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, TestExtensionEnumTrait::kC}; constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, TestExtensionEnumTrait::kC};
#elif defined(NCTEST_TASK_TRAITS_EXTENSION_INVALID_TYPE) // [r"no matching function for call to 'MakeTaskTraitsExtension'"] #elif defined(NCTEST_TASK_TRAITS_EXTENSION_INVALID_TYPE) // [r"no matching constructor for initialization of 'const base::TaskTraits'"]
constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, 123}; constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, 123};
#elif defined(NCTEST_TASK_TRAITS_EXTENSION_TOO_MUCH_DATA_FOR_STORAGE) // [r"no matching constructor for initialization of 'base::TaskTraitsExtensionStorage'"] #elif defined(NCTEST_TASK_TRAITS_EXTENSION_TOO_MUCH_DATA_FOR_STORAGE) // [r"no matching constructor for initialization of 'base::TaskTraitsExtensionStorage'"]
constexpr TaskTraitsExtensionStorage TestSerializeTaskTraitsWithTooMuchData() { constexpr TaskTraitsExtensionStorage TestSerializeTaskTraitsWithTooMuchData() {
......
...@@ -24,7 +24,7 @@ constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, ...@@ -24,7 +24,7 @@ constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN,
constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN,
MayBlock(), MayBlock(),
TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
#elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE) // [r"no matching function for call to 'MakeTaskTraitsExtension'"] #elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE) // [r"no matching constructor for initialization of 'const base::TaskTraits'"]
constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true}; constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true};
#endif #endif
......
...@@ -20,21 +20,26 @@ class TestTaskTraitsExtension { ...@@ -20,21 +20,26 @@ class TestTaskTraitsExtension {
static constexpr uint8_t kExtensionId = static constexpr uint8_t kExtensionId =
TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; TaskTraitsExtensionStorage::kFirstEmbedderExtensionId;
struct ValidTrait { struct ValidTrait : public TaskTraits::ValidTrait {
ValidTrait(TestExtensionEnumTrait) {} using TaskTraits::ValidTrait::ValidTrait;
ValidTrait(TestExtensionBoolTrait) {}
ValidTrait(TestExtensionEnumTrait);
ValidTrait(TestExtensionBoolTrait);
}; };
using TestExtensionEnumFilter =
trait_helpers::EnumTraitFilter<TestExtensionEnumTrait,
TestExtensionEnumTrait::kA>;
using TestExtensionBoolFilter =
trait_helpers::BooleanTraitFilter<TestExtensionBoolTrait>;
template <class... ArgTypes, template <class... ArgTypes,
class CheckArgumentsAreValid = std::enable_if_t< class CheckArgumentsAreValid = std::enable_if_t<
trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
constexpr TestTaskTraitsExtension(ArgTypes... args) constexpr TestTaskTraitsExtension(ArgTypes... args)
: enum_trait_(trait_helpers::GetValueFromArgList( : enum_trait_(trait_helpers::GetTraitFromArgList<TestExtensionEnumFilter>(
trait_helpers::EnumArgGetter<TestExtensionEnumTrait,
TestExtensionEnumTrait::kA>(),
args...)), args...)),
bool_trait_(trait_helpers::GetValueFromArgList( bool_trait_(trait_helpers::GetTraitFromArgList<TestExtensionBoolFilter>(
trait_helpers::BooleanArgGetter<TestExtensionBoolTrait>(),
args...)) {} args...)) {}
constexpr TaskTraitsExtensionStorage Serialize() const { constexpr TaskTraitsExtensionStorage Serialize() const {
...@@ -65,8 +70,7 @@ template <class... ArgTypes, ...@@ -65,8 +70,7 @@ template <class... ArgTypes,
class = std::enable_if_t< class = std::enable_if_t<
trait_helpers::AreValidTraits<TestTaskTraitsExtension::ValidTrait, trait_helpers::AreValidTraits<TestTaskTraitsExtension::ValidTrait,
ArgTypes...>::value>> ArgTypes...>::value>>
constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension( constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension(ArgTypes... args) {
ArgTypes&&... args) {
return TestTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize(); return TestTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize();
} }
......
...@@ -41,13 +41,20 @@ struct NonNestable {}; ...@@ -41,13 +41,20 @@ struct NonNestable {};
// Posting to a BrowserThread must only be done after it was initialized (ref. // Posting to a BrowserThread must only be done after it was initialized (ref.
// BrowserMainLoop::CreateThreads() phase). // BrowserMainLoop::CreateThreads() phase).
class CONTENT_EXPORT BrowserTaskTraitsExtension { class CONTENT_EXPORT BrowserTaskTraitsExtension {
using BrowserThreadIDFilter =
base::trait_helpers::RequiredEnumTraitFilter<BrowserThread::ID>;
using NonNestableFilter =
base::trait_helpers::BooleanTraitFilter<NonNestable>;
public: public:
static constexpr uint8_t kExtensionId = static constexpr uint8_t kExtensionId =
base::TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; base::TaskTraitsExtensionStorage::kFirstEmbedderExtensionId;
struct ValidTrait { struct ValidTrait : public base::TaskTraits::ValidTrait {
ValidTrait(BrowserThread::ID) {} using base::TaskTraits::ValidTrait::ValidTrait;
ValidTrait(NonNestable) {}
ValidTrait(BrowserThread::ID);
ValidTrait(NonNestable);
}; };
template < template <
...@@ -55,11 +62,10 @@ class CONTENT_EXPORT BrowserTaskTraitsExtension { ...@@ -55,11 +62,10 @@ class CONTENT_EXPORT BrowserTaskTraitsExtension {
class CheckArgumentsAreValid = std::enable_if_t< class CheckArgumentsAreValid = std::enable_if_t<
base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
constexpr BrowserTaskTraitsExtension(ArgTypes... args) constexpr BrowserTaskTraitsExtension(ArgTypes... args)
: browser_thread_(base::trait_helpers::GetValueFromArgList( : browser_thread_(
base::trait_helpers::RequiredEnumArgGetter<BrowserThread::ID>(), base::trait_helpers::GetTraitFromArgList<BrowserThreadIDFilter>(
args...)), args...)),
nestable_(!base::trait_helpers::GetValueFromArgList( nestable_(!base::trait_helpers::GetTraitFromArgList<NonNestableFilter>(
base::trait_helpers::BooleanArgGetter<NonNestable>(),
args...)) {} args...)) {}
constexpr base::TaskTraitsExtensionStorage Serialize() const { constexpr base::TaskTraitsExtensionStorage Serialize() const {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
namespace content { namespace content {
#if defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD) // [r"no member named 'GetDefaultValue' in 'base::trait_helpers::RequiredEnumArgGetter<content::BrowserThread::ID>'"] #if defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD) // [r"TaskTraits contains a Trait that must be explicity initialized in its constructor."]
constexpr base::TaskTraits traits = {NonNestable()}; constexpr base::TaskTraits traits = {NonNestable()};
#elif defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_THREADS) // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."] #elif defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_THREADS) // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."]
constexpr base::TaskTraits traits = {BrowserThread::UI, constexpr base::TaskTraits traits = {BrowserThread::UI,
......
...@@ -40,13 +40,20 @@ struct NonNestable {}; ...@@ -40,13 +40,20 @@ struct NonNestable {};
// Posting to a WebThread must only be done after it was initialized (ref. // Posting to a WebThread must only be done after it was initialized (ref.
// WebMainLoop::CreateThreads() phase). // WebMainLoop::CreateThreads() phase).
class WebTaskTraitsExtension { class WebTaskTraitsExtension {
using WebThreadIDFilter =
base::trait_helpers::RequiredEnumTraitFilter<WebThread::ID>;
using NonNestableFilter =
base::trait_helpers::BooleanTraitFilter<NonNestable>;
public: public:
static constexpr uint8_t kExtensionId = static constexpr uint8_t kExtensionId =
base::TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; base::TaskTraitsExtensionStorage::kFirstEmbedderExtensionId;
struct ValidTrait { struct ValidTrait : public base::TaskTraits::ValidTrait {
ValidTrait(WebThread::ID) {} using base::TaskTraits::ValidTrait::ValidTrait;
ValidTrait(NonNestable) {}
ValidTrait(WebThread::ID);
ValidTrait(NonNestable);
}; };
template < template <
...@@ -54,11 +61,9 @@ class WebTaskTraitsExtension { ...@@ -54,11 +61,9 @@ class WebTaskTraitsExtension {
class CheckArgumentsAreValid = std::enable_if_t< class CheckArgumentsAreValid = std::enable_if_t<
base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
constexpr WebTaskTraitsExtension(ArgTypes... args) constexpr WebTaskTraitsExtension(ArgTypes... args)
: web_thread_(base::trait_helpers::GetValueFromArgList( : web_thread_(base::trait_helpers::GetTraitFromArgList<WebThreadIDFilter>(
base::trait_helpers::RequiredEnumArgGetter<WebThread::ID>(),
args...)), args...)),
nestable_(!base::trait_helpers::GetValueFromArgList( nestable_(!base::trait_helpers::GetTraitFromArgList<NonNestableFilter>(
base::trait_helpers::BooleanArgGetter<NonNestable>(),
args...)) {} args...)) {}
constexpr base::TaskTraitsExtensionStorage Serialize() const { constexpr base::TaskTraitsExtensionStorage Serialize() const {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment