Xcode 7 breaks dispatch_block_wait for Swift

This crashing bug appeared with beta 6; I reported it the very next day, but it's still a crasher with Xcode 7 GM (and 7.1b); targeting OS X, running on 10.10.5.

Could be a show stopper for some:


if let block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, { print("Test") })
{
  dispatch_async(dispatch_get_global_queue(qos_class_self(), 0), block)
  dispatch_block_wait(block, DISPATCH_TIME_FOREVER)
}


This will crash on line 4, at dispatch_block_wait().

A dispatch_block_notify() call would also crash there.

(The dispatch_async() call from line 3 is not necessary in either case.)


This might be a side effect of the improvement thusly described in the beta 6 release notes:

"C typedefs of block types are now imported as typealiases for Swift closures"


rdar://22451458

Yeah, Swift is really unhappy with the dispatch block API using blocks as both closures and opaque ‘handles’.

dispatch_block_wait
is failing because the block you’re passing in is not the ‘same’ block as the block you got back from
dispatch_block_create
. It’s identity has been mutated by its passage in to and out of Swift.

I fully expect we’ll fix this in Xcode but I can’t offer any predictions as to when that might happen.

I don’t see any direct way to work around this. An obvious, albeit long-winded, way is to wrap the dispatch block API.

@implementation QDispatchBlock

- (instancetype)initWithBlock:(dispatch_block_t)block {
    self = [super init];
    if (self != nil) {
        self->_block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, block);
    }
    return self;
}

- (void)dispatchAsyncToQueue:(dispatch_queue_t)queue {
    dispatch_async(dispatch_get_global_queue(qos_class_self(), 0), self.block);
}

- (void)wait {
    dispatch_block_wait(self.block, DISPATCH_TIME_FOREVER);
}

@end

and then call it from Swift as:

let block = QDispatchBlock(block: { print("Test") } )
block.dispatchAsyncToQueue(dispatch_get_global_queue(qos_class_self(), 0))
block.wait()

I kinda like this approach anyway; I’ve spent long enough working on Cocoa that dispatch APIs always seem a little weird to me.

You may be able to construct a sneakier workaround using

@convention(block)
, but I tried various options and wasn’t able to get it working.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for you elaborate and honest answer Quinn.


Have you come up with any way of handling this in Swift? It doesn't feel right to have to "go back" to ObjC for doing this.

I tried to work around this by trying to cast dispatch_block_t to AnyObject using unsafeBitCast() but we couldn't because

sizeof(AnyObject) == 8

sizeof(dispatch_block_t) == 16

which means you get a size mismatch runtime error when you try to.


I guess the real mystery here is why dispatch_block_t was changed to

public typealias dispatch_block_t = () -> Void

without a @convention(block) attribute, which should have made it an 8-byte pointer. I can imagine it was done to optimize bridging between swift closures, but unfortunately broke functionality.

Have you come up with any way of handling this in Swift?

Like john.estropia, I tried to work around that was unable to find anything that worked. The

@convention(block)
option seems like the right direction but I couldn’t get it to fit.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

This is reported fixed in Xcode 7.1b2. From the release notes:

The type

dispatch_block_t
now refers to the type
@convention(block) () -> Void
, as it did in Swift 1.2. This allows programs using
dispatch_block_create
to work properly, which was an issue in Xcode 7.0. Note that converting to a Swift closure value and back is not guaranteed to preserve the identity of a
dispatch_block_t
. (22432170)

Haven’t had a chance to try it for myself yet.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

This still seems to be broken in Xcode 7.3, and furthermore the Obj-C wrapper workaround presented above does not work.

Xcode 7 breaks dispatch_block_wait for Swift
 
 
Q