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