// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TASK_TASK_TRAITS_EXTENSION_H_ #define BASE_TASK_TASK_TRAITS_EXTENSION_H_ #include #include #include #include #include "base/base_export.h" #include "base/traits_bag.h" namespace base { // Embedders can attach additional traits to a TaskTraits object in a way that // is opaque to base. These extension traits can then be specified along the // base traits when constructing the TaskTraits object. They are then stored and // propagated with the TaskTraits object. // // To support constexpr-compatible construction, extension traits are stored in // a fixed-size byte array in the TaskTraits object and serialized into and // parsed from this storage by an embedder-provided extension class and // MakeTaskTraitsExtension() template function. The embedder can later access // the extension traits via TaskTraits::GetExtension<[ExtensionClass]>(). // // A TaskTraits extension class needs to specify publicly: // (1) -- static constexpr uint8_t kExtensionId. // This field's value identifies the type of the extension uniquely within // each process. The embedder is responsible for ensuring uniqueness and // can assign values between kFirstEmbedderExtensionId and kMaxExtensionId // of TaskTraitsExtensionStorage::ExtensionId. // (2) -- static const [ExtensionClass] Parse( // -- const base::TaskTraitsExtensionStorage& extension). // Parses and constructs an extension object from the provided storage. // // For each TaskTraits extension class, the embedder has to provide a // corresponding MakeTaskTraitsExtension definition inside the same namespace // as its extension traits: // (3) -- template <...> // -- constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension( // -- ArgTypes... args). // Constructs and serializes an extension with the given arguments into // a TaskTraitsExtensionStorage and returns it. When the extension is used, // 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): // -------- // // namespace my_embedder { // enum class MyExtensionTrait {kMyValue1, kMyValue2}; // // class MyTaskTraitsExtension { // public: // static constexpr uint8_t kExtensionId = // TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; // // struct ValidTrait : public TaskTraits::ValidTrait { // // Accept base traits in MakeTaskTraitsExtension (see above). // using TaskTraits::ValidTrait::ValidTrait; // // ValidTrait(MyExtensionTrait); // }; // // using MyExtensionTraitFilter = // trait_helpers::EnumTraitFilter; // // // Constructor that accepts only valid traits as specified by ValidTraits. // template ::value>> // constexpr MyTaskTraitsExtension(ArgTypes... args) // : my_trait_(trait_helpers::GetTraitFromArgList( // args...)) {} // // // Serializes MyTaskTraitsExtension into a storage object and returns it. // constexpr base::TaskTraitsExtensionStorage Serialize() const { // // Note: can't use reinterpret_cast or placement new because neither are // // constexpr-compatible. // return {kExtensionId, {{static_cast(my_trait_)}}}; // } // // // Creates a MyTaskTraitsExtension by parsing it from a storage object. // static const MyTaskTraitsExtension Parse( // const base::TaskTraitsExtensionStorage& extension) { // return MyTaskTraitsExtension( // static_cast(extension.data[0])); // } // // constexpr MyExtensionTrait my_trait() const { return my_trait_; } // // private: // MyExtensionTrait my_trait_; // }; // // // Creates a MyTaskTraitsExtension for the provided |args| and serializes it // // into |extension|. Accepts only valid arguments for the // // MyTaskTraitsExtension() constructor. // template ::value>> // constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension( // ArgTypes... args) { // return MyTaskTraitsExtension(args...).Serialize(); // } // } // namespace my_embedder // // // Construction of TaskTraits with extension traits. // constexpr TaskTraits t1 = {my_embedder::MyExtensionTrait::kValueB}; // constexpr TaskTraits t2 = {base::MayBlock(), // my_embedder::MyExtensionTrait::kValueA}; // // // Extension traits can also be specified directly when posting a task. // base::PostTask(FROM_HERE, // {my_embedder::MyExtensionTrait::kValueB}, // base::BindOnce(...)); // Stores extension traits opaquely inside a fixed-size data array. We store // this data directly (rather than in a separate object on the heap) to support // constexpr-compatible TaskTraits construction. struct BASE_EXPORT TaskTraitsExtensionStorage { // Size in bytes. // Keep in sync with org.chromium.base.task.TaskTraits.EXTENSION_STORAGE_SIZE static constexpr size_t kStorageSize = 8; inline constexpr TaskTraitsExtensionStorage(); inline constexpr TaskTraitsExtensionStorage( uint8_t extension_id_in, const std::array& data_in); inline constexpr TaskTraitsExtensionStorage( uint8_t extension_id_in, std::array&& data_in); inline constexpr TaskTraitsExtensionStorage( const TaskTraitsExtensionStorage& other); inline TaskTraitsExtensionStorage& operator=( const TaskTraitsExtensionStorage& other) = default; inline bool operator==(const TaskTraitsExtensionStorage& other) const; enum ExtensionId : uint8_t { // Keep in sync with org.chromium.base.task.TaskTraits.INVALID_EXTENSION_ID kInvalidExtensionId = 0, // The embedder is responsible for assigning the remaining values uniquely. kFirstEmbedderExtensionId = 1, // Maximum number of extension types is artificially limited to support // super efficient TaskExecutor lookup in post_task.cc. // Keep in sync with org.chromium.base.TaskTraits.MAX_EXTENSION_ID kMaxExtensionId = 4 }; // Identifies the type of extension. See ExtensionId enum above. uint8_t extension_id; // Serialized extension data. std::array data; }; // TODO(https://crbug.com/874482): These constructors need to be "inline" but // defined outside the class above, because the chromium-style clang plugin // doesn't exempt constexpr constructors at the moment. inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage() : extension_id(kInvalidExtensionId), data{} {} inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage( uint8_t extension_id_in, const std::array& data_in) : extension_id(extension_id_in), data(data_in) {} inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage( uint8_t extension_id_in, std::array&& data_in) : extension_id(extension_id_in), data(std::move(data_in)) {} inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage( const TaskTraitsExtensionStorage& other) = default; namespace trait_helpers { // Helper class whose constructor tests if an extension accepts a list of // argument types. struct TaskTraitsExtension { template ()...))> constexpr TaskTraitsExtension(ArgTypes... args) {} }; // Tests that that a trait extension accepts all |ArgsTypes...|. template using AreValidTraitsForExtension = std::is_constructible; // 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 constexpr TaskTraitsExtensionStorage GetTaskTraitsExtension( std::true_type base_traits, ArgTypes... args) { return TaskTraitsExtensionStorage(); } template 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. inline bool TaskTraitsExtensionStorage::operator==( const TaskTraitsExtensionStorage& other) const { static_assert( 9 == sizeof(TaskTraitsExtensionStorage), "Update comparison operator when TaskTraitsExtensionStorage changes"); return extension_id == other.extension_id && data == other.data; } } // namespace base #endif // BASE_TASK_TASK_TRAITS_EXTENSION_H_