in idb_companion/Server/FBIDBServiceHandler.mm [1188:1232]
Status FBIDBServiceHandler::log(ServerContext *context, const idb::LogRequest *request, grpc::ServerWriter<idb::LogResponse> *response)
{@autoreleasepool{
// In the background, write out the log data. Prevent future writes if the client write fails.
// This will happen asynchronously with the server thread.
FBMutableFuture<NSNull *> *writingDone = FBMutableFuture.future;
id<FBDataConsumer, FBDataConsumerLifecycle, FBDataConsumerSync> consumer = [FBBlockDataConsumer synchronousDataConsumerWithBlock:^(NSData *data) {
idb::LogResponse item;
item.set_output(data.bytes, data.length);
if (writingDone.hasCompleted) {
return;
}
bool success = response->Write(item);
if (success) {
return;
}
// The client write failed, the client has gone so don't write again.
[writingDone resolveWithResult:NSNull.null];
}];
// Setup the log operation.
NSError *error = nil;
BOOL logFromCompanion = request->source() == idb::LogRequest::Source::LogRequest_Source_COMPANION;
NSArray<NSString *> *arguments = extract_string_array(request->arguments());
id<FBLogOperation> operation = [(logFromCompanion ? [_commandExecutor tail_companion_logs:consumer] : [_target tailLog:arguments consumer:consumer]) block:&error];
if (!operation) {
return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String);
}
// Poll on completion (logging happens asynchronously). This occurs when the stream is cancelled, the log operation is done, or the last write failed.
FBFuture<NSNull *> *completed = [FBFuture race:@[writingDone, operation.completed]];
while (completed.hasCompleted == NO && context->IsCancelled() == false) {
// Sleep for 200ms before polling again.
usleep(1000 * 200);
}
// Signal that we're done writing due to the operation completion or the client going away.
// This will also prevent the polling of the ClientContext.
[writingDone resolveWithResult:NSNull.null];
// Teardown the log operation now that we're done with it
FBFuture<NSNull *> *teardown = [operation.completed cancel];
[teardown block:nil];
return Status::OK;
}}