• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_IOS_SCOPED_CRITICAL_ACTION_H_
6 #define BASE_IOS_SCOPED_CRITICAL_ACTION_H_
7 
8 #include <map>
9 #include <string>
10 #include <string_view>
11 #include <utility>
12 
13 #include "base/feature_list.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/synchronization/lock.h"
16 #include "base/time/time.h"
17 
18 namespace base {
19 namespace ios {
20 
21 // Skip starting background tasks if the application is terminating.
22 BASE_DECLARE_FEATURE(kScopedCriticalActionSkipOnShutdown);
23 
24 // This class attempts to allow the application to continue to run for a period
25 // of time after it transitions to the background. The construction of an
26 // instance of this class marks the beginning of a task that needs background
27 // running time when the application is moved to the background and the
28 // destruction marks the end of such a task.
29 //
30 // Note there is no guarantee that the task will continue to finish when the
31 // application is moved to the background.
32 //
33 // This class should be used at times where leaving a task unfinished might be
34 // detrimental to user experience. For example, it should be used to ensure that
35 // the application has enough time to save important data or at least attempt to
36 // save such data.
37 class ScopedCriticalAction {
38  public:
39   ScopedCriticalAction(std::string_view task_name);
40 
41   ScopedCriticalAction(const ScopedCriticalAction&) = delete;
42   ScopedCriticalAction& operator=(const ScopedCriticalAction&) = delete;
43 
44   ~ScopedCriticalAction();
45 
46   // Skip starting new background tasks if the application is terminating.
47   // This must be triggered by the application and cannot be triggered by
48   // a UIApplicationWillTerminateNotification, as that notification fires
49   // after -[UIApplicationDelegate applicationWillTerminate:].
50   static void ApplicationWillTerminate();
51 
52   // Exposed for unit-testing.
53   static void ClearNumActiveBackgroundTasksForTest();
54   static int GetNumActiveBackgroundTasksForTest();
55   static void ResetApplicationWillTerminateForTest();
56 
57  private:
58   // Core logic; ScopedCriticalAction should not be reference counted so
59   // that it follows the normal pattern of stack-allocating ScopedFoo objects,
60   // but the expiration handler needs to have a reference counted object to
61   // refer to. All functions are thread safe.
62   class Core : public base::RefCountedThreadSafe<Core> {
63    public:
64     Core();
65 
66     Core(const Core&) = delete;
67     Core& operator=(const Core&) = delete;
68 
69     // Informs the OS that the background task has started. This is a
70     // static method to ensure that the instance has a non-zero refcount.
71     // |task_name| is used by the OS to log any leaked background tasks.
72     // Invoking this function more than once is allowed: all except the
73     // first successful call will be a no-op.
74     static void StartBackgroundTask(scoped_refptr<Core> core,
75                                     std::string_view task_name);
76     // Informs the OS that the background task has completed. This is a
77     // static method to ensure that the instance has a non-zero refcount.
78     // Invoking this function more than once is allowed: all except the
79     // first call will be a no-op.
80     static void EndBackgroundTask(scoped_refptr<Core> core);
81 
82    private:
83     friend base::RefCountedThreadSafe<Core>;
84     ~Core();
85 
86     // |UIBackgroundTaskIdentifier| returned by
87     // |beginBackgroundTaskWithName:expirationHandler:| when marking the
88     // beginning of a long-running background task. It is defined as a uint64_t
89     // instead of a |UIBackgroundTaskIdentifier| so this class can be used in
90     // .cc files.
91     uint64_t background_task_id_ GUARDED_BY(background_task_id_lock_);
92     Lock background_task_id_lock_;
93   };
94 
95   // This class is thread safe.
96   class ActiveBackgroundTaskCache {
97    public:
98     // This struct should be considered internal to this class and opaque to
99     // callers.
100     struct InternalEntry {
101       InternalEntry();
102       InternalEntry(const InternalEntry&) = delete;
103       InternalEntry(InternalEntry&&);
104       ~InternalEntry();
105 
106       InternalEntry& operator=(const InternalEntry&) = delete;
107       InternalEntry& operator=(InternalEntry&&);
108 
109       // The instance of the core that drives the background task.
110       scoped_refptr<Core> core;
111       // Refcounting for the number of ScopedCriticalAction instances that
112       // require the existence of this background task.
113       int num_active_handles = 0;
114     };
115 
116     using NameAndTime = std::pair<std::string, base::TimeTicks>;
117     using InternalEntriesMap = std::map<NameAndTime, InternalEntry>;
118     // A handle should be treated as an opaque token by the caller.
119     using Handle = InternalEntriesMap::iterator;
120 
121     // Returns a leaky singleton instance.
122     static ActiveBackgroundTaskCache* GetInstance();
123 
124     ActiveBackgroundTaskCache();
125     ~ActiveBackgroundTaskCache();
126 
127     // Starts a new background task if none existed with the same name. If a
128     // task already exists with the same name, its lifetime is effectively
129     // extended. Callers must invoke ReleaseHandle() once they no longer need to
130     // prevent background suspension.
131     Handle EnsureBackgroundTaskExistsWithName(std::string_view task_name);
132 
133     // Indicates that a previous caller to EnsureBackgroundTaskExistsWithName()
134     // no longer needs to prevent background suspension.
135     void ReleaseHandle(Handle handle);
136 
137     // Skip starting new background tasks if the application is terminating.
138     void ApplicationWillTerminate();
139 
140     // Exposed for unit-testing.
141     void ResetApplicationWillTerminateForTest();
142 
143    private:
144     std::atomic_bool application_is_terminating_{false};
145     InternalEntriesMap entries_map_ GUARDED_BY(entries_map_lock_);
146     Lock entries_map_lock_;
147   };
148 
149   const ActiveBackgroundTaskCache::Handle task_handle_;
150 };
151 
152 }  // namespace ios
153 }  // namespace base
154 
155 #endif  // BASE_IOS_SCOPED_CRITICAL_ACTION_H_
156