4

In my Swift project, I'm trying to process a FIFO queue (I will call it a list here to avoid confusion) in a background thread. When I use dispatch_async, it results in a EXC_BAD_ACCESS error after only some of the list has been executed. I've simplified the code as much as I could into the following playground code. In the playground, when main_thread is set to true, the code processes all 100 items in the list. If it's false, only a handful of items get processed. If the code is in a project, EXC_BAD_ACCESS occurs when main_thread is false. Obviously, I've also tried specifying a serial queue but that doesn't seem to help. What am I missing or not understanding? Thanks.

import UIKit

let main_thread = false
let serial_queue = true

class main_class {
    var this_list = list_class()


    func run(){
        for i in 1...100 {
            this_list.add_to_list( String(i) )
        }

        if main_thread {
            this_list.process_list()
        } else {
            if serial_queue {
                let my_serial_queue = dispatch_queue_create("msq", DISPATCH_QUEUE_SERIAL)
                dispatch_async(my_serial_queue){()->Void in
                    self.this_list.process_list()
                }
            } else {
                dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), { () ->Void in
                    self.this_list.process_list()
                })
            }

        }
    }
}

class list_class {
    var my_list: [String] = []

    func add_to_list( this: String ){
        my_list.append( this )
    }

    func process_list(){
        if my_list.count > 0 {
            print("removing " + my_list[0])
            remove_from_list( my_list[0] )
        }
    }

    func remove_from_list( this: String ){
        let found = my_list.indexOf( this )
        if found != nil {
            my_list.removeAtIndex( found! )
            process_list()
        }
    }
}

var blah = main_class()
blah.run()
Matt
  • 43
  • 5

2 Answers2

1

Your main thread is exiting while the background thread is still running. Main thread ends -> process gets terminated.

Set up something for main to block on while the background thread is still operating. For example, use dispatch_group_wait(); along with dispatch_group_enter() and dispatch_group_leave().

Graham Perks
  • 23,007
  • 8
  • 61
  • 83
  • I'm obviously missing some fairly core concept here :( The point of putting dispatch_async in there was so the main thread was not blocked. I'm horribly confused now. – Matt Aug 28 '16 at 02:47
  • @Matt: The point of putting `dispatch_async` in there was so the main thread was not blocked *by that asynchronous workload*. The main thread is then free to do other things, like waiting for I/O. If you have nothing else to do on the main thread, then `dispatch_async` is pointless. – Siyuan Ren Aug 28 '16 at 04:22
  • In a "normal" app main would be busy doing something else, or waiting for user input; anything but ending. Dealing with user input is crucial and important to handle promptly; so you don't want main doing long work. You're correct in moving that to a background thread. But in your sample here, there's no user input for main to deal with so that model isn't relevant. – Graham Perks Aug 28 '16 at 04:33
0

Thanks to Graham Perks answer that I needed dispatch_group_wait - I ended up with this code working as needed. I hadn't expected to need 2 async calls :( XCPlayground was only required in the playground.

import UIKit
import XCPlayground

var GlobalMainQueue: dispatch_queue_t {
    return dispatch_get_main_queue()
}
var GlobalBackgroundQueue: dispatch_queue_t {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
}

class main_class {
    var this_list = list_class()
    var done = "NO"
    func run(){
        for i in 1...100 {
            this_list.add_to_list( String(i) )
        }

        dispatch_async( GlobalBackgroundQueue ){
            let ds_group = dispatch_group_create()

            dispatch_group_enter(ds_group)
            dispatch_async(GlobalMainQueue){
                self.this_list.process_list()
                dispatch_group_leave(ds_group)
            }

            dispatch_group_wait(ds_group, DISPATCH_TIME_FOREVER)
            dispatch_async(GlobalMainQueue){
                self.done = "YES"
            }
        }

    }
}

class list_class {
    var my_list: [String] = []

    func add_to_list( this: String ){
        my_list.append( this )
    }

    func process_list(){
        if my_list.count > 0 {
            print("removing " + my_list[0])
            remove_from_list( my_list[0] )
        }
    }

    func remove_from_list( this: String ){
        let found = my_list.indexOf( this )
        if found != nil {
            my_list.removeAtIndex( found! )
            process_list()
        }
    }
}

var blah = main_class()
blah.run()
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
Matt
  • 43
  • 5
  • This does not belong to an answer. – Siyuan Ren Aug 28 '16 at 04:20
  • Why is that? Is it because the specific wording of my question was "What am I missing or not understanding"? and my answer only expands on the hints that Graham Perks provided? I'm not trying to be difficult, I just don't get why you think it doesn't belong? Should it have been put in a comment below Graham's answer? I didn't think code could/should go in comments. Thanks for helping a noob poster. – Matt Sep 02 '16 at 05:30