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