Skip to content

Commit

Permalink
Extract KWBackgroundTask into its own file
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Sharp committed Apr 30, 2015
1 parent e488490 commit 009e3ba
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 117 deletions.
38 changes: 38 additions & 0 deletions Classes/Core/KWBackgroundTask.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Licensed under the terms in License.txt
//
// Copyright 2010 Allen Ding. All rights reserved.
//

#import <Foundation/Foundation.h>

// Used to suppress compiler warnings by
// casting receivers to this protocol
@protocol NSTask_KWWarningSuppressor

- (void)setLaunchPath:(NSString *)path;
- (void)setArguments:(NSArray *)arguments;
- (void)setEnvironment:(NSDictionary *)dict;
- (void)setStandardOutput:(id)output;
- (void)setStandardError:(id)output;
- (void)launch;
- (void)waitUntilExit;

@property (readonly) int terminationStatus;

@end

@interface KWBackgroundTask : NSObject

@property (nonatomic, readonly) id<NSTask_KWWarningSuppressor> task;
@property (nonatomic, readonly) NSPipe *standardOutput;
@property (nonatomic, readonly) NSPipe *standardError;
@property (nonatomic, readonly) NSString *command;
@property (nonatomic, readonly) NSArray *arguments;
@property (nonatomic, readonly) NSData *output;

- (instancetype)initWithCommand:(NSString *)command arguments:(NSArray *)arguments;

- (void)launchAndWaitForExit;

@end
95 changes: 95 additions & 0 deletions Classes/Core/KWBackgroundTask.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// Licensed under the terms in License.txt
//
// Copyright 2010 Allen Ding. All rights reserved.
//

#import "KWBackgroundTask.h"

NSString *const NSTaskDidTerminateNotification;

static NSString *const KWTaskDidTerminateNotification = @"KWTaskDidTerminateNotification";

static NSString *const KWBackgroundTaskException = @"KWBackgroundTaskException";

@implementation KWBackgroundTask

- (instancetype)initWithCommand:(NSString *)command arguments:(NSArray *)arguments {
if (self = [super init]) {
_command = command;
_arguments = arguments;
}
return self;
}

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSTaskDidTerminateNotification object:nil];
}

- (NSString *)description {
return [NSString stringWithFormat:@"%@ `%@ %@`", [super description], self.command, [self.arguments componentsJoinedByString:@" "]];
}

// Run this task for 10 seconds
// if it times out raise an exception
- (void)launchAndWaitForExit {
CFRunLoopRef runLoop = [NSRunLoop currentRunLoop].getCFRunLoop;
__weak KWBackgroundTask *weakSelf = self;
CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 10.0, 0, 0, 0, ^(CFRunLoopTimerRef timer) {
[NSException raise:KWBackgroundTaskException format:@"Task %@ timed out", weakSelf];
CFRunLoopStop(runLoop);
});
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopDefaultMode);

id taskObserver = [[NSNotificationCenter defaultCenter] addObserverForName:KWTaskDidTerminateNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
CFRunLoopStop(runLoop);
}];

[NSThread detachNewThreadSelector:@selector(launch) toTarget:self withObject:nil];
CFRunLoopRun();
CFRunLoopRemoveTimer(runLoop, timer, kCFRunLoopDefaultMode);

[[NSNotificationCenter defaultCenter] removeObserver:taskObserver];
}

#pragma mark - Private

- (void)launch {
__block id<NSTask_KWWarningSuppressor> task = [[NSClassFromString(@"NSTask") alloc] init];
[task setEnvironment:[NSDictionary dictionary]];
[task setLaunchPath:_command];
[task setArguments:_arguments];

NSPipe *standardOutput = [NSPipe pipe];
[task setStandardOutput:standardOutput];

// Consume standard error but don't use it
NSPipe *standardError = [NSPipe pipe];
[task setStandardError:standardError];

_task = task;
_standardError = standardError;
_standardOutput = standardOutput;

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidTerminate:) name:NSTaskDidTerminateNotification object:task];

@try {
[_task launch];
} @catch (NSException *exception) {
[NSException raise:KWBackgroundTaskException format:@"Task %@ failed to launch", self];
}
CFRunLoopRun();
}

- (void)taskDidTerminate:(NSNotification *)note {
if ([_task terminationStatus] != 0) {
[NSException raise:KWBackgroundTaskException format:@"Task %@ terminated with non 0 exit code", self];
} else {
_output = [[_standardOutput fileHandleForReading] readDataToEndOfFile];
}

[[NSNotificationCenter defaultCenter] postNotificationName:KWTaskDidTerminateNotification object:self];
[NSThread exit];
}

@end
118 changes: 1 addition & 117 deletions Classes/Core/KWSymbolicator.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <pthread.h>
#import <mach-o/dyld.h>
#import "KWSymbolicator.h"
#import "KWBackgroundTask.h"

