2

I am making Trivia View with tvos where 4 Uilabels are added as subviews each dispatched after time on custom queue after LOGIN

function used for dispatch on queue is :

func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {

 self.TriviaDispatchQueue?.asyncAfter(deadline: .now() + seconds, qos: .default, flags: DispatchWorkItemFlags.detached, execute: {
                 DispatchQueue.main.sync {
                    completion()
                }

            })
   }

My Dispatch code is:

func showTrivia() {

  self.TriviaDispatchQueue = DispatchQueue(label: "TriviaQueue")

    self.delayWithSeconds(5, completion: {

                        self.fadeIn(forView: self.Answer0Lbl)

                        self.delayWithSeconds(2, completion: {

                            self.fadeIn(forView: self.Answer1Lbl)


                            self.delayWithSeconds(2, completion: {

                                self.fadeIn(forView: self.Answer2Lbl)
                                self.delayWithSeconds(2, completion: {

                                    self.fadeIn(forView: self.Answer3Lbl)

                                })
                            })
                        })
                    })
}

So when user moves from Trivia view to Other View in TV from top menu, then Queue is suspended to stop trivia

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    onMainThread {
                       self.TriviaClass.TriviaDispatchQueue?.suspend()
                 }
}

After that when user returns back to Trivia View then Queue is resumed:

override func viewDidAppear(_ animated: Bool) {
 super.viewDidAppear(animated)

 onMainThread {
                   self.TriviaClass.TriviaDispatchQueue?.resume()
              }
}

Everything works fine on first login but after LOGOUT and again RE-LOGIN crashes the app:Thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)

TRIVIA CLASS is shared with variable

var TriviaDispatchQueue: DispatchQueue?
Rakshit Korat
  • 1,159
  • 10
  • 23

2 Answers2

2

So I'm quite late to the party here, but I can tell you exactly what the problem is:

A dispatch_source (here a queue) gets deallocated while in suspended state. That is not allowed. (Reference counting decides TriviaClass is not needed anymore, but first deallocates the dispatch_queue.)

I can't find a link to the documentation, but one of the GCD engineers explains it here:

https://developer.apple.com/videos/play/wwdc2016/720/?time=2005 (I think you need an apple developer account to watch these, not really sure though...)

SOLUTION: I would recommend to not suspend any dispatch_source's at all. Ever. (Maybe in unit tests, but in production code you just add dangerous time bombs all over your code that are close to impossible to debug.) Add a check in the dispatched block if the block is still needed or rely on weak pointers to not execute code you dispatched earlier but don't want to run anymore.

LimeRed
  • 1,278
  • 13
  • 18
1

Is TriviaClass owned by the view controller whose code you've pasted above? If so, the problem is likely that TriviaClass is being released while TriviaDispatchQueue is suspended.

Per the docs, this will result in undefined behavior.

https://developer.apple.com/documentation/dispatch/dispatchobject/1452801-suspend

Important

It is a programmer error to release an object that is currently suspended, because suspension implies that there is still work to be done. Therefore, always balance calls to this method with a corresponding call to resume() before disposing of the object. The behavior when releasing the last reference to a dispatch object while it is in a suspended state is undefined.

Aidan O
  • 549
  • 4
  • 10