Thomvis/BrightFutures

Apparent dealloc error after Swift 5/Xcode 10.3 upgrade

adjustnox opened this issue · 10 comments

Hello,

I am taking over a project, and after making the upgrade to Xcode 10.3, Brightfutures is crashing every time. Any help offered would be appreciated. I am attaching the Stacktrace and screenshots of the code in question.
Screenshot 2019-08-06 12 52 47
Screenshot 2019-08-06 12 52 55

  • thread #25, queue = 'NSOperationQueue 0x600000c6caa0 (QOS: UNSPECIFIED)', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x000000010c942d68 libswiftCore.dylibswift_getAssociatedTypeWitnessSlowImpl(swift::MetadataRequest, swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*) + 200 frame #1: 0x000000010c941308 libswiftCore.dylibswift_getAssociatedTypeWitness + 152
    frame #2: 0x000000010a0164c9 Kit`MutableAsyncType<>.success(value=, self=) at :0
    • frame #3: 0x000000010a0174ab KitPromise.success(value=10745 bytes, self=0x0000600000c75ee0) at Promise.swift:49:16 frame #4: 0x0000000109fd5ea5 Kitclosure #1 in URLSessionRequestHandler.handleRequest(data=10745 bytes, response=0x0000600000c427e0, error=nil, promise=0x0000600000c75ee0) at URLSessionRequestHandler.swift:20:25
      frame #5: 0x0000000109fd5fec Kitpartial apply for closure #1 in URLSessionRequestHandler.handleRequest(_:) at <compiler-generated>:0 frame #6: 0x0000000109fd6116 Kitthunk for @escaping @callee_guaranteed (@guaranteed Data?, @guaranteed NSURLResponse?, @guaranteed Error?) -> () at :0
      frame #7: 0x00000001038d72b1 DAILYLOOK__InstrumentDataTaskWithRequestCompletionHandler_block_invoke.81 + 218 frame #8: 0x00000001038d72b1 DAILYLOOK__InstrumentDataTaskWithRequestCompletionHandler_block_invoke.81 + 218
      frame #9: 0x000000010aa34178 CFNetwork__75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19 frame #10: 0x000000010aa4ac56 CFNetwork__49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 172
      frame #11: 0x0000000106d7f33e Foundation__NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7 frame #12: 0x0000000106d7f246 Foundation-[NSBlockOperation main] + 68
      frame #13: 0x0000000106d7c120 Foundation-[__NSOperationInternal _start:] + 688 frame #14: 0x0000000106d81e87 Foundation__NSOQSchedule_f + 227
      frame #15: 0x000000010d30cd7f libdispatch.dylib_dispatch_call_block_and_release + 12 frame #16: 0x000000010d30ddb5 libdispatch.dylib_dispatch_client_callout + 8
      frame #17: 0x000000010d310c95 libdispatch.dylib_dispatch_continuation_pop + 552 frame #18: 0x000000010d31008f libdispatch.dylib_dispatch_async_redirect_invoke + 849
      frame #19: 0x000000010d31e632 libdispatch.dylib_dispatch_root_queue_drain + 351 frame #20: 0x000000010d31efca libdispatch.dylib_dispatch_worker_thread2 + 130
      frame #21: 0x000000010d6f66b3 libsystem_pthread.dylib_pthread_wqthread + 583 frame #22: 0x000000010d6f63fd libsystem_pthread.dylibstart_wqthread + 13

did you have a strong reference on the Future returned by handleRequest?

Sorry to hear you're encountering issues. I can't reproduce the issue myself, it would be helpful if you could provide a crashing test case in a PR if possible.

Could you also try rewriting the method to not use a Promise (as shown at https://github.com/Thomvis/BrightFutures#providing-futures) to see if the crash still happens?

I removed all Promises (there were several, nested). Once I switched to using only futures, the crashes stopped.

Glad to hear. Would still love to have a reproducing test case to get this issue fixed, so if you have any insights you can share please do :)

I have a project I am working on to do just that. Will post back when I get it in place.

We're getting this exact same issue happening after the Swift 5/Xcode 11/BrightFutures 8.0.0 upgrade.

@adjustnox any luck on getting the reproducible project set up?

@Thomvis We tried converting the following block to Futures:

{
	private var computeFeedItemsTask:Future<[ContentItemVM], Never>?



	// FAILING METHOD
	private func computeFeedItems() -> Future<[ContentItemVM], Never> {
		...

		let existingComputation:Future<[ContentItemVM], Never> = computeFeedItemsTask ?? Future( value: [ContentItemVM]() )
		// FAILING LINE
		let existingComputationPart:String = (existingComputation.isCompleted) ? "finished" : "in progress...."

		...

		computeFeedItemsTask = existingComputation.map( computeFeedItemsQueue.context ){ _ /*(_:[FeedItemVM])*/ -> /*Future<*/[ContentItemVM]/*, NoError>*/ in
			...

			return feedItems

		}.onSuccess( DispatchQueue.main.context ){ (feedItems:[ContentItemVM]) in
			...
		}
	}
}

STACK TRACE:

#0	0x0000000112ebf94f in swift_getAssociatedTypeWitnessSlowImpl(swift::MetadataRequest, swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*) ()

#2	0x000000010ee8edaf in AsyncType.isCompleted.getter ()

#3	0x000000010eb491f6 in FeedController.computeFeedItems() at /Users/marchy/Documents/Projects/Happ/Mobile-iOS/Code/Happ.Mobile/Controllers/FeedController.swift:2090

#4	0x000000010eb2c8f5 in FeedController.viewDidLoad() at /Users/marchy/Documents/Projects/Happ/Mobile-iOS/Code/Happ.Mobile/Controllers/FeedController.swift:936

by changing the .map{...} call above to computeFeedItemsTask = Future { complete in DispatchQueue.global().async { ... }} as suggested in the docs, but it actually doesn't even run that far, since existingComputation fails beforehand when trying to access existingComputation.isCompleted when set with its initial empty array value (Future( value: [ContentItemVM]() )).

Here's some outputs of the Future's state:

(lldb) po existingComputation
Async<Result<Array<ContentItemVM>, Never>>(Optional(Swift.Result<Swift.Array<Happ_Mobile.ContentItemVM>, Swift.Never>.success([])))

(lldb) po existingComputation.isCompleted
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x0).

(lldb) po existingComputation.result
▿ Optional<Result<Array<ContentItemVM>, Never>>
  ▿ some : Result<Array<ContentItemVM>, Never>
    - success : 0 elements

Seems pretty wild given the implementation of AsyncType::isCompleted seems to be simply:

public extension AsyncType {
    var isCompleted: Bool {
        return result != nil
    }
    ...
}

Hi @Thomvis @adjustnox did you guys have a chance to look into this by any chance?

This is preventing us from upgrading to Xcode 11 / Swift 5 / iOS 13.
Any insight would be much appreciated

It was a hold up for us as well, so we ended up replacing the promises with escaping closures :/

Can't offer any help, I'm afraid.

Sorry that this is still an issue. Still not sure what the root cause could be and why this is not reproducible in a new/simple project.

Can you think of any way in which your projects are similarly special? E.g. what is the install method (CocoaPods, Carthage, etc.)

Cocoapods for us.

I can say we had several nested promises, which we switched to futures as per earlier suggestions.