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