| 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..449ca44746d4884761c272ab7e6ab9b59ec460d5
|
| --- /dev/null
|
| +++ b/ios/chrome/app/UIApplication+ExitsOnSuspend.mm
|
| @@ -0,0 +1,110 @@
|
| +// 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) {
|
| + 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)
|
|
|