Crashes in requestSendPTPCommand(_:outData:completion:) of ImageCaptureCore framework

Hi there :)

We're in our way to make an app to can communicate with DSLR camera by using ImageCaptureCore framework for PTP communication with the camera.

In our app, we're sending some PTP commands to the camera by using requestSendPTPCommand(_:outData:completion:). This is our snipped-code to execute a PTP command.

public extension ICCameraDevice {
    func sendCommand(command: Command) async {
        do {
            print("sendCommand ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
            print("sendCommand \(command.tag()) : sendCommand Started")
            let result = try await self.requestSendPTPCommand(command.encodeCommand().commandBuffer, outData: nil)
            let (data, response) = result
            
            print("sendCommand \(command.tag()) : sendCommand Finished")
            print("sendCommand data: \(data.bytes.count)")
            print("sendCommand response: \(response.bytes.count)")

            
            if !response.bytes.isEmpty {
                command.decodeResponse(responseData: response)
            }
            
            print("sendCommand \(command.tag()) : sendCommand Finished with response code \(command.responseCode)")
            print("sendCommand ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
            
            if command.responseCode != .ok {
                isRunning = false
                errorResponseCode = command.responseCode.rawValue
                print("response error with code = \(command.responseCode)")
                return
            }
            
            let copiedData = data.deepCopy()
            command.decodeData(data: copiedData)
        } catch {
            isRunning = false
            print("Error Send Command with error: \(error.localizedDescription)")
        }
    }
}

The function sendCommand(command: Command) async is called in a while-loop in async-await way. So that it needs to wait a sent command to finish before executing another command. The looping keeps running since the device connected to the camera.

The result is, the process is running by no problem at all for several minutes, It can get camera's setting, device info, even its images. But then the problems occurred. The amount of time is random, some time it takes only 15 minutes, some time it takes 1 hour.

There are 2 problems recorded in our case:

1. The requestSendPTPCommand(_:outData:completion:) result returning empty data without throwing any error, because the error never be caught in catch block.

This is my printed result:

sendCommand +++++++++++++++++++++++++++++++++++++
sendCommand GetObjectHandlesCommand : sendCommand Started
sendCommand GetObjectHandlesCommand : sendCommand Finished
sendCommand data: 0
sendCommand response: 0
sendCommand GetObjectHandlesCommand : sendCommand Finished with response code undefined
sendCommand +++++++++++++++++++++++++++++++++++++

2. It crashes with the last message in my logger:

sendCommand +++++++++++++++++++++++++++++++++++++
sendCommand GetObjectHandlesCommand : sendCommand Started
2023-10-27 10:44:37.186768+0700 PTPHelper_Example[76486:11538353] [PTPHelper_Example]     remoteCamera ! Canon EOS 200D - Error Domain=NSCocoaErrorDomain Code=4097 “connection to service with pid 76493 created from an endpoint” UserInfo={NSDebugDescription=connection to service with pid 76493 created from an endpoint}
2023-10-27 10:44:37.187146+0700 PTPHelper_Example[76486:11538353] [PTPHelper_Example]     failureBlock ! Canon EOS 200D - Failure block was called due to a connection failure.

For crashed issue, I've tried to attach in this post. But it always failed with messaged An error occured while uploading this log. Please try again later.. So that, I uploaded it in my google drive with url: https://drive.google.com/file/d/1IvJohGwp1zSkncTWHc3430weGntciB3K/view?usp=sharing

Reproduced on iOS 16.3.1.

I've checked the stack traces of the other threads but nothing suspicious got my attention. But it might relate to this issue https://developer.apple.com/forums/thread/104576. But I can't ensure.

Any good idea of how to address these crashes shown above?

Thank you!

  • I am using the ImageCaptureCore frame to get images in the camera, but I don't know how to get them, can you tell me? thank you.

Add a Comment

Replies

Looking at your crash report (PTPHelper_Example 27-10-23 12.18.crash) I see this:

Thread 3 name:   Dispatch queue: com.apple.NSXPCConnection.user.endpoint
Thread 3 Crashed:
0   libswift_Concurrency.dylib … swift::AsyncTask::flagAsAndEnqueueOnExecutor(swift::ExecutorRef) + 276
1   libswift_Concurrency.dylib … swift::AsyncTask::flagAsAndEnqueueOnExecutor(swift::ExecutorRef) + 268
2   libswift_Concurrency.dylib … swift_continuation_throwingResumeWithError + 408
3   PTPHelper                  … UnsafeContinuation.resume(throwing:) + 100
4   PTPHelper                  … _resumeUnsafeThrowingContinuationWithError<A>(_:_:) + 76
5   PTPHelper                  … @objc completion handler block implementation for @escaping @callee_unowned @c…
6   ImageCaptureCore           … __47-[ICCameraDevice remoteCameraWithFailureBlock:]_block_invoke + 188

The code in frame 3, in the PTPHelper framework, is your code, right?

If so, it seems to be using crashing trying to resume a Swift concurrency continuation. The most common cause of such crashes is that the code isn’t following the continuation rules properly, for example:

  • It’s resuming the same continuation twice, or

  • It’s releasing a continuation without resuming it

The code above is using an unchecked continuation, which means it doesn’t diagnose these problems for you. I think that’s because the code was generated by the compiler because you called an Objective-C method as a Swift async function. The glue generated by the compiler assumes that the Objective-C method will follow the rules, and so if it doesn’t follow the rules you get mysterious problems.

To investigate this further I recommend that you avoid this auto-generated glue and write your own glue using a CheckedContinuation. That’ll should diagnose the problem precisely.

Of course it won’t actually fix the problem, but at least you know why things are failing like this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Thank you for you response @eskimo , really appreciate it!. I'll try to check the continuation by using withCheckedThrowingContinuation

Add a Comment

I am using the ImageCaptureCore frame to get images in the camera, but I don't know how to get them, can you tell me? thank you.