long kwCallerAddress (void){
#if !__arm__
Expand All @@ -29,123 +30,6 @@ long kwCallerAddress (void){
return 0;
}

NSString *const NSTaskDidTerminateNotification;

// Used to suppress compiler warnings by
// casting receivers to this protocol
@protocol NSTask_KWWarningSuppressor

- (void)setLaunchPath:(NSString *)path;
- (void)setArguments:(NSArray *)arguments;
- (void)setEnvironment:(NSDictionary *)dict;
- (void)setStandardOutput:(id)output;
- (void)setStandardError:(id)output;
- (void)launch;
- (void)waitUntilExit;

@property (readonly) int terminationStatus;

@end

static NSString *const KWTaskDidTerminateNotification = @"KWTaskDidTerminateNotification";

@interface KWBackgroundTask : NSObject

@property (nonatomic, readonly) id<NSTask_KWWarningSuppressor> task;
@property (nonatomic, readonly) NSPipe *standardOutput;
@property (nonatomic, readonly) NSPipe *standardError;
@property (nonatomic, readonly) NSString *command;
@property (nonatomic, readonly) NSArray *arguments;
@property (nonatomic, readonly) NSData *output;

- (void)launchAndWaitForExit;

@end

static NSString *const KWBackgroundTaskException = @"KWBackgroundTaskException";

@implementation KWBackgroundTask

- (instancetype)initWithCommand:(NSString *)command arguments:(NSArray *)arguments {
if (self = [super init]) {
_command = command;
_arguments = arguments;
}
return self;
}

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSTaskDidTerminateNotification object:nil];
}

- (NSString *)description {
return [NSString stringWithFormat:@"%@ `%@ %@`", [super description], self.command, [self.arguments componentsJoinedByString:@" "]];
}

// Run this task for 10 seconds
// if it times out raise an exception
- (void)launchAndWaitForExit {
CFRunLoopRef runLoop = [NSRunLoop currentRunLoop].getCFRunLoop;
__weak KWBackgroundTask *weakSelf = self;
CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 10.0, 0, 0, 0, ^(CFRunLoopTimerRef timer) {
[NSException raise:KWBackgroundTaskException format:@"Task %@ timed out", weakSelf];
CFRunLoopStop(runLoop);
});
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopDefaultMode);

id taskObserver = [[NSNotificationCenter defaultCenter] addObserverForName:KWTaskDidTerminateNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
CFRunLoopStop(runLoop);
}];

[NSThread detachNewThreadSelector:@selector(launch) toTarget:self withObject:nil];
CFRunLoopRun();
CFRunLoopRemoveTimer(runLoop, timer, kCFRunLoopDefaultMode);

[[NSNotificationCenter defaultCenter] removeObserver:taskObserver];
}

#pragma mark - Private

- (void)launch {
__block id<NSTask_KWWarningSuppressor> task = [[NSClassFromString(@"NSTask") alloc] init];
[task setEnvironment:[NSDictionary dictionary]];
[task setLaunchPath:_command];
[task setArguments:_arguments];

NSPipe *standardOutput = [NSPipe pipe];
[task setStandardOutput:standardOutput];

// Consume standard error but don't use it
NSPipe *standardError = [NSPipe pipe];
[task setStandardError:standardError];

_task = task;
_standardError = standardError;
_standardOutput = standardOutput;

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidTerminate:) name:NSTaskDidTerminateNotification object:task];

@try {
[_task launch];
} @catch (NSException *exception) {
[NSException raise:KWBackgroundTaskException format:@"Task %@ failed to launch", self];
}
CFRunLoopRun();
}

- (void)taskDidTerminate:(NSNotification *)note {
if ([_task terminationStatus] != 0) {
[NSException raise:KWBackgroundTaskException format:@"Task %@ terminated with non 0 exit code", self];
} else {
_output = [[_standardOutput fileHandleForReading] readDataToEndOfFile];
}

[[NSNotificationCenter defaultCenter] postNotificationName:KWTaskDidTerminateNotification object:self];
[NSThread exit];
}

@end

@implementation KWCallSite (KWSymbolication)

