• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_THREADING_SEQUENCE_BOUND_H_
6 #define BASE_THREADING_SEQUENCE_BOUND_H_
7 
8 #include <new>
9 #include <tuple>
10 #include <type_traits>
11 #include <utility>
12 
13 #include "base/check.h"
14 #include "base/location.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/memory/scoped_refptr.h"
17 #include "base/run_loop.h"
18 #include "base/sequence_checker.h"
19 #include "base/task/sequenced_task_runner.h"
20 #include "base/threading/sequence_bound_internal.h"
21 
22 namespace base {
23 
24 // Performing blocking work on a different task runner is a common pattern for
25 // improving responsiveness of foreground task runners. `SequenceBound<T>`
26 // provides an abstraction for an owner object living on the owner sequence, to
27 // construct, call methods on, and destroy an object of type T that lives on a
28 // different sequence (the bound sequence).
29 //
30 // This makes it natural for code running on different sequences to be
31 // partitioned along class boundaries, e.g.:
32 //
33 // class Tab {
34 //  private:
35 //   void OnScroll() {
36 //     // ...
37 //     io_helper_.AsyncCall(&IOHelper::SaveScrollPosition);
38 //   }
39 //   base::SequenceBound<IOHelper> io_helper_{GetBackgroundTaskRunner()};
40 // };
41 //
42 // Note: `SequenceBound<T>` intentionally does not expose a raw pointer to the
43 // managed `T` to ensure its internal sequence-safety invariants are not
44 // violated. As a result, `AsyncCall()` cannot simply use `base::OnceCallback`
45 //
46 // SequenceBound also supports replies:
47 //
48 //   class Database {
49 //    public:
50 //     int Query(int value) {
51 //       return value * value;
52 //     }
53 //   };
54 //
55 //   // SequenceBound itself is owned on
56 //   // `SequencedTaskRunner::GetCurrentDefault()`. The managed Database
57 //   // instance managed by it is constructed and owned on `GetDBTaskRunner()`.
58 //   base::SequenceBound<Database> db(GetDBTaskRunner());
59 //
60 //   // `Database::Query()` runs on `GetDBTaskRunner()`, but
61 //   // `reply_callback` will run on the owner task runner.
62 //   auto reply_callback = [] (int result) {
63 //     LOG(ERROR) << result;  // Prints 25.
64 //   };
65 //   db.AsyncCall(&Database::Query).WithArgs(5)
66 //     .Then(base::BindOnce(reply_callback));
67 //
68 //   // When `db` goes out of scope, the Database instance will also be
69 //   // destroyed via a task posted to `GetDBTaskRunner()`.
70 //
71 // Sequence safety:
72 //
73 // Const-qualified methods may be used concurrently from multiple sequences,
74 // e.g. `AsyncCall()` or `is_null()`. Calls that are forwarded to the
75 // managed `T` will be posted to the bound sequence and executed serially
76 // there.
77 //
78 // Mutable methods (e.g. `Reset()`, destruction, or move assignment) require
79 // external synchronization if used concurrently with any other methods,
80 // including const-qualified methods.
81 //
82 // Advanced usage:
83 //
84 // Using `SequenceBound<std::unique_ptr<T>>` allows transferring ownership of an
85 // already-constructed `T` to `SequenceBound`. This can be helpful for more
86 // complex situations, where `T` needs to be constructed on a specific sequence
87 // that is different from where `T` will ultimately live.
88 //
89 // Construction (via the constructor or emplace) takes a `std::unique_ptr<T>`
90 // instead of forwarding the arguments to `T`'s constructor:
91 //
92 //   std::unique_ptr<Database> db_impl = MakeDatabaseOnMainThread();
93 //   base::SequenceBound<std::unique_ptr<Database>> db(GetDbTaskRunner(),
94 //                                                     std::move(db_impl));
95 //
96 // All other usage (e.g. `AsyncCall()`, `Reset()`) functions identically to a
97 // regular `SequenceBound<T>`:
98 //
99 //   // No need to dereference the `std::unique_ptr` explicitly:
100 //   db.AsyncCall(&Database::Query).WithArgs(5).Then(base::BindOnce(...));
101 template <typename T,
102           typename CrossThreadTraits =
103               sequence_bound_internal::CrossThreadTraits>
104 class SequenceBound {
105  private:
106   using Storage = sequence_bound_internal::Storage<T, CrossThreadTraits>;
107   // This is usually just `T` except if `T` is a `std::unique_ptr`; in that
108   // case, `UnwrappedT` is the type of the object owned by the
109   // `std::unique_ptr`, e.g. if `T` is `std::unique_ptr<std::string>`, then
110   // UnwrappedT is `std::string`.
111   using UnwrappedT = std::remove_pointer_t<typename Storage::Ptr>;
112 
113  public:
114   template <typename Signature>
115   using CrossThreadTask =
116       typename CrossThreadTraits::template CrossThreadTask<Signature>;
117 
118   // Note: on construction, SequenceBound binds to the current sequence. Any
119   // subsequent SequenceBound calls (including destruction) must run on that
120   // same sequence.
121 
122   // Constructs a null SequenceBound with no managed `T`.
123   SequenceBound() = default;
124 
125   // Constructs a SequenceBound that manages a new instance of `T` on
126   // `task_runner`. `T` will be constructed on `task_runner`.
127   //
128   // Once this constructor returns, it is safe to immediately use `AsyncCall()`,
129   // et cetera; these calls will be sequenced after the construction of the
130   // managed `T`.
131   template <typename... Args>
SequenceBound(scoped_refptr<SequencedTaskRunner> task_runner,Args &&...args)132   explicit SequenceBound(scoped_refptr<SequencedTaskRunner> task_runner,
133                          Args&&... args)
134       : impl_task_runner_(std::move(task_runner)) {
135     storage_.Construct(*impl_task_runner_, std::forward<Args>(args)...);
136   }
137 
138   // If non-null, the managed `T` will be destroyed on `impl_task_runner_`.`
~SequenceBound()139   ~SequenceBound() { Reset(); }
140 
141   // Disallow copy or assignment. SequenceBound has single ownership of the
142   // managed `T`.
143   SequenceBound(const SequenceBound&) = delete;
144   SequenceBound& operator=(const SequenceBound&) = delete;
145 
146   // Move construction and assignment.
SequenceBound(SequenceBound && other)147   SequenceBound(SequenceBound&& other) { MoveRecordFrom(other); }
148 
149   SequenceBound& operator=(SequenceBound&& other) {
150     Reset();
151     MoveRecordFrom(other);
152     return *this;
153   }
154 
155   // Move conversion helpers: allows upcasting from SequenceBound<Derived> to
156   // SequenceBound<Base>.
157   template <typename U>
158   // NOLINTNEXTLINE(google-explicit-constructor): Intentionally implicit.
SequenceBound(SequenceBound<U,CrossThreadTraits> && other)159   SequenceBound(SequenceBound<U, CrossThreadTraits>&& other) {
160     // TODO(https://crbug.com/1382549): static_assert that U* is convertible to
161     // T*.
162     MoveRecordFrom(other);
163   }
164 
165   template <typename U>
166   SequenceBound& operator=(SequenceBound<U, CrossThreadTraits>&& other) {
167     // TODO(https://crbug.com/1382549): static_assert that U* is convertible to
168     // T*.
169     Reset();
170     MoveRecordFrom(other);
171     return *this;
172   }
173 
174   // Constructs a new managed instance of `T` on `task_runner`. If `this` is
175   // already managing another instance of `T`, that pre-existing instance will
176   // first be destroyed by calling `Reset()`.
177   //
178   // Once `emplace()` returns, it is safe to immediately use `AsyncCall()`,
179   // et cetera; these calls will be sequenced after the construction of the
180   // managed `T`.
181   template <typename... Args>
emplace(scoped_refptr<SequencedTaskRunner> task_runner,Args &&...args)182   SequenceBound& emplace(scoped_refptr<SequencedTaskRunner> task_runner,
183                          Args&&... args) {
184     Reset();
185     impl_task_runner_ = std::move(task_runner);
186     storage_.Construct(*impl_task_runner_, std::forward<Args>(args)...);
187     return *this;
188   }
189 
190   // Invokes `method` of the managed `T` on `impl_task_runner_`. May only be
191   // used when `is_null()` is false.
192   //
193   // Basic usage:
194   //
195   //   helper.AsyncCall(&IOHelper::DoWork);
196   //
197   // If `method` accepts arguments, use `WithArgs()` to bind them:
198   //
199   //   helper.AsyncCall(&IOHelper::DoWorkWithArgs)
200   //       .WithArgs(args);
201   //
202   // Use `Then()` to run a callback on the owner sequence after `method`
203   // completes:
204   //
205   //   helper.AsyncCall(&IOHelper::GetValue)
206   //       .Then(std::move(process_result_callback));
207   //
208   // If a method returns a non-void type, use of `Then()` is required, and the
209   // method's return value will be passed to the `Then()` callback. To ignore
210   // the method's return value instead, wrap `method` in `base::IgnoreResult()`:
211   //
212   //   // Calling `GetPrefs` to force-initialize prefs.
213   //   helper.AsyncCall(base::IgnoreResult(&IOHelper::GetPrefs));
214   //
215   // `WithArgs()` and `Then()` may also be combined:
216   //
217   //   // Ordering is important: `Then()` must come last.
218   //   helper.AsyncCall(&IOHelper::GetValueWithArgs)
219   //       .WithArgs(args)
220   //       .Then(std::move(process_result_callback));
221   //
222   // Note: internally, `AsyncCall()` is implemented using a series of helper
223   // classes that build the callback chain and post it on destruction. Capturing
224   // the return value and passing it elsewhere or triggering lifetime extension
225   // (e.g. by binding the return value to a reference) are both unsupported.
226   template <typename R,
227             typename C,
228             typename... Args,
229             typename = std::enable_if_t<std::is_base_of_v<C, UnwrappedT>>>
R(C::* method)230   auto AsyncCall(R (C::*method)(Args...),
231                  const Location& location = Location::Current()) const {
232     return AsyncCallBuilder<R (C::*)(Args...), R, std::tuple<Args...>>(
233         this, &location, method);
234   }
235 
236   template <typename R,
237             typename C,
238             typename... Args,
239             typename = std::enable_if_t<std::is_base_of_v<C, UnwrappedT>>>
R(C::* method)240   auto AsyncCall(R (C::*method)(Args...) const,
241                  const Location& location = Location::Current()) const {
242     return AsyncCallBuilder<R (C::*)(Args...) const, R, std::tuple<Args...>>(
243         this, &location, method);
244   }
245 
246   template <typename R,
247             typename C,
248             typename... Args,
249             typename = std::enable_if_t<std::is_base_of_v<C, UnwrappedT>>>
250   auto AsyncCall(internal::IgnoreResultHelper<R (C::*)(Args...) const> method,
251                  const Location& location = Location::Current()) const {
252     return AsyncCallBuilder<
253         internal::IgnoreResultHelper<R (C::*)(Args...) const>, void,
254         std::tuple<Args...>>(this, &location, method);
255   }
256 
257   template <typename R,
258             typename C,
259             typename... Args,
260             typename = std::enable_if_t<std::is_base_of_v<C, UnwrappedT>>>
261   auto AsyncCall(internal::IgnoreResultHelper<R (C::*)(Args...)> method,
262                  const Location& location = Location::Current()) const {
263     return AsyncCallBuilder<internal::IgnoreResultHelper<R (C::*)(Args...)>,
264                             void, std::tuple<Args...>>(this, &location, method);
265   }
266 
267   // Posts `task` to `impl_task_runner_`, passing it a reference to the wrapped
268   // object. This allows arbitrary logic to be safely executed on the object's
269   // task runner. The object is guaranteed to remain alive for the duration of
270   // the task.
271   // TODO(crbug.com/1182140): Consider checking whether the task runner can run
272   // tasks in current sequence, and using "plain" binds and task posting (here
273   // and other places that `CrossThreadTraits::PostTask`).
274   using ConstPostTaskCallback = CrossThreadTask<void(const UnwrappedT&)>;
275   void PostTaskWithThisObject(
276       ConstPostTaskCallback callback,
277       const Location& location = Location::Current()) const {
278     DCHECK(!is_null());
279     // Even though the lifetime of the object pointed to by `get()` may not have
280     // begun yet, the storage has been allocated. Per [basic.life/6] and
281     // [basic.life/7], "Indirection through such a pointer is permitted but the
282     // resulting lvalue may only be used in limited ways, as described below."
283     CrossThreadTraits::PostTask(
284         *impl_task_runner_, location,
285         CrossThreadTraits::BindOnce(std::move(callback),
286                                     std::cref(*storage_.get())));
287   }
288 
289   // Same as above, but for non-const operations. The callback takes a pointer
290   // to the wrapped object rather than a const ref.
291   using PostTaskCallback = CrossThreadTask<void(UnwrappedT*)>;
292   void PostTaskWithThisObject(
293       PostTaskCallback callback,
294       const Location& location = Location::Current()) const {
295     DCHECK(!is_null());
296     CrossThreadTraits::PostTask(
297         *impl_task_runner_, location,
298         CrossThreadTraits::BindOnce(
299             std::move(callback),
300             CrossThreadTraits::Unretained(storage_.get())));
301   }
302 
FlushPostedTasksForTesting()303   void FlushPostedTasksForTesting() const {
304     DCHECK(!is_null());
305     RunLoop run_loop;
306     CrossThreadTraits::PostTask(*impl_task_runner_, FROM_HERE,
307                                 run_loop.QuitClosure());
308     run_loop.Run();
309   }
310 
311   // TODO(liberato): Add PostOrCall(), to support cases where synchronous calls
312   // are okay if it's the same task runner.
313 
314   // Resets `this` to null. If `this` is not currently null, posts destruction
315   // of the managed `T` to `impl_task_runner_`.
Reset()316   void Reset() {
317     if (is_null())
318       return;
319 
320     storage_.Destruct(*impl_task_runner_);
321     impl_task_runner_ = nullptr;
322   }
323 
324   // Resets `this` to null. If `this` is not currently null, posts destruction
325   // of the managed `T` to `impl_task_runner_`. Blocks until the destructor has
326   // run.
SynchronouslyResetForTest()327   void SynchronouslyResetForTest() {
328     if (is_null())
329       return;
330 
331     scoped_refptr<SequencedTaskRunner> task_runner = impl_task_runner_;
332     Reset();
333     // `Reset()` posts a task to destroy the managed `T`; synchronously wait for
334     // that posted task to complete.
335     RunLoop run_loop;
336     CrossThreadTraits::PostTask(*task_runner, FROM_HERE,
337                                 run_loop.QuitClosure());
338     run_loop.Run();
339   }
340 
341   // Return true if `this` is logically null; otherwise, returns false.
342   //
343   // A SequenceBound is logically null if there is no managed `T`; it is only
344   // valid to call `AsyncCall()` on a non-null SequenceBound.
345   //
346   // Note that the concept of 'logically null' here does not exactly match the
347   // lifetime of `T`, which lives on `impl_task_runner_`. In particular, when
348   // SequenceBound is first constructed, `is_null()` may return false, even
349   // though the lifetime of `T` may not have begun yet on `impl_task_runner_`.
350   // Similarly, after `SequenceBound::Reset()`, `is_null()` may return true,
351   // even though the lifetime of `T` may not have ended yet on
352   // `impl_task_runner_`.
is_null()353   bool is_null() const { return !storage_.get(); }
354 
355   // True if `this` is not logically null. See `is_null()`.
356   explicit operator bool() const { return !is_null(); }
357 
358  private:
359   // For move conversion.
360   template <typename U, typename V>
361   friend class SequenceBound;
362 
363   template <template <typename> class CallbackType>
364   using EnableIfIsCrossThreadTask =
365       typename CrossThreadTraits::template EnableIfIsCrossThreadTask<
366           CallbackType>;
367 
368   // Support helpers for `AsyncCall()` implementation.
369   //
370   // Several implementation notes:
371   // 1. Tasks are posted via destroying the builder or an explicit call to
372   //    `Then()`.
373   //
374   // 2. A builder may be consumed by:
375   //
376   //    - calling `Then()`, which immediately posts the task chain
377   //    - calling `WithArgs()`, which returns a new builder with the captured
378   //      arguments
379   //
380   //    Builders that are consumed have the internal `sequence_bound_` field
381   //    nulled out; the hope is the compiler can see this and use it to
382   //    eliminate dead branches (e.g. correctness checks that aren't needed
383   //    since the code can be statically proved correct).
384   //
385   // 3. Builder methods are rvalue-qualified to try to enforce that the builder
386   //    is only used as a temporary. Note that this only helps so much; nothing
387   //    prevents a determined caller from using `std::move()` to force calls to
388   //    a non-temporary instance.
389   //
390   // TODO(dcheng): It might also be possible to use Gmock-style matcher
391   // composition, e.g. something like:
392   //
393   //   sb.AsyncCall(&Helper::DoWork, WithArgs(args),
394   //                Then(std::move(process_result));
395   //
396   // In theory, this might allow the elimination of magic destructors and
397   // better static checking by the compiler.
398   template <typename MethodRef>
399   class AsyncCallBuilderBase {
400    protected:
AsyncCallBuilderBase(const SequenceBound * sequence_bound,const Location * location,MethodRef method)401     AsyncCallBuilderBase(const SequenceBound* sequence_bound,
402                          const Location* location,
403                          MethodRef method)
404         : sequence_bound_(sequence_bound),
405           location_(location),
406           method_(method) {
407       // Common entry point for `AsyncCall()`, so check preconditions here.
408       DCHECK(sequence_bound_);
409       DCHECK(sequence_bound_->storage_.get());
410     }
411 
412     AsyncCallBuilderBase(AsyncCallBuilderBase&&) = default;
413     AsyncCallBuilderBase& operator=(AsyncCallBuilderBase&&) = default;
414 
415     // `sequence_bound_` is consumed and set to `nullptr` when `Then()` is
416     // invoked. This is used as a flag for two potential states
417     //
418     // - if a method returns void, invoking `Then()` is optional. The destructor
419     //   will check if `sequence_bound_` is null; if it is, `Then()` was
420     //   already invoked and the task chain has already been posted, so the
421     //   destructor does not need to do anything. Otherwise, the destructor
422     //   needs to post the task to make the async call. In theory, the compiler
423     //   should be able to eliminate this branch based on the presence or
424     //   absence of a call to `Then()`.
425     //
426     // - if a method returns a non-void type, `Then()` *must* be invoked. The
427     //   destructor will `CHECK()` if `sequence_bound_` is non-null, since that
428     //   indicates `Then()` was not invoked. Similarly, note this branch should
429     //   be eliminated by the optimizer if the code is free of bugs. :)
430     raw_ptr<const SequenceBound<T, CrossThreadTraits>, DanglingUntriaged>
431         sequence_bound_;
432     // Subtle: this typically points at a Location *temporary*. This is used to
433     // try to detect errors resulting from lifetime extension of the async call
434     // factory temporaries, since the factory destructors can perform work. If
435     // the lifetime of the factory is incorrectly extended, dereferencing
436     // `location_` will trigger a stack-use-after-scope when running with ASan.
437     const raw_ptr<const Location> location_;
438     MethodRef method_;
439   };
440 
441   template <typename MethodRef, typename ReturnType, typename ArgsTuple>
442   class AsyncCallBuilderImpl;
443 
444   // Selected method has no arguments and returns void.
445   template <typename MethodRef>
446   class AsyncCallBuilderImpl<MethodRef, void, std::tuple<>>
447       : public AsyncCallBuilderBase<MethodRef> {
448    public:
449     // Note: despite being here, this is actually still protected, since it is
450     // protected on the base class.
451     using AsyncCallBuilderBase<MethodRef>::AsyncCallBuilderBase;
452 
~AsyncCallBuilderImpl()453     ~AsyncCallBuilderImpl() {
454       if (this->sequence_bound_) {
455         CrossThreadTraits::PostTask(
456             *this->sequence_bound_->impl_task_runner_, *this->location_,
457             CrossThreadTraits::BindOnce(
458                 this->method_, CrossThreadTraits::Unretained(
459                                    this->sequence_bound_->storage_.get())));
460       }
461     }
462 
Then(CrossThreadTask<void ()> then_callback)463     void Then(CrossThreadTask<void()> then_callback) && {
464       this->sequence_bound_->PostTaskAndThenHelper(
465           *this->location_,
466           CrossThreadTraits::BindOnce(
467               this->method_, CrossThreadTraits::Unretained(
468                                  this->sequence_bound_->storage_.get())),
469           std::move(then_callback));
470       this->sequence_bound_ = nullptr;
471     }
472 
473    private:
474     friend SequenceBound;
475 
476     AsyncCallBuilderImpl(AsyncCallBuilderImpl&&) = default;
477     AsyncCallBuilderImpl& operator=(AsyncCallBuilderImpl&&) = default;
478   };
479 
480   // Selected method has no arguments and returns non-void.
481   template <typename MethodRef, typename ReturnType>
482   class AsyncCallBuilderImpl<MethodRef, ReturnType, std::tuple<>>
483       : public AsyncCallBuilderBase<MethodRef> {
484    public:
485     // Note: despite being here, this is actually still protected, since it is
486     // protected on the base class.
487     using AsyncCallBuilderBase<MethodRef>::AsyncCallBuilderBase;
488 
~AsyncCallBuilderImpl()489     ~AsyncCallBuilderImpl() {
490       // Must use Then() since the method's return type is not void.
491       // Should be optimized out if the code is bug-free.
492       CHECK(!this->sequence_bound_)
493           << "Then() not invoked for a method that returns a non-void type; "
494           << "make sure to invoke Then() or use base::IgnoreResult()";
495     }
496 
497     template <template <typename> class CallbackType,
498               typename ThenArg,
499               typename = EnableIfIsCrossThreadTask<CallbackType>>
Then(CallbackType<void (ThenArg)> then_callback)500     void Then(CallbackType<void(ThenArg)> then_callback) && {
501       this->sequence_bound_->PostTaskAndThenHelper(
502           *this->location_,
503           CrossThreadTraits::BindOnce(
504               this->method_, CrossThreadTraits::Unretained(
505                                  this->sequence_bound_->storage_.get())),
506           std::move(then_callback));
507       this->sequence_bound_ = nullptr;
508     }
509 
510    private:
511     friend SequenceBound;
512 
513     AsyncCallBuilderImpl(AsyncCallBuilderImpl&&) = default;
514     AsyncCallBuilderImpl& operator=(AsyncCallBuilderImpl&&) = default;
515   };
516 
517   // Selected method has arguments. Return type can be void or non-void.
518   template <typename MethodRef, typename ReturnType, typename... Args>
519   class AsyncCallBuilderImpl<MethodRef, ReturnType, std::tuple<Args...>>
520       : public AsyncCallBuilderBase<MethodRef> {
521    public:
522     // Note: despite being here, this is actually still protected, since it is
523     // protected on the base class.
524     using AsyncCallBuilderBase<MethodRef>::AsyncCallBuilderBase;
525 
~AsyncCallBuilderImpl()526     ~AsyncCallBuilderImpl() {
527       // Must use WithArgs() since the method takes arguments.
528       // Should be optimized out if the code is bug-free.
529       CHECK(!this->sequence_bound_);
530     }
531 
532     template <typename... BoundArgs>
WithArgs(BoundArgs &&...bound_args)533     auto WithArgs(BoundArgs&&... bound_args) {
534       const SequenceBound* const sequence_bound =
535           std::exchange(this->sequence_bound_, nullptr);
536       return AsyncCallWithBoundArgsBuilder<ReturnType>(
537           sequence_bound, this->location_,
538           CrossThreadTraits::BindOnce(
539               this->method_,
540               CrossThreadTraits::Unretained(sequence_bound->storage_.get()),
541               std::forward<BoundArgs>(bound_args)...));
542     }
543 
544    private:
545     friend SequenceBound;
546 
547     AsyncCallBuilderImpl(AsyncCallBuilderImpl&&) = default;
548     AsyncCallBuilderImpl& operator=(AsyncCallBuilderImpl&&) = default;
549   };
550 
551   // `MethodRef` is either a member function pointer type or a member function
552   //     pointer type wrapped with `internal::IgnoreResultHelper`.
553   // `R` is the return type of `MethodRef`. This is always `void` if
554   //     `MethodRef` is an `internal::IgnoreResultHelper` wrapper.
555   // `ArgsTuple` is a `std::tuple` with template type arguments corresponding to
556   //     the types of the method's parameters.
557   template <typename MethodRef, typename R, typename ArgsTuple>
558   using AsyncCallBuilder = AsyncCallBuilderImpl<MethodRef, R, ArgsTuple>;
559 
560   // Support factories when arguments are bound using `WithArgs()`. These
561   // factories don't need to handle raw method pointers, since everything has
562   // already been packaged into a base::OnceCallback.
563   template <typename ReturnType>
564   class AsyncCallWithBoundArgsBuilderBase {
565    protected:
AsyncCallWithBoundArgsBuilderBase(const SequenceBound * sequence_bound,const Location * location,CrossThreadTask<ReturnType ()> callback)566     AsyncCallWithBoundArgsBuilderBase(const SequenceBound* sequence_bound,
567                                       const Location* location,
568                                       CrossThreadTask<ReturnType()> callback)
569         : sequence_bound_(sequence_bound),
570           location_(location),
571           callback_(std::move(callback)) {
572       DCHECK(sequence_bound_);
573       DCHECK(sequence_bound_->storage_.get());
574     }
575 
576     // Subtle: the internal helpers rely on move elision. Preventing move
577     // elision (e.g. using `std::move()` when returning the temporary) will
578     // trigger a `CHECK()` since `sequence_bound_` is not reset to nullptr on
579     // move.
580     AsyncCallWithBoundArgsBuilderBase(
581         AsyncCallWithBoundArgsBuilderBase&&) noexcept = default;
582     AsyncCallWithBoundArgsBuilderBase& operator=(
583         AsyncCallWithBoundArgsBuilderBase&&) noexcept = default;
584 
585     raw_ptr<const SequenceBound<T, CrossThreadTraits>> sequence_bound_;
586     const raw_ptr<const Location> location_;
587     CrossThreadTask<ReturnType()> callback_;
588   };
589 
590   // Note: this doesn't handle a void return type, which has an explicit
591   // specialization below.
592   template <typename ReturnType>
593   class AsyncCallWithBoundArgsBuilderDefault
594       : public AsyncCallWithBoundArgsBuilderBase<ReturnType> {
595    public:
~AsyncCallWithBoundArgsBuilderDefault()596     ~AsyncCallWithBoundArgsBuilderDefault() {
597       // Must use Then() since the method's return type is not void.
598       // Should be optimized out if the code is bug-free.
599       CHECK(!this->sequence_bound_);
600     }
601 
602     template <template <typename> class CallbackType,
603               typename ThenArg,
604               typename = EnableIfIsCrossThreadTask<CallbackType>>
Then(CallbackType<void (ThenArg)> then_callback)605     void Then(CallbackType<void(ThenArg)> then_callback) && {
606       this->sequence_bound_->PostTaskAndThenHelper(*this->location_,
607                                                    std::move(this->callback_),
608                                                    std::move(then_callback));
609       this->sequence_bound_ = nullptr;
610     }
611 
612    protected:
613     using AsyncCallWithBoundArgsBuilderBase<
614         ReturnType>::AsyncCallWithBoundArgsBuilderBase;
615 
616    private:
617     friend SequenceBound;
618 
619     AsyncCallWithBoundArgsBuilderDefault(
620         AsyncCallWithBoundArgsBuilderDefault&&) = default;
621     AsyncCallWithBoundArgsBuilderDefault& operator=(
622         AsyncCallWithBoundArgsBuilderDefault&&) = default;
623   };
624 
625   class AsyncCallWithBoundArgsBuilderVoid
626       : public AsyncCallWithBoundArgsBuilderBase<void> {
627    public:
628     // Note: despite being here, this is actually still protected, since it is
629     // protected on the base class.
630     using AsyncCallWithBoundArgsBuilderBase<
631         void>::AsyncCallWithBoundArgsBuilderBase;
632 
~AsyncCallWithBoundArgsBuilderVoid()633     ~AsyncCallWithBoundArgsBuilderVoid() {
634       if (this->sequence_bound_) {
635         CrossThreadTraits::PostTask(*this->sequence_bound_->impl_task_runner_,
636                                     *this->location_,
637                                     std::move(this->callback_));
638       }
639     }
640 
Then(CrossThreadTask<void ()> then_callback)641     void Then(CrossThreadTask<void()> then_callback) && {
642       this->sequence_bound_->PostTaskAndThenHelper(*this->location_,
643                                                    std::move(this->callback_),
644                                                    std::move(then_callback));
645       this->sequence_bound_ = nullptr;
646     }
647 
648    private:
649     friend SequenceBound;
650 
651     AsyncCallWithBoundArgsBuilderVoid(AsyncCallWithBoundArgsBuilderVoid&&) =
652         default;
653     AsyncCallWithBoundArgsBuilderVoid& operator=(
654         AsyncCallWithBoundArgsBuilderVoid&&) = default;
655   };
656 
657   template <typename ReturnType>
658   using AsyncCallWithBoundArgsBuilder = typename std::conditional<
659       std::is_void<ReturnType>::value,
660       AsyncCallWithBoundArgsBuilderVoid,
661       AsyncCallWithBoundArgsBuilderDefault<ReturnType>>::type;
662 
PostTaskAndThenHelper(const Location & location,CrossThreadTask<void ()> callback,CrossThreadTask<void ()> then_callback)663   void PostTaskAndThenHelper(const Location& location,
664                              CrossThreadTask<void()> callback,
665                              CrossThreadTask<void()> then_callback) const {
666     CrossThreadTraits::PostTaskAndReply(*impl_task_runner_, location,
667                                         std::move(callback),
668                                         std::move(then_callback));
669   }
670 
671   template <typename ReturnType,
672             template <typename>
673             class CallbackType,
674             typename ThenArg,
675             typename = EnableIfIsCrossThreadTask<CallbackType>>
PostTaskAndThenHelper(const Location & location,CrossThreadTask<ReturnType ()> callback,CallbackType<void (ThenArg)> then_callback)676   void PostTaskAndThenHelper(const Location& location,
677                              CrossThreadTask<ReturnType()> callback,
678                              CallbackType<void(ThenArg)> then_callback) const {
679     CrossThreadTask<void(ThenArg)>&& once_then_callback =
680         std::move(then_callback);
681     CrossThreadTraits::PostTaskAndReplyWithResult(
682         *impl_task_runner_, location, std::move(callback),
683         std::move(once_then_callback));
684   }
685 
686   // Helper to support move construction and move assignment.
687   //
688   // TODO(https://crbug.com/1382549): Constrain this so converting between
689   // std::unique_ptr<T> and T are explicitly forbidden (rather than simply
690   // failing to build in spectacular ways).
691   template <typename From>
MoveRecordFrom(From && other)692   void MoveRecordFrom(From&& other) {
693     impl_task_runner_ = std::move(other.impl_task_runner_);
694 
695     storage_.TakeFrom(std::move(other.storage_));
696   }
697 
698   Storage storage_;
699 
700   // Task runner which manages `storage_.get()`. `storage_.get()`'s pointee is
701   // constructed, destroyed, and otherwise used only on this task runner.
702   scoped_refptr<SequencedTaskRunner> impl_task_runner_;
703 };
704 
705 }  // namespace base
706 
707 #endif  // BASE_THREADING_SEQUENCE_BOUND_H_
708