1 // Copyright 2019 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 #include "base/task/common/scoped_defer_task_posting.h"
6
7 #include "base/compiler_specific.h"
8 #include "third_party/abseil-cpp/absl/base/attributes.h"
9
10 namespace base {
11
12 namespace {
13
14 // Holds a thread-local pointer to the current scope or null when no
15 // scope is active.
16 ABSL_CONST_INIT thread_local ScopedDeferTaskPosting* scoped_defer_task_posting =
17 nullptr;
18
19 } // namespace
20
21 // static
PostOrDefer(scoped_refptr<SequencedTaskRunner> task_runner,const Location & from_here,OnceClosure task,base::TimeDelta delay)22 void ScopedDeferTaskPosting::PostOrDefer(
23 scoped_refptr<SequencedTaskRunner> task_runner,
24 const Location& from_here,
25 OnceClosure task,
26 base::TimeDelta delay) {
27 ScopedDeferTaskPosting* scope = Get();
28 if (scope) {
29 scope->DeferTaskPosting(std::move(task_runner), from_here, std::move(task),
30 delay);
31 return;
32 }
33
34 task_runner->PostDelayedTask(from_here, std::move(task), delay);
35 }
36
37 // static
Get()38 ScopedDeferTaskPosting* ScopedDeferTaskPosting::Get() {
39 // Workaround false-positive MSAN use-of-uninitialized-value on
40 // thread_local storage for loaded libraries:
41 // https://github.com/google/sanitizers/issues/1265
42 MSAN_UNPOISON(&scoped_defer_task_posting, sizeof(ScopedDeferTaskPosting*));
43
44 return scoped_defer_task_posting;
45 }
46
47 // static
Set(ScopedDeferTaskPosting * scope)48 bool ScopedDeferTaskPosting::Set(ScopedDeferTaskPosting* scope) {
49 // We can post a task from within a ScheduleWork in some tests, so we can
50 // get nested scopes. In this case ignore all except the top one.
51 if (Get() && scope)
52 return false;
53 scoped_defer_task_posting = scope;
54 return true;
55 }
56
57 // static
IsPresent()58 bool ScopedDeferTaskPosting::IsPresent() {
59 return !!Get();
60 }
61
ScopedDeferTaskPosting()62 ScopedDeferTaskPosting::ScopedDeferTaskPosting() {
63 top_level_scope_ = Set(this);
64 }
65
~ScopedDeferTaskPosting()66 ScopedDeferTaskPosting::~ScopedDeferTaskPosting() {
67 if (!top_level_scope_) {
68 DCHECK(deferred_tasks_.empty());
69 return;
70 }
71 Set(nullptr);
72 for (DeferredTask& deferred_task : deferred_tasks_) {
73 deferred_task.task_runner->PostDelayedTask(deferred_task.from_here,
74 std::move(deferred_task.task),
75 deferred_task.delay);
76 }
77 }
78
DeferredTask(scoped_refptr<SequencedTaskRunner> task_runner,Location from_here,OnceClosure task,base::TimeDelta delay)79 ScopedDeferTaskPosting::DeferredTask::DeferredTask(
80 scoped_refptr<SequencedTaskRunner> task_runner,
81 Location from_here,
82 OnceClosure task,
83 base::TimeDelta delay)
84 : task_runner(std::move(task_runner)),
85 from_here(from_here),
86 task(std::move(task)),
87 delay(delay) {}
88
89 ScopedDeferTaskPosting::DeferredTask::DeferredTask(DeferredTask&&) = default;
90
91 ScopedDeferTaskPosting::DeferredTask::~DeferredTask() = default;
92
DeferTaskPosting(scoped_refptr<SequencedTaskRunner> task_runner,const Location & from_here,OnceClosure task,base::TimeDelta delay)93 void ScopedDeferTaskPosting::DeferTaskPosting(
94 scoped_refptr<SequencedTaskRunner> task_runner,
95 const Location& from_here,
96 OnceClosure task,
97 base::TimeDelta delay) {
98 deferred_tasks_.push_back(
99 {std::move(task_runner), from_here, std::move(task), delay});
100 }
101
102 } // namespace base
103