Chromium Code Reviews| Index: ios/chrome/app/UIApplication+ExitsOnSuspend.mm |
| diff --git a/ios/chrome/app/UIApplication+ExitsOnSuspend.mm b/ios/chrome/app/UIApplication+ExitsOnSuspend.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..70927488bb7a72fbf5d29d5d25c670964f4db63a |
| --- /dev/null |
| +++ b/ios/chrome/app/UIApplication+ExitsOnSuspend.mm |
| @@ -0,0 +1,109 @@ |
| +// Copyright 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. |
| + |
| +#if !defined(NDEBUG) |
| + |
| +#import "ios/chrome/app/UIApplication+ExitsOnSuspend.h" |
| + |
| +#include "base/ios/block_types.h" |
| +#include "base/logging.h" |
| +#include "base/threading/thread_restrictions.h" |
| + |
| +NSString* const kExitsOnSuspend = @"EnableExitsOnSuspend"; |
| + |
| +namespace { |
| +int backgroundTasksCount = 0; |
| +UIBackgroundTaskIdentifier countTaskIdentifier = UIBackgroundTaskInvalid; |
| + |
| +// Perform a block on the main thread. Asynchronously if the thread is not the |
| +// main thread, synchronously if the thread is the main thread. |
| +void ExecuteBlockOnMainThread(ProceduralBlock block) { |
| + if ([NSThread isMainThread]) |
| + block(); |
| + else |
| + dispatch_async(dispatch_get_main_queue(), block); |
| +} |
| +} // namespace |
| + |
| +// Category defining interposing methods. These methods keep a tally of the |
| +// background tasks count. |
| +@implementation UIApplication (BackgroundTasksCounter) |
| + |
| +// Method to replace -beginBackgroundTaskWithExpirationHandler:. The original |
| +// method is called within. |
| +- (UIBackgroundTaskIdentifier) |
| + cr_interpose_beginBackgroundTaskWithExpirationHandler: |
| + (ProceduralBlock)handler { |
| + UIBackgroundTaskIdentifier identifier = |
| + [self cr_interpose_beginBackgroundTaskWithExpirationHandler:handler]; |
| + if (identifier != UIBackgroundTaskInvalid) |
|
sdefresne
2015/04/28 14:57:25
nit: please use {} as the code in if is complex
|
| + ExecuteBlockOnMainThread(^{ |
| + backgroundTasksCount++; |
| + }); |
| + return identifier; |
| +} |
| + |
| +// Method to replace -endBackgroundTask:. The original method is called within. |
| +- (void)cr_interpose_endBackgroundTask:(UIBackgroundTaskIdentifier)identifier { |
| + if (identifier != UIBackgroundTaskInvalid) |
| + ExecuteBlockOnMainThread(^{ |
| + backgroundTasksCount--; |
| + }); |
| + [self cr_interpose_endBackgroundTask:identifier]; |
| +} |
| + |
| +@end |
| + |
| +@interface UIApplication (ExitsOnSuspend_Private) |
| +// Terminate the app immediately. exit(0) is used. |
| +- (void)cr_terminateImmediately; |
| +// Terminate the app via -cr_terminateImmediately when the background tasks |
| +// count is one. The remaining task is the count observation task. |
| +- (void)cr_terminateWhenCountIsOne; |
| +@end |
| + |
| +@implementation UIApplication (ExitsOnSuspend) |
| + |
| +- (void)cr_terminateWhenDoneWithBackgroundTasks { |
| + // Add a background task for the count observation. |
| + DCHECK(countTaskIdentifier == UIBackgroundTaskInvalid); |
| + countTaskIdentifier = [self beginBackgroundTaskWithExpirationHandler:^{ |
| + // If we get to the end of the 10 minutes, exit. |
| + [self cr_terminateImmediately]; |
| + }]; |
| + |
| + [self cr_terminateWhenCountIsOne]; |
| +} |
| + |
| +- (void)cr_cancelTermination { |
| + [NSObject |
| + cancelPreviousPerformRequestsWithTarget:self |
| + selector:@selector( |
| + cr_terminateWhenCountIsOne) |
| + object:nil]; |
| + |
| + // Cancel the count observation background task. |
| + [self endBackgroundTask:countTaskIdentifier]; |
| + countTaskIdentifier = UIBackgroundTaskInvalid; |
| +} |
| + |
| +#pragma mark - Private |
| + |
| +- (void)cr_terminateImmediately { |
| + DVLOG(1) << "App exited when suspended after running background tasks."; |
| + // exit(0) will trigger at_exit handlers. Some need to be run on a IOAllowed |
| + // thread, such as file_util::MemoryMappedFile::CloseHandles(). |
| + base::ThreadRestrictions::SetIOAllowed(true); |
| + exit(0); |
| +} |
| + |
| +- (void)cr_terminateWhenCountIsOne { |
| + if (backgroundTasksCount <= 1) |
| + [self cr_terminateImmediately]; |
| + [self performSelector:_cmd withObject:nil afterDelay:1]; |
| +} |
| + |
| +@end |
| + |
| +#endif // !defined(NDEBUG) |