diff --git a/ChangeLog b/ChangeLog index c486f9e77..d775aa557 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2016-06-26 Richard Frith-Macdonald + + * Headers/Foundation/NSTask.h: + * Source/NSTask.m: + * Tests/base/NSTask/general.m: + * Tests/base/NSTask/notify.m: + Add -terminationReason method from OSX10.5 + 2016-06-25 Richard Frith-Macdonald * Source/NSMessagePort.m: diff --git a/Headers/Foundation/NSTask.h b/Headers/Foundation/NSTask.h index e049024f4..88c4fb03c 100644 --- a/Headers/Foundation/NSTask.h +++ b/Headers/Foundation/NSTask.h @@ -36,6 +36,14 @@ extern "C" { #endif +#if OS_API_VERSION(MAC_OS_X_VERSION_10_5,GS_API_LATEST) +enum { + NSTaskTerminationReasonExit = 1, + NSTaskTerminationReasonUncaughtSignal = 2 +}; +typedef NSInteger NSTaskTerminationReason; +#endif + @interface NSTask : NSObject { #if GS_EXPOSE(NSTask) @@ -53,6 +61,7 @@ extern "C" { BOOL _hasTerminated; BOOL _hasCollected; BOOL _hasNotified; + NSTaskTerminationReason _terminationReason; #endif #if GS_NONFRAGILE #else @@ -97,6 +106,9 @@ extern "C" { #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) - (int) processIdentifier; #endif +#if OS_API_VERSION(MAC_OS_X_VERSION_10_5,GS_API_LATEST) +- (NSTaskTerminationReason) terminationReason; +#endif - (int) terminationStatus; /* diff --git a/Source/NSTask.m b/Source/NSTask.m index 759c33e9d..800b337ed 100644 --- a/Source/NSTask.m +++ b/Source/NSTask.m @@ -118,6 +118,10 @@ static void handleSignal(int sig) } #ifdef _WIN32 +/* We use exit code 10 to denote a process termination. + * Windows does nt have an exit code to denote termination this way. + */ +#define WIN_SIGNALLED 10 @interface NSConcreteWindowsTask : NSTask { @public @@ -222,7 +226,7 @@ pty_slave(const char* name) @interface NSTask (Private) - (NSString *) _fullLaunchPath; - (void) _collectChild; -- (void) _terminatedChild: (int)status; +- (void) _terminatedChild: (int)status reason: (NSTaskTerminationReason)reason; @end @@ -673,6 +677,30 @@ pty_slave(const char* name) #endif } +/** + * Returns the termination reason of the task.
+ * If the task has not completed running, raises an + * NSInvalidArgumentException. + */ +- (NSTaskTerminationReason) terminationReason +{ + if (_hasLaunched == NO) + { + [NSException raise: NSInvalidArgumentException + format: @"NSTask - task has not yet launched"]; + } + if (_hasCollected == NO) + { + [self _collectChild]; + } + if (_hasTerminated == NO) + { + [NSException raise: NSInvalidArgumentException + format: @"NSTask - task has not yet terminated"]; + } + return _terminationReason; +} + /** * Returns the termination status of the task.
* If the task has not completed running, raises an @@ -876,13 +904,14 @@ pty_slave(const char* name) [self subclassResponsibility: _cmd]; } -- (void) _terminatedChild: (int)status +- (void) _terminatedChild: (int)status reason: (NSTaskTerminationReason)reason { [tasksLock lock]; IF_NO_GC([[self retain] autorelease];) NSMapRemove(activeTasks, (void*)(intptr_t)_taskId); [tasksLock unlock]; _terminationStatus = status; + _terminationReason = reason; _hasCollected = YES; _hasTerminated = YES; if (_hasNotified == NO) @@ -930,7 +959,9 @@ GSPrivateCheckTasks() { if (eCode != STILL_ACTIVE) { - [t _terminatedChild: eCode]; + [t _terminatedChild: eCode reason: (WIN_SIGNALLED == eCode) + ? NSTaskTerminationReasonUncaughtSignal + : NSTaskTerminationReasonExit]; found = YES; } } @@ -972,8 +1003,11 @@ GSPrivateCheckTasks() return; } + /* We use exit code 10 to denote a process termination. + * Windows does nt have an exit code to denote termination this way. + */ _hasTerminated = YES; - TerminateProcess(procInfo.hProcess, 10); + TerminateProcess(procInfo.hProcess, WIN_SIGNALLED); } @@ -1307,7 +1341,9 @@ quotedFromString(NSString *aString) } else if (eCode != STILL_ACTIVE) { - [self _terminatedChild: eCode]; + [self _terminatedChild: eCode reason: (WIN_SIGNALLED == eCode) + ? NSTaskTerminationReasonUncaughtSignal + : NSTaskTerminationReasonExit]; } } } @@ -1364,7 +1400,8 @@ GSPrivateCheckTasks() NSLog(@"waitpid %d, exit status = %d", result, status); #endif - [t _terminatedChild: WEXITSTATUS(status)]; + [t _terminatedChild: WEXITSTATUS(status) + reason: NSTaskTerminationReasonExit]; found = YES; } else if (WIFSIGNALED(status)) @@ -1373,7 +1410,8 @@ GSPrivateCheckTasks() NSLog(@"waitpid %d, termination status = %d", result, status); #endif - [t _terminatedChild: WTERMSIG(status)]; + [t _terminatedChild: WTERMSIG(status) + reason: NSTaskTerminationReasonUncaughtSignal]; found = YES; } else @@ -1669,7 +1707,8 @@ GSPrivateCheckTasks() NSLog(@"waitpid %d, exit status = %d", result, status); #endif - [self _terminatedChild: WEXITSTATUS(status)]; + [self _terminatedChild: WEXITSTATUS(status) + reason: NSTaskTerminationReasonExit]; } else if (WIFSIGNALED(status)) { @@ -1677,7 +1716,8 @@ GSPrivateCheckTasks() NSLog(@"waitpid %d, termination status = %d", result, status); #endif - [self _terminatedChild: WTERMSIG(status)]; + [self _terminatedChild: WTERMSIG(status) + reason: NSTaskTerminationReasonUncaughtSignal]; } else { diff --git a/Tests/base/NSTask/general.m b/Tests/base/NSTask/general.m index b91cfbe0e..80b882e6e 100644 --- a/Tests/base/NSTask/general.m +++ b/Tests/base/NSTask/general.m @@ -49,6 +49,8 @@ int main() [task setArguments: args]; [task launch]; [task waitUntilExit]; + PASS([task terminationReason] == NSTaskTerminationReasonExit, + "termination reason for normal exit works"); [arp release]; arp = nil; return 0; diff --git a/Tests/base/NSTask/notify.m b/Tests/base/NSTask/notify.m index 79fb6e9a4..a4ddf41ec 100644 --- a/Tests/base/NSTask/notify.m +++ b/Tests/base/NSTask/notify.m @@ -6,7 +6,7 @@ #import #import -#import "ObjectTesting.h" +#import "ObjectTesting.h" @interface TaskHandler : NSObject { @@ -16,74 +16,81 @@ @implementation TaskHandler -static BOOL taskTerminationNotificationReceived; +static BOOL taskTerminationNotificationReceived; - (void) setLaunchPath: (NSString*)s { ASSIGNCOPY(path, s); } -- (void) taskDidTerminate: (NSNotification *)notification -{ - NSLog(@"Received NSTaskDidTerminateNotification %@", notification); - taskTerminationNotificationReceived = YES; -} +- (void) taskDidTerminate: (NSNotification *)notification +{ + NSLog(@"Received NSTaskDidTerminateNotification %@", notification); + taskTerminationNotificationReceived = YES; +} -- (void) testNSTaskNotifications -{ - NSDate *deadline; - BOOL earlyTermination = NO; +- (void) testNSTaskNotifications +{ + NSDate *deadline; + BOOL earlyTermination = NO; for (;;) - { - NSTask *task = [NSTask new]; + { + NSTask *task = [NSTask new]; [task setLaunchPath: path]; [task setArguments: [NSArray arrayWithObjects: - @"-c", @"echo Child starting; sleep 10; echo Child exiting", nil]]; - taskTerminationNotificationReceived = NO; + @"-c", @"echo Child starting; sleep 10; echo Child exiting", nil]]; + taskTerminationNotificationReceived = NO; [[NSNotificationCenter defaultCenter] - addObserver: self - selector: @selector(taskDidTerminate:) - name: NSTaskDidTerminateNotification - object: task]; - [task launch]; - NSLog(@"Launched pid %d", [task processIdentifier]); + addObserver: self + selector: @selector(taskDidTerminate:) + name: NSTaskDidTerminateNotification + object: task]; + [task launch]; + NSLog(@"Launched pid %d", [task processIdentifier]); if (earlyTermination) - { - NSLog(@"Running run loop for 5 seconds"); - deadline = [NSDate dateWithTimeIntervalSinceNow:5.0]; - while ([deadline timeIntervalSinceNow] > 0.0) + { + NSLog(@"Running run loop for 5 seconds"); + deadline = [NSDate dateWithTimeIntervalSinceNow:5.0]; + while ([deadline timeIntervalSinceNow] > 0.0 + && !taskTerminationNotificationReceived) { [[NSRunLoop currentRunLoop] - runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]]; - NSLog(@"Run loop finished, will now call -[NSTask terminate]"); - [task terminate]; - NSLog(@"Terminate returned, waiting for termination"); - [task waitUntilExit]; + runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]]; + NSLog(@"Run loop finished, will now call -[NSTask terminate]"); + [task terminate]; + NSLog(@"Terminate returned, waiting for termination"); + [task waitUntilExit]; + PASS([task terminationReason] + == NSTaskTerminationReasonUncaughtSignal, + "termination reason for signal exit works"); } } else - { - NSLog(@"Running run loop for 15 seconds"); - deadline = [NSDate dateWithTimeIntervalSinceNow: 15.0]; + { + NSLog(@"Running run loop for 15 seconds"); + deadline = [NSDate dateWithTimeIntervalSinceNow: 15.0]; while ([deadline timeIntervalSinceNow] > 0.0 - && !taskTerminationNotificationReceived) + && !taskTerminationNotificationReceived) { [[NSRunLoop currentRunLoop] - runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]]; + runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]]; } - } - [task release]; + PASS([task terminationReason] + == NSTaskTerminationReasonExit, + "termination reason for normal exit works"); + } + [task release]; NSAssert(taskTerminationNotificationReceived, - @"termination notification not received"); + @"termination notification not received"); [[NSNotificationCenter defaultCenter] - removeObserver: self name: NSTaskDidTerminateNotification object: nil]; - if (earlyTermination) - break; - earlyTermination = YES; - } -} + removeObserver: self name: NSTaskDidTerminateNotification object: nil]; + if (earlyTermination) + break; + earlyTermination = YES; + } +} @end