summaryrefslogtreecommitdiff
path: root/chromium/base/task/task_traits_extension.h
blob: 1ba37c3fae58d4f72cebf027b88f8075e6efb337 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// 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 <stdint.h>

#include <array>
#include <tuple>
#include <utility>

#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<MyExtensionTrait, MyExtensionTrait::kA>;
//
//   // Constructor that accepts only valid traits as specified by ValidTraits.
//   template <class... ArgTypes,
//             class CheckArgumentsAreValid = std::enable_if_t<
//                 base::trait_helpers::AreValidTraits<
//                     ValidTrait, ArgTypes...>::value>>
//   constexpr MyTaskTraitsExtension(ArgTypes... args)
//       : my_trait_(trait_helpers::GetTraitFromArgList<MyExtensionTraitFilter>(
//                      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<uint8_t>(my_trait_)}}};
//   }
//
//   // Creates a MyTaskTraitsExtension by parsing it from a storage object.
//   static const MyTaskTraitsExtension Parse(
//       const base::TaskTraitsExtensionStorage& extension) {
//     return MyTaskTraitsExtension(
//         static_cast<MyExtensionTrait>(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 <class... ArgTypes,
//           class = std::enable_if_t<
//               base::trait_helpers::AreValidTraits<
//                   MyTaskTraitsExtension::ValidTrait, ArgTypes...>::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<uint8_t, kStorageSize>& data_in);
  inline constexpr TaskTraitsExtensionStorage(
      uint8_t extension_id_in,
      std::array<uint8_t, kStorageSize>&& 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<uint8_t, kStorageSize> 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<uint8_t, kStorageSize>& data_in)
    : extension_id(extension_id_in), data(data_in) {}

inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage(
    uint8_t extension_id_in,
    std::array<uint8_t, kStorageSize>&& 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 <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>
using AreValidTraitsForExtension =
    std::is_constructible<TaskTraitsExtension, ArgTypes...>;

// 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.
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_