Matt
Matt

Reputation: 43

Swift dispatch_async results in EXC_BAD_ACCESS error

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()

Upvotes: 4

Views: 2748

Answers (2)

Matt
Matt

Reputation: 43

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

Upvotes: 0

Graham Perks
Graham Perks

Reputation: 23400

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().

Upvotes: 1

Related Questions