Swift and Threads


(Gerriet M. Denkmann) #1

This function works flawlessly in Release build:

func markAndTell( talk: Bool, number: Int)
{
  let nbrOfThreads = 8
  let step = 2
  let itemsPerThread = number * step
  let bitLimit = nbrOfThreads * itemsPerThread
  var bitfield = [Bool](count: bitLimit, repeatedValue: false)

  let queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 );
  dispatch_apply( Int(nbrOfThreads), queue,
    { ( idx: size_t) -> Void in
      
      let startIndex = itemsPerThread * Int(idx)
      let endIndex = min( startIndex + itemsPerThread, bitLimit )
      if talk { print("Thread[\(idx)] does \(startIndex) ..< \(endIndex)")}
      
      var currIndex = startIndex
      while( currIndex < endIndex )
      {
        bitfield[currIndex] = true // this might crash
        currIndex += step
      }
    }
  )
}

But it does not work in Debug builds.

“does not work” means: for !talk and any number > 0 or: talk and number ≥ 110:

malloc: *** error for object 0x101007808: incorrect checksum for freed object
- object was probably modified after being freed. *** set a breakpoint in malloc_error_break to debug

Or:
fatal error: UnsafeMutablePointer.initializeFrom non-following overlapping range

Or:
just plain wrong data in bitfield.

So: is the code ok and the compiler broken in Debug mode?
Or is the code fundamentally wrong and that it works in Release is just a fluke?
If so: how could it be fixed?

Btw.: no problems with bitfield malloced into some UnsafeMutablePointer<UInt8>.

Gerriet.


(Quinn “The Eskimo!”) #2

This one.

You’re accessing a mutable value (`bitfield`) from multiple threads. Swift does not support that, even in situations like this where you’re sure that the items are preallocated and that the threads are accessing non-overlapping values.

As to how you fix this, it kinda depends on your final use for the array. If you’re OK with the results being split into per-thread chunks, you can have each thread work on its own chunk and then pass the results back to the ‘main’ thread when it’s done. OTOH, if you need your results in one big array, things get tricky.

Share and Enjoy

···

On 13 Sep 2016, at 05:03, Gerriet M. Denkmann via swift-users <swift-users@swift.org> wrote:

Or is the code fundamentally wrong …?

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware


(Gerriet M. Denkmann) #3

Stephen J. Butler just suggested a solution (on CocoaDev) which works and is fast and uses no additional memory.

Here it is:

let nbrOfThreads = 8
let step = 2
let itemsPerThread = number * step
let bitLimit = nbrOfThreads * itemsPerThread
var bitfield = [Bool](count: bitLimit, repeatedValue: false)

let queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 );

bitfield.withUnsafeMutableBufferPointer { (inout bitfieldBuffer : UnsafeMutableBufferPointer<Bool>) -> () in
    dispatch_apply( Int(nbrOfThreads), queue ) { ( idx: size_t) -> Void in
        let startIndex = itemsPerThread * Int(idx)
        let endIndex = min( startIndex + itemsPerThread, bitLimit )
        if talk { print("Thread[\(idx)] does \(startIndex) ..< \(endIndex)") }

        var currIndex = startIndex
        while( currIndex < endIndex )
        {
            bitfieldBuffer[currIndex] = true
            currIndex += step
        }
    }
}

I hope that this is really thread-safe (testing it, I have no problems so far).

Kind regards,

Gerriet.

···

On 13 Sep 2016, at 14:49, Quinn The Eskimo! via swift-users <swift-users@swift.org> wrote:

On 13 Sep 2016, at 05:03, Gerriet M. Denkmann via swift-users <swift-users@swift.org> wrote:

Or is the code fundamentally wrong …?

This one.

You’re accessing a mutable value (`bitfield`) from multiple threads. Swift does not support that, even in situations like this where you’re sure that the items are preallocated and that the threads are accessing non-overlapping values.

As to how you fix this, it kinda depends on your final use for the array. If you’re OK with the results being split into per-thread chunks, you can have each thread work on its own chunk and then pass the results back to the ‘main’ thread when it’s done. OTOH, if you need your results in one big array, things get tricky.