First up, you should file a bug about this, including whatever information you think is relevant from the following. Please post your bug number, just for the record.
With your specific instructions (thanks!) I was able to reproduce the problem. I then simplified the code to get rid of a couple of potential pitfalls:
StaticString
is kinda weird, so I replaced it with [UInt8]
I printed
d
, just in case throwing away the result triggered the problem
After that the final code looked like this:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let a: [UInt8] = [1, 2, 3]
let d = a.withUnsafeBufferPointer { (b: UnsafeBufferPointer<UInt8>) -> DispatchData in
print("utf8ToDispatchData() - B")
return DispatchData(bytes: b)
}
print(d)
}
and it still reproduced the problem. Here’s the backtrace:
Thread 2 Queue : com.apple.root.default-qos (concurrent)
#0 … partial apply for thunk ()
#1 … _dispatch_call_block_and_release ()
#2 … _dispatch_client_callout ()
#3 … _dispatch_root_queue_drain ()
#4 … _dispatch_worker_thread3 ()
#5 … _pthread_wqthread ()
#6 … start_wqthread ()
Enqueued from com.apple.main-thread (Thread 1)Queue : com.apple.main-thread (serial)
#0 … _dispatch_async_f_slow ()
#1 … -[OS_dispatch_data dealloc] ()
#2 … AppDelegate.applicationDidFinishLaunching(Notification) -> () at /Users/quinn/Desktop/xxb/xxb/AppDelegate.swift:16
…
The key thing is thread 1 frame 1. If you disassemble it you see this:
(lldb) disas -n "-[OS_dispatch_data dealloc]"
libdispatch.dylib`-[OS_dispatch_data dealloc]:
0x1008812b5 <+0>: pushq %rbp
0x1008812b6 <+1>: movq %rsp, %rbp
0x1008812b9 <+4>: pushq %r15
0x1008812bb <+6>: pushq %r14
0x1008812bd <+8>: pushq %r12
0x1008812bf <+10>: pushq %rbx
0x1008812c0 <+11>: subq $0x10, %rsp
0x1008812c4 <+15>: movq %rdi, %rbx
0x1008812c7 <+18>: callq 0x10088133a ; _dispatch_data_dispose
0x1008812cc <+23>: movq 0x8(%rbx), %r12
…
It’s this call to
_dispatch_data_dispose
that triggers the crash. Happily, that code is
open source:
void
_dispatch_data_dispose(dispatch_data_t dd)
{
if (_dispatch_data_leaf(dd)) {
_dispatch_data_destroy_buffer(dd->buf, dd->size, dd->do_targetq,
dd->destructor);
} else {
size_t i;
for (i = 0; i < _dispatch_data_num_records(dd); ++i) {
_dispatch_data_release(dd->records[i].data_object);
}
free((void *)dd->buf);
}
}
I suspect (but didn’t confirm) that it’s running through the code path to
_dispatch_data_destroy_buffer
:
static void
_dispatch_data_destroy_buffer(const void* buffer, size_t size,
dispatch_queue_t queue, dispatch_block_t destructor)
{
if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE) {
free((void*)buffer);
} else if (destructor == DISPATCH_DATA_DESTRUCTOR_NONE) {
// do nothing
} else if (destructor == DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE) {
mach_vm_size_t vm_size = size;
mach_vm_address_t vm_addr = (uintptr_t)buffer;
mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
} else {
if (!queue) {
queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}
dispatch_async_f(queue, destructor, _dispatch_call_block_and_release);
}
}
which is where the
dispatch_async_f
is coming from (thread 1 frame 0). But check out this code: it special cases each of the standard ways of disposing of the dispatch data’s underlying buffer (
DISPATCH_DATA_DESTRUCTOR_FREE
and so on), so the only way to get to the
dispatch_async_f
is if the dispatch data is using a custom deallocator. Presumably that custom deallocator is being supplied by the Swift overlay.
I didn’t investigate any further because a) it’s clear that this should work, b) there’s an obvious place to look for the bug, and c) that’s in a current beta component (Xcode 8.0b5’s Swift compiler and overlay).
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"