Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SBTProxyURLProtocol calls completionHandler in the main thread #206

Open
fermoya opened this issue Feb 27, 2024 · 4 comments
Open

SBTProxyURLProtocol calls completionHandler in the main thread #206

fermoya opened this issue Feb 27, 2024 · 4 comments

Comments

@fermoya
Copy link

fermoya commented Feb 27, 2024

Hi, I've noticed that SBTProxyURLProtocol.startLoading returns the response for the subbed request in the main thread:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(stubbingResponseTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    ....
}

Can we use dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) or any other queue that's not the main thread instead (or at least allow customization)? There are scenarios in my suite where I mock HW by using mocks within the main target whose values are customized from a UI-Test. For this, I use dummy http requests. The mocked functions can be async or sync. In order for the sync methods to work I need to block the main thread. However the test hangs indefinitely because the main thread is blocked and SBTProxyURLProtocol tries to return the data into the main queue.

I don't think the change I suggest breaks anything and I think is more accurate as you'd expect the response to come from a background thread instead of the main thread.

@tcamin
Copy link
Member

tcamin commented Feb 27, 2024

Modifying that queue would likely lead to thread-safety issues, so I would recommend against such changes. Instead of blocking the main thread with a semaphore, consider using the run loop (specifically, [NSRunLoop.mainRunLoop runUntilDate:]). While this approach isn't ideal, it could serve as a viable workaround.

@fermoya
Copy link
Author

fermoya commented Feb 27, 2024

Modifying that queue would likely lead to thread-safety issues
startLoading is already called in a different thread than it's then dispatching the response (in the main queue now) so I don't understand why changing to a different queue would introduce any issue. At the very least I think it should use dispatch_get_current_queue.

consider using the run loop
how would you do that? I do something like this:

func foo<Output>(
        for call: @escaping (@escaping (Output) -> Void) -> Any
    ) -> Output {
        let completionQueue = DispatchQueue(label: "com.test.UITest", qos: .background)

        var output: Output?
        completionQueue.async {
            call { opResult in
                output = opResult
            }
        }

        RunLoop.main.run(until: Date(timeIntervalSinceNow: 10))

        guard let output else {
            fatalError("Shouldn't time out")
        }

        return output
    }

But same difference, UI hangs for 10 seconds until it fails. Main thread is blocked so SBTProxyURLProtocol doesn't get called

@tcamin
Copy link
Member

tcamin commented Feb 27, 2024

You need to invoke the RunLoop.main.run repeatedly until you detect that your operation is completed. There are a few examples in the tunnel codebase to take a look at.

@fermoya
Copy link
Author

fermoya commented Feb 27, 2024

@tcamin from what I see, it's really easy to enter a deadlock (see here), which I think it's my case and it's not something very difficult to achieve.

I still don't understand why SBTProxyURLProtocol.startLoading has to be dispatched in the main queue. I've tried replacing the source code myself and it works just fine if a different queue is used instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants