1 // Copyright 2012 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_CRITICAL_CLOSURE_H_
6 #define BASE_CRITICAL_CLOSURE_H_
7
8 #include <string_view>
9 #include <utility>
10
11 #include "base/functional/callback.h"
12 #include "base/location.h"
13 #include "build/build_config.h"
14 #include "build/ios_buildflags.h"
15
16 #if BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_APP_EXTENSION)
17 #include <optional>
18
19 #include "base/functional/bind.h"
20 #include "base/ios/scoped_critical_action.h"
21 #endif
22
23 namespace base {
24
25 namespace internal {
26
27 #if BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_APP_EXTENSION)
28 // This class wraps a closure so it can continue to run for a period of time
29 // when the application goes to the background by using
30 // |ios::ScopedCriticalAction|.
31 class ImmediateCriticalClosure {
32 public:
33 explicit ImmediateCriticalClosure(std::string_view task_name,
34 OnceClosure closure);
35 ImmediateCriticalClosure(const ImmediateCriticalClosure&) = delete;
36 ImmediateCriticalClosure& operator=(const ImmediateCriticalClosure&) = delete;
37 ~ImmediateCriticalClosure();
38 void Run();
39
40 private:
41 ios::ScopedCriticalAction critical_action_;
42 OnceClosure closure_;
43 };
44
45 // This class is identical to ImmediateCriticalClosure, but the critical action
46 // is started when the action runs, not when the CriticalAction is created.
47 class PendingCriticalClosure {
48 public:
49 explicit PendingCriticalClosure(std::string_view task_name,
50 OnceClosure closure);
51 PendingCriticalClosure(const PendingCriticalClosure&) = delete;
52 PendingCriticalClosure& operator=(const PendingCriticalClosure&) = delete;
53 ~PendingCriticalClosure();
54 void Run();
55
56 private:
57 std::optional<ios::ScopedCriticalAction> critical_action_;
58 std::string task_name_;
59 OnceClosure closure_;
60 };
61 #endif // BUILDFLAG(IS_IOS)
62
63 } // namespace internal
64
65 // Returns a closure that will continue to run for a period of time when the
66 // application goes to the background if possible on platforms where
67 // applications don't execute while backgrounded, otherwise the original task is
68 // returned. If |is_immediate| is true, the closure will immediately prevent
69 // background suspension. Otherwise, the closure will wait to request background
70 // permission until it is run.
71 //
72 // Example:
73 // file_task_runner_->PostTask(
74 // FROM_HERE,
75 // MakeCriticalClosure(task_name,
76 // base::BindOnce(&WriteToDiskTask, path_, data)));
77 //
78 // Note new closures might be posted in this closure. If the new closures need
79 // background running time, |MakeCriticalClosure| should be applied on them
80 // before posting. |task_name| is used by the platform to identify any tasks
81 // that do not complete in time for suspension.
82 //
83 // This function is used automatically for tasks posted to a sequence runner
84 // using TaskShutdownBehavior::BLOCK_SHUTDOWN.
85 #if BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_APP_EXTENSION)
MakeCriticalClosure(std::string_view task_name,OnceClosure closure,bool is_immediate)86 inline OnceClosure MakeCriticalClosure(std::string_view task_name,
87 OnceClosure closure,
88 bool is_immediate) {
89 // Wrapping a null closure in a critical closure has unclear semantics and
90 // most likely indicates a bug. CHECK-ing early allows detecting and
91 // investigating these cases more easily.
92 CHECK(!closure.is_null());
93 if (is_immediate) {
94 return base::BindOnce(&internal::ImmediateCriticalClosure::Run,
95 Owned(new internal::ImmediateCriticalClosure(
96 task_name, std::move(closure))));
97 } else {
98 return base::BindOnce(&internal::PendingCriticalClosure::Run,
99 Owned(new internal::PendingCriticalClosure(
100 task_name, std::move(closure))));
101 }
102 }
103
MakeCriticalClosure(const Location & posted_from,OnceClosure closure,bool is_immediate)104 inline OnceClosure MakeCriticalClosure(const Location& posted_from,
105 OnceClosure closure,
106 bool is_immediate) {
107 return MakeCriticalClosure(posted_from.ToString(), std::move(closure),
108 is_immediate);
109 }
110
111 #else // BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_APP_EXTENSION)
112
MakeCriticalClosure(std::string_view task_name,OnceClosure closure,bool is_immediate)113 inline OnceClosure MakeCriticalClosure(std::string_view task_name,
114 OnceClosure closure,
115 bool is_immediate) {
116 // No-op for platforms where the application does not need to acquire
117 // background time for closures to finish when it goes into the background.
118 return closure;
119 }
120
MakeCriticalClosure(const Location & posted_from,OnceClosure closure,bool is_immediate)121 inline OnceClosure MakeCriticalClosure(const Location& posted_from,
122 OnceClosure closure,
123 bool is_immediate) {
124 return closure;
125 }
126
127 #endif // BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_APP_EXTENSION)
128
129 } // namespace base
130
131 #endif // BASE_CRITICAL_CLOSURE_H_
132