1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155

base / ios / scoped_critical_action.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_IOS_SCOPED_CRITICAL_ACTION_H_
#define BASE_IOS_SCOPED_CRITICAL_ACTION_H_

#include <map>
#include <string>
#include <string_view>
#include <utility>

#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"

namespace base {
namespace ios {

// Skip starting background tasks if the application is terminating.
BASE_DECLARE_FEATURE(kScopedCriticalActionSkipOnShutdown);

// This class attempts to allow the application to continue to run for a period
// of time after it transitions to the background. The construction of an
// instance of this class marks the beginning of a task that needs background
// running time when the application is moved to the background and the
// destruction marks the end of such a task.
//
// Note there is no guarantee that the task will continue to finish when the
// application is moved to the background.
//
// This class should be used at times where leaving a task unfinished might be
// detrimental to user experience. For example, it should be used to ensure that
// the application has enough time to save important data or at least attempt to
// save such data.
class ScopedCriticalAction {
 public:
  ScopedCriticalAction(std::string_view task_name);

  ScopedCriticalAction(const ScopedCriticalAction&) = delete;
  ScopedCriticalAction& operator=(const ScopedCriticalAction&) = delete;

  ~ScopedCriticalAction();

  // Skip starting new background tasks if the application is terminating.
  // This must be triggered by the application and cannot be triggered by
  // a UIApplicationWillTerminateNotification, as that notification fires
  // after -[UIApplicationDelegate applicationWillTerminate:].
  static void ApplicationWillTerminate();

  // Exposed for unit-testing.
  static void ClearNumActiveBackgroundTasksForTest();
  static int GetNumActiveBackgroundTasksForTest();
  static void ResetApplicationWillTerminateForTest();

 private:
  // Core logic; ScopedCriticalAction should not be reference counted so
  // that it follows the normal pattern of stack-allocating ScopedFoo objects,
  // but the expiration handler needs to have a reference counted object to
  // refer to. All functions are thread safe.
  class Core : public base::RefCountedThreadSafe<Core> {
   public:
    Core();

    Core(const Core&) = delete;
    Core& operator=(const Core&) = delete;

    // Informs the OS that the background task has started. This is a
    // static method to ensure that the instance has a non-zero refcount.
    // |task_name| is used by the OS to log any leaked background tasks.
    // Invoking this function more than once is allowed: all except the
    // first successful call will be a no-op.
    static void StartBackgroundTask(scoped_refptr<Core> core,
                                    std::string_view task_name);
    // Informs the OS that the background task has completed. This is a
    // static method to ensure that the instance has a non-zero refcount.
    // Invoking this function more than once is allowed: all except the
    // first call will be a no-op.
    static void EndBackgroundTask(scoped_refptr<Core> core);

   private:
    friend base::RefCountedThreadSafe<Core>;
    ~Core();

    // |UIBackgroundTaskIdentifier| returned by
    // |beginBackgroundTaskWithName:expirationHandler:| when marking the
    // beginning of a long-running background task. It is defined as a uint64_t
    // instead of a |UIBackgroundTaskIdentifier| so this class can be used in
    // .cc files.
    uint64_t background_task_id_ GUARDED_BY(background_task_id_lock_);
    Lock background_task_id_lock_;
  };

  // This class is thread safe.
  class ActiveBackgroundTaskCache {
   public:
    // This struct should be considered internal to this class and opaque to
    // callers.
    struct InternalEntry {
      InternalEntry();
      InternalEntry(const InternalEntry&) = delete;
      InternalEntry(InternalEntry&&);
      ~InternalEntry();

      InternalEntry& operator=(const InternalEntry&) = delete;
      InternalEntry& operator=(InternalEntry&&);

      // The instance of the core that drives the background task.
      scoped_refptr<Core> core;
      // Refcounting for the number of ScopedCriticalAction instances that
      // require the existence of this background task.
      int num_active_handles = 0;
    };

    using NameAndTime = std::pair<std::string, base::TimeTicks>;
    using InternalEntriesMap = std::map<NameAndTime, InternalEntry>;
    // A handle should be treated as an opaque token by the caller.
    using Handle = InternalEntriesMap::iterator;

    // Returns a leaky singleton instance.
    static ActiveBackgroundTaskCache* GetInstance();

    ActiveBackgroundTaskCache();
    ~ActiveBackgroundTaskCache();

    // Starts a new background task if none existed with the same name. If a
    // task already exists with the same name, its lifetime is effectively
    // extended. Callers must invoke ReleaseHandle() once they no longer need to
    // prevent background suspension.
    Handle EnsureBackgroundTaskExistsWithName(std::string_view task_name);

    // Indicates that a previous caller to EnsureBackgroundTaskExistsWithName()
    // no longer needs to prevent background suspension.
    void ReleaseHandle(Handle handle);

    // Skip starting new background tasks if the application is terminating.
    void ApplicationWillTerminate();

    // Exposed for unit-testing.
    void ResetApplicationWillTerminateForTest();

   private:
    std::atomic_bool application_is_terminating_{false};
    InternalEntriesMap entries_map_ GUARDED_BY(entries_map_lock_);
    Lock entries_map_lock_;
  };

  const ActiveBackgroundTaskCache::Handle task_handle_;
};

}  // namespace ios
}  // namespace base

#endif  // BASE_IOS_SCOPED_CRITICAL_ACTION_H_