 Chromium Code Reviews
 Chromium Code Reviews Issue 2829083002:
  Add constexpr TaskTraits constructor.  (Closed)
    
  
    Issue 2829083002:
  Add constexpr TaskTraits constructor.  (Closed) 
  | Index: base/task_scheduler/task_traits.h | 
| diff --git a/base/task_scheduler/task_traits.h b/base/task_scheduler/task_traits.h | 
| index 47e9b81eeecd46877ffb4f71a42d173b04aa35b8..5323bc281910f570118689148f47ae6967129ab2 100644 | 
| --- a/base/task_scheduler/task_traits.h | 
| +++ b/base/task_scheduler/task_traits.h | 
| @@ -8,6 +8,8 @@ | 
| #include <stdint.h> | 
| #include <iosfwd> | 
| +#include <type_traits> | 
| +#include <utility> | 
| #include "base/base_export.h" | 
| #include "build/build_config.h" | 
| @@ -77,72 +79,180 @@ enum class TaskShutdownBehavior { | 
| BLOCK_SHUTDOWN, | 
| }; | 
| +// Tasks with this trait may block. This includes but is not limited to tasks | 
| +// that wait on synchronous file I/O operations: read or write a file from disk, | 
| +// interact with a pipe or a socket, rename or delete a file, enumerate files in | 
| +// a directory, etc. This trait isn't required for the mere use of locks. For | 
| +// tasks that block on base/ synchronization primitives, see | 
| +// WithBaseSyncPrimitives(). | 
| 
robliao
2017/04/21 18:02:06
WithBaseSyncPrimitives() -> the WithBaseSyncPrimit
 
fdoray
2017/04/21 20:35:20
Done.
 | 
| +struct MayBlock {}; | 
| + | 
| +// Tasks with this trait will pass base::AssertWaitAllowed(), i.e. will be | 
| +// allowed on the following methods : | 
| +// - base::WaitableEvent::Wait | 
| +// - base::ConditionVariable::Wait | 
| +// - base::PlatformThread::Join | 
| +// - base::PlatformThread::Sleep | 
| +// - base::Process::WaitForExit | 
| +// - base::Process::WaitForExitWithTimeout | 
| +// | 
| +// Tasks should generally not use these methods. | 
| +// | 
| +// Instead of waiting on a WaitableEvent or a ConditionVariable, put the work | 
| +// that should happen after the wait in a callback and post that callback from | 
| +// where the WaitableEvent or ConditionVariable would have been signaled. If | 
| +// something needs to be scheduled after many tasks have executed, use | 
| +// base::BarrierClosure. | 
| +// | 
| +// Avoid creating threads. Instead, use | 
| 
robliao
2017/04/21 18:02:06
This guidance should probably be placed somewhere
 
fdoray
2017/04/21 20:35:20
Done. Writing a CL that adds it to base::Thread/ba
 
fdoray
2017/04/21 20:43:18
https://codereview.chromium.org/2834783004/
 | 
