diff --git a/sources/iTermFileDescriptorMultiClient.m b/sources/iTermFileDescriptorMultiClient.m index f65388bf1d..a83bb20c17 100644 --- a/sources/iTermFileDescriptorMultiClient.m +++ b/sources/iTermFileDescriptorMultiClient.m @@ -627,12 +627,14 @@ - (void)waitForChild:(iTermFileDescriptorMultiClientChild *)child [child addWaitCallback:waitCallback]; __weak __typeof(self) weakSelf = self; - [self send:&message state:state callback:[_thread newCallbackWithBlock:^(iTermFileDescriptorMultiClientState *state, + iTermCallback *sendCallback = + [_thread newCallbackWithBlock:^(iTermFileDescriptorMultiClientState *state, NSNumber *value) { [weakSelf didWriteWaitRequestWithStatus:value.boolValue child:child state:state]; - }]]; + }]; + [self send:&message state:state callback:sendCallback]; } - (void)didWriteWaitRequestWithStatus:(BOOL)sendOK @@ -667,7 +669,7 @@ - (void)handleWait:(iTermMultiServerResponseWait)wait } else { result = [iTermResult withObject:@(wait.status)]; } - [child invokeWaitCallback:result]; + [child invokeAllWaitCallbacks:result]; } #pragma mark - Termination diff --git a/sources/iTermFileDescriptorMultiClientChild.h b/sources/iTermFileDescriptorMultiClientChild.h index d3f255fb7e..e5e3cb00d8 100644 --- a/sources/iTermFileDescriptorMultiClientChild.h +++ b/sources/iTermFileDescriptorMultiClientChild.h @@ -35,7 +35,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (void)addWaitCallback:(iTermCallback *> *)callback; -- (void)invokeWaitCallback:(iTermResult *)status; - (void)invokeAllWaitCallbacks:(iTermResult *)status; // Must be called on child's thread. diff --git a/sources/iTermFileDescriptorMultiClientChild.m b/sources/iTermFileDescriptorMultiClientChild.m index c357e768c2..2e44618f13 100644 --- a/sources/iTermFileDescriptorMultiClientChild.m +++ b/sources/iTermFileDescriptorMultiClientChild.m @@ -59,6 +59,13 @@ - (instancetype)initWithReport:(iTermMultiServerReportChild *)report return self; } +- (void)dealloc { + if (_fd >= 0) { + close(_fd); + _fd = -1; + } +} + - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p pid=%@ fd=%@>", NSStringFromClass(self.class), self, @(self.pid), @(self.fd)]; @@ -124,18 +131,6 @@ - (void)addWaitCallback:(iTermCallback *> *)callback }]; } -- (void)invokeWaitCallback:(iTermResult *)status { - __block iTermCallback *> *callback; - [_thread dispatchRecursiveSync:^(id _Nonnull state) { - callback = _callbacks.firstObject; - if (!callback) { - return; - } - [_callbacks removeObjectAtIndex:0]; - }]; - [callback invokeWithObject:status]; -} - - (void)invokeAllWaitCallbacks:(iTermResult *)status { __block NSArray *> *> *callbacks; [_thread dispatchRecursiveSync:^(id _Nonnull state) { diff --git a/sources/iTermMultiServerConnection.m b/sources/iTermMultiServerConnection.m index 9cfd973c1c..dc83748449 100644 --- a/sources/iTermMultiServerConnection.m +++ b/sources/iTermMultiServerConnection.m @@ -411,24 +411,25 @@ - (void)fileDescriptorMultiClientDidClose:(iTermFileDescriptorMultiClient *)clie - (void)fileDescriptorMultiClient:(iTermFileDescriptorMultiClient *)client childDidTerminate:(iTermFileDescriptorMultiClientChild *)child { const pid_t pid = child.pid; + iTermCallback *callback = [iTermThread.main newCallbackWithBlock:^(iTermMainThreadState *_Nonnull state, + iTermResult *result) { + [result handleObject: + ^(NSNumber * _Nonnull statusNumber) { + DLog(@"Child with pid %d terminated with status %d", pid, statusNumber.intValue); + + // Post a notification that causes the task to be removed from the task notifier. Note that + // this is usually unnecessary because the file descriptor will return 0 before we finish + // round-tripping on Wait. This is a backstop in case of the unexpected. + [[iTermMultiServerChildDidTerminateNotification notificationWithProcessID:child.pid + terminationStatus:child.terminationStatus] post]; + } error: + ^(NSError * _Nonnull error) { + DLog(@"Failed to wait on child with pid %d: %@", pid, error); + }]; + }]; [client waitForChild:child removePreemptively:NO - callback:[iTermThread.main newCallbackWithBlock:^(iTermMainThreadState *_Nonnull state, - iTermResult *result) { - [result handleObject: - ^(NSNumber * _Nonnull statusNumber) { - DLog(@"Child with pid %d terminated with status %d", pid, statusNumber.intValue); - - // Post a notification that causes the task to be removed from the task notifier. Note that - // this is usually unnecessary because the file descriptor will return 0 before we finish - // round-tripping on Wait. This is a backstop in case of the unexpected. - [[iTermMultiServerChildDidTerminateNotification notificationWithProcessID:child.pid - terminationStatus:child.terminationStatus] post]; - } error: - ^(NSError * _Nonnull error) { - DLog(@"Failed to wait on child with pid %d: %@", pid, error); - }]; - }]]; + callback:callback]; } @end diff --git a/sources/iTermMultiServerJobManager.m b/sources/iTermMultiServerJobManager.m index d5c345913f..f5f188fd6a 100644 --- a/sources/iTermMultiServerJobManager.m +++ b/sources/iTermMultiServerJobManager.m @@ -400,6 +400,9 @@ - (iTermJobManagerAttachResults)finishAttaching:(iTermMultiServerJobManagerParti results |= iTermJobManagerAttachResultsRegistered; } } + if (result.brokenPipe) { + [task brokenPipe]; + } return results; } @@ -559,6 +562,7 @@ - (void)killWithMode:(iTermJobManagerKillingMode)mode removePreemptively:YES callback:[self.thread newCallbackWithBlock:^(iTermMultiServerJobManagerState *state, iTermResult *result) { + // NOTE: killWithMode:state: must be idempotent. Be careful here. DLog(@"Preemptive wait for %d finished with result %@", pid, result); }]]; } diff --git a/sources/iTermThreadSafety.h b/sources/iTermThreadSafety.h index 57bea9b272..eba6113ae6 100644 --- a/sources/iTermThreadSafety.h +++ b/sources/iTermThreadSafety.h @@ -90,6 +90,9 @@ NS_ASSUME_NONNULL_BEGIN // Combines a block and an iTermThread to run it on. @interface iTermCallback<__contravariant CallerState, __covariant ObjectType>: NSObject @property (nonatomic, readonly) iTermThread *thread; +#if DEBUG +@property (atomic) BOOL trace; +#endif + (instancetype)onThread:(iTermThread *)thread block:(void (^)(CallerState state, ObjectType _Nullable result))block; - (void)invokeWithObject:(ObjectType _Nullable)object; diff --git a/sources/iTermThreadSafety.m b/sources/iTermThreadSafety.m index 7d4d39accf..c199c7f249 100644 --- a/sources/iTermThreadSafety.m +++ b/sources/iTermThreadSafety.m @@ -337,6 +337,9 @@ - (void)dealloc { } - (void)invokeWithObject:(id)object { +#if DEBUG + BOOL trace = self.trace; +#endif void (^block)(id, id) = [_block retain]; [self retain]; #if CHECK_DOUBLE_INVOKES @@ -348,6 +351,11 @@ - (void)invokeWithObject:(id)object { _invokeStack, stack, _creationStack, _debugInfo); _invokeStack = [stack copy]; [stack release]; +#endif +#if DEBUG + if (trace) { + NSLog(@"%@", stack); + } #endif block(state, object); [block release];