diff options
author | chenyu@chromium.org <chenyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-01 15:42:16 +0000 |
---|---|---|
committer | chenyu@chromium.org <chenyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-01 15:42:16 +0000 |
commit | f0a6f4bb3d6a54ab6435af16dd87314058a4b843 (patch) | |
tree | e60ca4e557599cdd45aee0772e8f64d4fcfbcda3 /base | |
parent | 9a0feec03233962ba4c767ae6622e951e28136f1 (diff) | |
download | chromium_src-f0a6f4bb3d6a54ab6435af16dd87314058a4b843.zip chromium_src-f0a6f4bb3d6a54ab6435af16dd87314058a4b843.tar.gz chromium_src-f0a6f4bb3d6a54ab6435af16dd87314058a4b843.tar.bz2 |
Add APIs to protect critical tasks on iOS.
On iOS, once an application goes into the background, it receives no CPU cycles until it is moved to foreground. At any time the OS may kill the application, but the application itself is never told or given any chance to clean up. Thus the time before going into the background is critical to ensure certain tasks have chance to complete.
Luckily, an application may ask for a little more time when going into the background by calling certain methods that let the OS know it wants to perform some short-lived work in the background. This cl provides APIs to protect critical tasks in this case.
- A class is provided to mark the beginning and end of a critical task for iOS.
- A wrapper around a task is introduced that annotates that a class is "critical". Right now we have the wrapping code for iOS and it is just a no-op for other platforms.
BUG=NONE
Review URL: https://chromiumcodereview.appspot.com/10835032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149422 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gypi | 4 | ||||
-rw-r--r-- | base/critical_closure.h | 37 | ||||
-rw-r--r-- | base/critical_closure_ios.mm | 52 | ||||
-rw-r--r-- | base/ios/scoped_critical_action.h | 48 | ||||
-rw-r--r-- | base/ios/scoped_critical_action.mm | 54 |
5 files changed, 195 insertions, 0 deletions
diff --git a/base/base.gypi b/base/base.gypi index 63362f1..4610794 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -86,6 +86,8 @@ 'compiler_specific.h', 'cpu.cc', 'cpu.h', + 'critical_closure.h', + 'critical_closure_ios.mm', 'debug/alias.cc', 'debug/alias.h', 'debug/debug_on_start_win.cc', @@ -151,6 +153,8 @@ 'hi_res_timer_manager_win.cc', 'hi_res_timer_manager.h', 'id_map.h', + 'ios/scoped_critical_action.h', + 'ios/scoped_critical_action.mm', 'json/json_file_value_serializer.cc', 'json/json_file_value_serializer.h', 'json/json_parser.cc', diff --git a/base/critical_closure.h b/base/critical_closure.h new file mode 100644 index 0000000..bb038dd --- /dev/null +++ b/base/critical_closure.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CRITICAL_CLOSURE_H_ +#define BASE_CRITICAL_CLOSURE_H_ + +#include "base/callback.h" + +namespace base { + +// Returns a closure that will continue to run for a period of time when the +// application goes to the background if possible on platforms where +// applications don't execute while backgrounded, otherwise the original task is +// returned. +// +// Example: +// file_message_loop_proxy_->PostTask( +// FROM_HERE, +// MakeCriticalClosure(base::Bind(&WriteToDiskTask, path_, data))); +// +// Note new closures might be posted in this closure. If the new closures need +// background running time, |MakeCriticalClosure| should be applied on them +// before posting. +#if defined(OS_IOS) +base::Closure MakeCriticalClosure(const base::Closure& closure); +#else +base::Closure MakeCriticalClosure(const base::Closure& closure) { + // No-op for platforms where the application does not need to acquire + // background time for closures to finish when it goes into the background. + return closure; +} +#endif // !defined(OS_IOS) + +} // namespace base + +#endif // BASE_CRITICAL_CLOSURE_H_ diff --git a/base/critical_closure_ios.mm b/base/critical_closure_ios.mm new file mode 100644 index 0000000..156612b --- /dev/null +++ b/base/critical_closure_ios.mm @@ -0,0 +1,52 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/critical_closure.h" + +#import <UIKit/UIKit.h> + +#include "base/bind.h" +#include "base/ios/scoped_critical_action.h" +#include "base/memory/ref_counted.h" + +namespace { + +// This class wraps a closure so it can continue to run for a period of time +// when the application goes to the background by using +// |base::ios::ScopedCriticalAction|. +class CriticalClosure : public base::RefCountedThreadSafe<CriticalClosure> { + public: + explicit CriticalClosure(base::Closure* closure) : closure_(closure) { + background_scope_.reset(new base::ios::ScopedCriticalAction()); + } + + void Run() { + closure_->Run(); + + background_scope_.reset(); + } + + private: + friend class base::RefCountedThreadSafe<CriticalClosure>; + + virtual ~CriticalClosure() {} + + scoped_ptr<base::Closure> closure_; + scoped_ptr<base::ios::ScopedCriticalAction> background_scope_; + + DISALLOW_COPY_AND_ASSIGN(CriticalClosure); +}; + +} // namespace + +namespace base { + +base::Closure MakeCriticalClosure(const base::Closure& closure) { + DCHECK([[UIDevice currentDevice] isMultitaskingSupported]); + scoped_refptr<CriticalClosure> critical_closure( + new CriticalClosure(new base::Closure(closure))); + return base::Bind(&CriticalClosure::Run, critical_closure.get()); +} + +} // namespace base diff --git a/base/ios/scoped_critical_action.h b/base/ios/scoped_critical_action.h new file mode 100644 index 0000000..660a83a --- /dev/null +++ b/base/ios/scoped_critical_action.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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 "base/synchronization/lock.h" + +namespace base { +namespace ios { + +// 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(); + ~ScopedCriticalAction(); + + private: + // Informs the OS that the background task has completed. + void EndBackgroundTask(); + + // |UIBackgroundTaskIdentifier| returned by + // |beginBackgroundTaskWithExpirationHandler:| when marking the beginning of + // a long-running background task. It is defined as an |unsigned int| instead + // of a |UIBackgroundTaskIdentifier| so this class can be used in .cc files. + unsigned int background_task_id_; + Lock background_task_id_lock_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCriticalAction); +}; + +} // namespace ios +} // namespace base + +#endif // BASE_IOS_SCOPED_CRITICAL_ACTION_H_ diff --git a/base/ios/scoped_critical_action.mm b/base/ios/scoped_critical_action.mm new file mode 100644 index 0000000..734c0a2 --- /dev/null +++ b/base/ios/scoped_critical_action.mm @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/ios/scoped_critical_action.h" + +#import <UIKit/UIKit.h> + +#include "base/logging.h" +#include "base/synchronization/lock.h" + +namespace base { +namespace ios { + +// This implementation calls |beginBackgroundTaskWithExpirationHandler:| when +// instantiated and |endBackgroundTask:| when destroyed, creating a scope whose +// execution will continue (temporarily) even after the app is backgrounded. +ScopedCriticalAction::ScopedCriticalAction() { + background_task_id_ = [[UIApplication sharedApplication] + beginBackgroundTaskWithExpirationHandler:^{ + DLOG(WARNING) << "Background task with id " << background_task_id_ + << " expired."; + // Note if |endBackgroundTask:| is not called for each task before time + // expires, the system kills the application. + EndBackgroundTask(); + }]; + if (background_task_id_ == UIBackgroundTaskInvalid) { + DLOG(WARNING) << + "beginBackgroundTaskWithExpirationHandler: returned an invalid ID"; + } else { + VLOG(3) << "Beginning background task with id " << background_task_id_; + } +} + +ScopedCriticalAction::~ScopedCriticalAction() { + EndBackgroundTask(); +} + +void ScopedCriticalAction::EndBackgroundTask() { + UIBackgroundTaskIdentifier task_id; + { + AutoLock lock_scope(background_task_id_lock_); + if (background_task_id_ == UIBackgroundTaskInvalid) + return; + task_id = background_task_id_; + background_task_id_ = UIBackgroundTaskInvalid; + } + + VLOG(3) << "Ending background task with id " << task_id; + [[UIApplication sharedApplication] endBackgroundTask:task_id]; +} + +} // namespace ios +} // namespace base |