| +// base::Create(Sequenced|SingleTreaded)TaskRunnerWithTraits(). If a thread is | 
| +// really needed, make it non-joinable and add cleanup work at the end of the | 
| +// thread's main function (if using base::Thread, override Cleanup()). | 
| +// | 
| +// On Windows, join processes asynchronously using base::win::ObjectWatcher. | 
| +// | 
| +// MayBlock() must be specified in conjunction with this trait if and only if | 
| +// removing usage of methods listed above in the labeled tasks would still | 
| +// result in tasks that may block (per MayBlock()'s definition). | 
| +// | 
| +// In doubt, consult with //base/task_scheduler/OWNERS. | 
| +struct WithBaseSyncPrimitives {}; | 
| + | 
| +namespace internal { | 
| 
robliao
2017/04/21 18:02:06
Place this is task_traits_details.h or task_traits
 
robliao
2017/04/21 18:03:13
Wow, that didn't come out right at all.
Place this
 
fdoray
2017/04/21 20:35:19
Done.
 | 
| + | 
| +// CheckNoArgOfType() generates a compile-time error if it receives an argument | 
| +// of type UnwantedType. The 2nd overload checks the type of the first argument | 
| 
robliao
2017/04/21 18:02:06
Nit: 2nd -> second and 1st -> first
 
fdoray
2017/04/21 20:35:19
Done.
 | 
| +// and makes a recursive call with the remaining arguments. The 1st overload | 
| +// takes no argument and is used to end the recursion. Both overloads return a | 
| +// bool because it is invalid for a constexpr function to return void. | 
| +template <class UnwantedType> | 
| +constexpr bool CheckNoArgOfType() { | 
| + return true; | 
| +} | 
| + | 
| +template <class UnwantedType, class FirstArgType, class... ArgTypes> | 
| +constexpr bool CheckNoArgOfType(const FirstArgType&, const ArgTypes&... args) { | 
| + static_assert(!std::is_same<UnwantedType, FirstArgType>::value, | 
| + "Multiple arguments of the same type were provided to the " | 
| + "constructor of TaskTraits."); | 
| + return CheckNoArgOfType<UnwantedType>(args...); | 
| +} | 
| + | 
| +// When the following call is made: | 
| +// GetValueFromArgList(CallFirstTag(), GetterType(), args...); | 
| +// The compiler prefers using the 3rd overload of GetValueFromArgList(), because | 
| +// the type of the first argument matches exactly. That overload uses | 
| +// Getter::operator()(FirstArgType) to extract a value from |first_arg|. If | 
| +// Getter::operator()(FirstArgType) isn't defined, the compiler uses the 1st or | 
| +// the 2nd overload instead (depending on whether |args| is empty). The 1st | 
| +// overload returns a default value obtained from Getter::operator()(). The 2nd | 
| +// overload discards |first_arg| and calls back GetValueFromArgList() with | 
| +// CallFirstTag() as first argument. | 
| +struct CallSecondTag {}; | 
| +struct CallFirstTag : CallSecondTag {}; | 
| + | 
| +// Overload 1: Default value. | 
| +template <class GetterType> | 
| +constexpr auto GetValueFromArgList(CallSecondTag, GetterType getter) | 
| + -> decltype(getter()) { | 
| + return getter(); | 
| +} | 
| + | 
| +// Overload 2: Discard first argument. | 
| +template <class GetterType, class FirstArgType, class... ArgTypes> | 
| +constexpr auto GetValueFromArgList(CallSecondTag, | 
| + GetterType getter, | 
| + const FirstArgType&, | 
| + const ArgTypes&... args) | 
| + -> decltype(GetValueFromArgList(CallFirstTag(), getter, args...)) { | 
| + return GetValueFromArgList(CallFirstTag(), getter, args...); | 
| +} | 
| + | 
| +// Overload 3: Get value from first argument. Check that no argument in |args| | 
| +// has the same type as |first_arg|. | 
| +template <class GetterType, | 
| + class FirstArgType, | 
| + class... ArgTypes, | 
| + class Instantiation = decltype( | 
| + std::declval<GetterType>()(std::declval<FirstArgType>()))> | 
| +constexpr typename GetterType::ValueType GetValueFromArgList( | 
| + CallFirstTag, | 
| + GetterType getter, | 
| + const FirstArgType& first_arg, | 
| + const ArgTypes&... args) { | 
| + // CheckNoArgOfType() is invoked as part of a comma operator statement to | 
| + // avoid an unused return value warning. | 
| + return CheckNoArgOfType<FirstArgType>(args...), getter(first_arg); | 
| +} | 
| + | 
| +template <typename ArgType> | 
| +struct BooleanArgGetter { | 
| + using ValueType = bool; | 
| + constexpr ValueType operator()(ArgType) const { return true; } | 
| + constexpr ValueType operator()() const { return false; } | 
| +}; | 
| + | 
| +template <typename ArgType, ArgType DefaultValue> | 
| +struct EnumArgGetter { | 
| + using ValueType = ArgType; | 
| + constexpr ValueType operator()(ArgType arg) const { return arg; } | 
| + constexpr ValueType operator()() const { return DefaultValue; } | 
| +}; | 
| + | 
| +} // namespace internal | 
| + | 
| // Describes metadata for a single task or a group of tasks. | 
| class BASE_EXPORT TaskTraits { | 
| public: | 
| - // Constructs a default TaskTraits for tasks that | 
| + // Invoking this constructor without arguments produces TaskTraits that are | 
| + // appropriate for tasks that | 
| // (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()), | 
| // (2) prefer inheriting the current priority to specifying their own, and | 
| // (3) can either block shutdown or be skipped on shutdown | 
| // (TaskScheduler implementation is free to choose a fitting default). | 
| - // Tasks that require stricter guarantees and/or know the specific | 
| - // TaskPriority appropriate for them should highlight those by requesting | 
| - // explicit traits below. | 
| - TaskTraits(); | 
| - TaskTraits(const TaskTraits& other) = default; | 
| - TaskTraits& operator=(const TaskTraits& other) = default; | 
| - ~TaskTraits(); | 
| - | 
| - // Tasks with this trait may block. This includes but is not limited to tasks | 
| - // that wait on synchronous file I/O operations: read or write a file from | 
| - // disk, interact with a pipe or a socket, rename or delete a file, enumerate | 
| - // files in a directory, etc. This trait isn't required for the mere use of | 
| - // locks. For tasks that block on base/ synchronization primitives, see | 
| - // WithBaseSyncPrimitives(). | 
| - TaskTraits& MayBlock(); | 
| - | 
| - // Tasks with this trait will pass base::AssertWaitAllowed(), i.e. will be | 
| - // allowed on the following methods : | 
| - // - base::WaitableEvent::Wait | 
| - // - base::ConditionVariable::Wait | 
| - // - base::PlatformThread::Join | 
| - // - base::PlatformThread::Sleep | 
| - // - base::Process::WaitForExit | 
| - // - base::Process::WaitForExitWithTimeout | 
| // | 
| - // Tasks should generally not use these methods. | 
| + // To get TaskTraits for tasks that require stricter guarantees and/or know | 
| + // the specific TaskPriority appropriate for them, provide arguments of type | 
| + // TaskPriority, TaskShutdownBehavior, MayBlock and/or WithBaseSyncPrimitives | 
| + // in any order to the constructor. | 
| // | 
| - // Instead of waiting on a WaitableEvent or a ConditionVariable, put the work | 
| - // that should happen after the wait in a callback and post that callback from | 
| - // where the WaitableEvent or ConditionVariable would have been signaled. If | 
| - // something needs to be scheduled after many tasks have executed, use | 
| - // base::BarrierClosure. | 
| - // | 
| - // Avoid creating threads. Instead, use | 
| - // base::Create(Sequenced|SingleTreaded)TaskRunnerWithTraits(). If a thread is | 
| - // really needed, make it non-joinable and add cleanup work at the end of the | 
| - // thread's main function (if using base::Thread, override Cleanup()). | 
| - // | 
| - // On Windows, join processes asynchronously using base::win::ObjectWatcher. | 
| - // | 
| - // MayBlock() must be specified in conjunction with this trait if and only if | 
| - // removing usage of methods listed above in the labeled tasks would still | 
| - // result in tasks that may block (per MayBlock()'s definition). | 
| - // | 
| - // In doubt, consult with //base/task_scheduler/OWNERS. | 
| - TaskTraits& WithBaseSyncPrimitives(); | 
| + // E.g. | 
| + // constexpr TaskTraits default_traits; | 
| + // constexpr TaskTraits user_visible_traits = {TaskPriority::USER_VISIBLE}; | 
| + // constexpr TaskTraits user_visible_may_block_traits = { | 
| + // TaskPriority::USER_VISIBLE, MayBlock()}; | 
| + // constexpr TaskTraits other_user_visible_may_block_traits = { | 
| + // MayBlock(), TaskPriority::USER_VISIBLE}; | 
| + template <class... ArgTypes> | 
| + constexpr TaskTraits(const ArgTypes&... args) | 
| + : priority_(internal::GetValueFromArgList( | 
| + internal::CallFirstTag(), | 
| + internal::EnumArgGetter<base::TaskPriority, | 
| + base::TaskPriority::INHERITED>(), | 
| + args...)), | 
| + shutdown_behavior_(internal::GetValueFromArgList( | 
| + internal::CallFirstTag(), | 
| + internal::EnumArgGetter< | 
| + base::TaskShutdownBehavior, | 
| + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN>(), | 
| + args...)), | 
| + may_block_(internal::GetValueFromArgList( | 
| + internal::CallFirstTag(), | 
| + internal::BooleanArgGetter<base::MayBlock>(), | 
| + args...)), | 
| + with_base_sync_primitives_(internal::GetValueFromArgList( | 
| + internal::CallFirstTag(), | 
| + internal::BooleanArgGetter<base::WithBaseSyncPrimitives>(), | 
| + args...)) {} | 
| - // Applies |priority| to tasks with these traits. | 
| - TaskTraits& WithPriority(TaskPriority priority); | 
| + constexpr TaskTraits(const TaskTraits& other) = default; | 
| + TaskTraits& operator=(const TaskTraits& other) = default; | 
| - // Applies |shutdown_behavior| to tasks with these traits. | 
| + // Deprecated. | 
| + TaskTraits& WithPriority(TaskPriority priority); | 
| TaskTraits& WithShutdownBehavior(TaskShutdownBehavior shutdown_behavior); | 
| - | 
| - // Returns true if tasks with these traits may block. | 
| - bool may_block() const { return may_block_; } | 
| - | 
| - // Returns true if tasks with these traits may use base/ sync primitives. | 
| - bool with_base_sync_primitives() const { return with_base_sync_primitives_; } | 
| + TaskTraits& MayBlock(); | 
| + TaskTraits& WithBaseSyncPrimitives(); | 
| // Returns the priority of tasks with these traits. | 
| TaskPriority priority() const { return priority_; } | 
| @@ -150,11 +260,17 @@ class BASE_EXPORT TaskTraits { | 
| // Returns the shutdown behavior of tasks with these traits. | 
| TaskShutdownBehavior shutdown_behavior() const { return shutdown_behavior_; } | 
| + // Returns true if tasks with these traits may block. | 
| + bool may_block() const { return may_block_; } | 
| + | 
| + // Returns true if tasks with these traits may use base/ sync primitives. | 
| + bool with_base_sync_primitives() const { return with_base_sync_primitives_; } | 
| + | 
| private: | 
| - bool may_block_; | 
| - bool with_base_sync_primitives_; | 
| TaskPriority priority_; | 
| TaskShutdownBehavior shutdown_behavior_; | 
| + bool may_block_; | 
| + bool with_base_sync_primitives_; | 
| }; | 
| // Returns string literals for the enums defined in this file. These methods |