static void GetTestBundleExecutablePathSlide(NSString **executablePath, long *slide) {
Expand Down
12 changes: 12 additions & 0 deletions Kiwi.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,10 @@
C533F7D517462CAA000CAB02 /* KWSymbolicator.m in Sources */ = {isa = PBXBuildFile; fileRef = C533F7D217462CAA000CAB02 /* KWSymbolicator.m */; };
C533F7D617462CAA000CAB02 /* KWSymbolicator.m in Sources */ = {isa = PBXBuildFile; fileRef = C533F7D217462CAA000CAB02 /* KWSymbolicator.m */; };
C931D36E18AB2DEB005BC184 /* KWBeZeroMatcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C931D36D18AB2DEB005BC184 /* KWBeZeroMatcherTest.m */; };
CE80E4441AF2528D00D2F0D6 /* KWBackgroundTask.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80E4421AF2528D00D2F0D6 /* KWBackgroundTask.h */; };
CE80E4451AF2528D00D2F0D6 /* KWBackgroundTask.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80E4421AF2528D00D2F0D6 /* KWBackgroundTask.h */; };
CE80E4461AF2528D00D2F0D6 /* KWBackgroundTask.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80E4431AF2528D00D2F0D6 /* KWBackgroundTask.m */; };
CE80E4471AF2528D00D2F0D6 /* KWBackgroundTask.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80E4431AF2528D00D2F0D6 /* KWBackgroundTask.m */; };
DA3EAD0818A7A1D700EBBF57 /* KWSharedExampleFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3EAD0718A7A1D700EBBF57 /* KWSharedExampleFunctionalTest.m */; };
DA3EAD0A18A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3EAD0918A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m */; };
DA9C69F8190CA6EE002C4DC0 /* NSNumber_KiwiAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA9C69F7190CA6EE002C4DC0 /* NSNumber_KiwiAdditionsTests.m */; };
Expand Down Expand Up @@ -965,6 +969,8 @@
C533F7D117462CAA000CAB02 /* KWSymbolicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWSymbolicator.h; sourceTree = "<group>"; };
C533F7D217462CAA000CAB02 /* KWSymbolicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWSymbolicator.m; sourceTree = "<group>"; };
C931D36D18AB2DEB005BC184 /* KWBeZeroMatcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = KWBeZeroMatcherTest.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
CE80E4421AF2528D00D2F0D6 /* KWBackgroundTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBackgroundTask.h; sourceTree = "<group>"; };
CE80E4431AF2528D00D2F0D6 /* KWBackgroundTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBackgroundTask.m; sourceTree = "<group>"; };
DA084D3F17E3804200592D5A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
DA084D4217E3804200592D5A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
DA3EAD0718A7A1D700EBBF57 /* KWSharedExampleFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWSharedExampleFunctionalTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1130,6 +1136,8 @@
9F982C3D16A802920030A0B1 /* KiwiMacros.h */,
9F982C4316A802920030A0B1 /* KWAny.h */,
9F982C4416A802920030A0B1 /* KWAny.m */,
CE80E4421AF2528D00D2F0D6 /* KWBackgroundTask.h */,
CE80E4431AF2528D00D2F0D6 /* KWBackgroundTask.m */,
9F982C6116A802920030A0B1 /* KWBlock.h */,
9F982C6216A802920030A0B1 /* KWBlock.m */,
9F982C6716A802920030A0B1 /* KWCallSite.h */,
Expand Down Expand Up @@ -1572,6 +1580,7 @@
9F982DC316A802920030A0B1 /* KWObjCUtilities.h in Headers */,
9F982DC716A802920030A0B1 /* KWPendingNode.h in Headers */,
9F982DCB16A802920030A0B1 /* KWProbe.h in Headers */,
CE80E4451AF2528D00D2F0D6 /* KWBackgroundTask.h in Headers */,
9F982DCD16A802920030A0B1 /* KWProbePoller.h in Headers */,
9F982DD516A802920030A0B1 /* KWReceiveMatcher.h in Headers */,
9F982DD916A802920030A0B1 /* KWRegisterMatchersNode.h in Headers */,
Expand Down Expand Up @@ -1655,6 +1664,7 @@
9F982D6E16A802920030A0B1 /* KWExpectationType.h in Headers */,
9F982D7016A802920030A0B1 /* KWFailure.h in Headers */,
9F982D7416A802920030A0B1 /* KWFormatter.h in Headers */,
CE80E4441AF2528D00D2F0D6 /* KWBackgroundTask.h in Headers */,
9F982D7816A802920030A0B1 /* KWFutureObject.h in Headers */,
9F982D7C16A802920030A0B1 /* KWGenericMatcher.h in Headers */,
9F982D8016A802920030A0B1 /* KWGenericMatchingAdditions.h in Headers */,
Expand Down Expand Up @@ -1979,6 +1989,7 @@
9F982D3116A802920030A0B1 /* KWBlock.m in Sources */,
9F982D3516A802920030A0B1 /* KWBlockNode.m in Sources */,
9F982D3916A802920030A0B1 /* KWBlockRaiseMatcher.m in Sources */,
CE80E4471AF2528D00D2F0D6 /* KWBackgroundTask.m in Sources */,
9F982D3D16A802920030A0B1 /* KWCallSite.m in Sources */,
9F982D4116A802920030A0B1 /* KWCaptureSpy.m in Sources */,
9F982D4516A802920030A0B1 /* KWConformToProtocolMatcher.m in Sources */,
Expand Down Expand Up @@ -2070,6 +2081,7 @@
9F982D3016A802920030A0B1 /* KWBlock.m in Sources */,
9F982D3416A802920030A0B1 /* KWBlockNode.m in Sources */,
9F982D3816A802920030A0B1 /* KWBlockRaiseMatcher.m in Sources */,
CE80E4461AF2528D00D2F0D6 /* KWBackgroundTask.m in Sources */,
9F982D3C16A802920030A0B1 /* KWCallSite.m in Sources */,
9F982D4016A802920030A0B1 /* KWCaptureSpy.m in Sources */,
9F982D4416A802920030A0B1 /* KWConformToProtocolMatcher.m in Sources */,
Expand Down

0 comments on commit 009e3ba

Please sign in to comment.