Task not executing at all

I have an 8th generation iPad, now updated with iPadOS 16.2 (20C65) and I have an issue that I also saw on earlier 16.* betas.

Task is not executing at all.

This is so frustrating because I have adopted async/await in my app, I support iOS 15+, everything was working fine but now that stuff inside Task { } is not executed my app seems to be broken. (Note: my main device is an iPhone 11, still on iOS 16.0, and it works fine there.)

It is also frustrating to see no other developers are complaining about this, like it happens only with my app. I have debugged with print statements and breakpoints and I can say for sure that stuff is not executing.

Does anybody have any ideas? Anything else I can try?

FB11866066

In situations such as this I like to start by establishing a baseline. If you create a new test app that runs a trivial Task, does that work?

Share and Enjoy

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

It is working now :-/ I've seen the issue before and eventually it fixed itself, I presumed from updating to a new OS beta. Now I see it works, it's the same app, same device and same OS version. I will report back if I see the issue again and do the minimal app test, thanks for replying!

New report:

  • Started failing again
  • Made minimal app with a Task and works fine
  • Problematic app: I saw that Tasks initially work but eventually stop working. I pinned it down to the use of this API:

https://developer.apple.com/documentation/mediaplayer/mpmediaitemartwork/1621736-image

Attempting to get a UIImage out of a MPMediaItemArtwork object is enough to break it (!!!) Again, not all devices and possibly not all OS versions, even not every time I launch the app on the same device. But when it fails systematically, I comment out the above API usage and the issue is gone.

Is this something you can investigate further?

If you add your MPMediaItemArtwork code to your minimal app, do things start to fail there?

Share and Enjoy

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

Nope, it works fine there.

OK, that’s a good baseline. So there’s something about your specific app that’s triggering this problem.

Are you able to run your app in the simulator? If so, does the problem reproduce there?

Share and Enjoy

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

Kind of. I can run it in the simulator but the music library is not available in the simulator, so I mock some library items. In this scenario the bit that uses MPMediaItemArtwork is not run at all. I'm not seeing issues with Tasks there.

I can run it in the simulator but the music library is not available in the simulator

Ah, OK, that won’t help then.

My best guess here is that you’ve managed to ‘consume’ all the threads in the Swift concurrency cooperative thread pool, which means that there’s no thread available to run your task. Before you continue here, I recommend that you watch WWDC 2021 Session 10254 Swift concurrency: Behind the scenes for some important background on this.

When your app gets into the problematic state, stop in the debugger and enter this command:

(lldb) thread backtrace all 

What do you see? Specifically, how many threads have a queue name like this:

(lldb) thread backtrace all 
  thread #2, queue = 'com.apple.root.user-initiated-qos.cooperative'
  …

And what do their backtraces look like?

Share and Enjoy

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

Whoa very interesting.

I have 6 of such threads and they all seem to be at the MPMediaItemArtwork.image(at:) call for different table cells. They are like this:

thread #9, queue = 'com.apple.root.user-initiated-qos.cooperative'
  frame #0: 0x00000002106bf5e0 libsystem_kernel.dylib`__ulock_wait + 8
  frame #1: 0x000000010307abdc libdispatch.dylib`_dlock_wait + 56
  frame #2: 0x000000010307a990 libdispatch.dylib`_dispatch_thread_event_wait_slow + 56
  frame #3: 0x000000010308bc54 libdispatch.dylib`__DISPATCH_WAIT_FOR_QUEUE__ + 384
  frame #4: 0x000000010308b594 libdispatch.dylib`_dispatch_sync_f_slow + 180
  frame #5: 0x00000001f6ee6940 MusicLibrary`-[ML3ArtworkConfiguration supportedSizesForMediaType:artworkType:] + 248
  frame #6: 0x00000001e37c64ec MediaPlayer`-[MPArtworkConfiguration supportedSizesForMediaType:artworkType:] + 68
  frame #7: 0x00000001e3704290 MediaPlayer`-[MPMediaLibraryArtwork validSizes] + 120
  frame #8: 0x00000001e379c814 MediaPlayer`-[MPMediaLibraryArtworkDataSource _existingRepresentationForArtworkCatalog:fromCacheOnly:] + 208
  frame #9: 0x00000001e379c9c0 MediaPlayer`-[MPMediaLibraryArtworkDataSource existingRepresentationForArtworkCatalog:] + 48
  frame #10: 0x00000001e363ac50 MediaPlayer`-[MPArtworkCatalog bestImageFromDisk] + 40
  frame #11: 0x00000001e368e6a8 MediaPlayer`__70-[MPConcreteMediaItemArtwork initWithArtworkCatalog:allowsNetworking:]_block_invoke + 296
(...)

That ulock_wait at the top seems to suggest there is a concurrency issue with this API, right? Or I might be using the wrong kind of queue! I will definitely watch that session. Thanks for the insight!!!!

Accepted Answer

All right! I seem to have fixed it with your help!

I had an async function to get image artwork to display in table view cells. Each cell would start a Task to call this function.

The function would first attempt to get the media item's embedded artwork as UIImage by calling MPMediaItemArtwork.image(at:). If this returned nil for some reason, it would then attempt to fetch it via MusicKit using a MusicCatalogResourceRequest. This is why the function is async and why the cell makes the call in a Task.

I split this function in two, one for the embedded artwork (sync) and one for MusicKit artwork (async). The cell calls the first one and only if that fails uses a Task to call the other one.

Now it works and when I pause the app I find no 'com.apple.root.user-initiated-qos.cooperative' threads.

I guess the takeaway is that that API is not thread safe but maybe you can provide some other insight given the above backtrace? Thank you!!

I’m glad you’re making progress.

but maybe you can provide some other insight given the above backtrace?

You posted the first 12 frames in that backtrace, but that’s not enough to show how your app code calls the system. Can you post an example of the full backtrace?

Share and Enjoy

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

Here's one complete thread. I tried posting the whole thing but it's beyond the post size limit.

  thread #4, queue = 'com.apple.root.user-initiated-qos.cooperative'
  frame #0: 0x00000002106bf5e0 libsystem_kernel.dylib`__ulock_wait + 8
  frame #1: 0x000000010441abdc libdispatch.dylib`_dlock_wait + 56
  frame #2: 0x000000010441a990 libdispatch.dylib`_dispatch_thread_event_wait_slow + 56
  frame #3: 0x000000010442bc54 libdispatch.dylib`__DISPATCH_WAIT_FOR_QUEUE__ + 384
  frame #4: 0x000000010442b594 libdispatch.dylib`_dispatch_sync_f_slow + 180
  frame #5: 0x00000001f6ee6940 MusicLibrary`-[ML3ArtworkConfiguration supportedSizesForMediaType:artworkType:] + 248
  frame #6: 0x00000001e37c64ec MediaPlayer`-[MPArtworkConfiguration supportedSizesForMediaType:artworkType:] + 68
  frame #7: 0x00000001e3704290 MediaPlayer`-[MPMediaLibraryArtwork validSizes] + 120
  frame #8: 0x00000001e379c814 MediaPlayer`-[MPMediaLibraryArtworkDataSource _existingRepresentationForArtworkCatalog:fromCacheOnly:] + 208
  frame #9: 0x00000001e379c9c0 MediaPlayer`-[MPMediaLibraryArtworkDataSource existingRepresentationForArtworkCatalog:] + 48
  frame #10: 0x00000001e363ac50 MediaPlayer`-[MPArtworkCatalog bestImageFromDisk] + 40
  frame #11: 0x00000001e368e6a8 MediaPlayer`__70-[MPConcreteMediaItemArtwork initWithArtworkCatalog:allowsNetworking:]_block_invoke + 296
  frame #12: 0x00000001025a7d28 VinylFetish`@nonobjc MPMediaItemArtwork.image(at:) at <compiler-generated>:0
  frame #13: 0x00000001025a7cd4 VinylFetish`protocol witness for MediaArtwork.image(at:) in conformance MPMediaItemArtwork at <compiler-generated>:0
  frame #14: 0x00000001026671bc VinylFetish`MKHelper.fetchArtwork(item=0x0000000280e331b0, imageSize=80, self=VinylFetish.MKHelper @ scalar) at MKHelper.swift:166:43
  frame #15: 0x000000010244bd18 VinylFetish`closure #1 in AlbumCell.updateFromItem(self=0x000000011e9d4800, item=0x0000000280e331b0) at AlbumCell.swift:154
  frame #16: 0x000000010244da54 VinylFetish`partial apply for closure #1 in AlbumCell.updateFromItem() at <compiler-generated>:0
  frame #17: 0x0000000102312eac VinylFetish`thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) at <compiler-generated>:0
  frame #18: 0x0000000102312ffc VinylFetish`partial apply for thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) at <compiler-generated>:0

Here's one complete thread.

One thread is not going to be enough, alas.

I tried posting the whole thing but it's beyond the post size limit.

OK. Copy the thread backtrace all output and paste it into a text file and then attach that.

Share and Enjoy

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

Hey @eskimo I'm facing the same issue that at some point Tasks are not executing anymore and here is my full stack if you could help check what's causing it to stuck:

Here is my backtrace @eskimo, thanks for taking a look.

Task not executing at all
 
 
Q