I'm running some Metal code in a thread, but encountering some issues I don't fully understand. Running the following code with USE_THREAD 0
and USE_AUTORELEASEPOOL 0
works fine but setting either one to 1 results in a SIGSEGV in objc_release
:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x20)
* frame #0: 0x00007fff2020d4af libobjc.A.dylib`objc_release + 31
frame #1: 0x00007fff2022b20f libobjc.A.dylib`AutoreleasePoolPage::releaseUntil(objc_object**) + 167
frame #2: 0x00007fff2020de30 libobjc.A.dylib`objc_autoreleasePoolPop + 161
frame #3: 0x0000000100003d60 a.out`render(void*) + 896
frame #4: 0x0000000100003dd8 a.out`main + 24
frame #5: 0x00007fff20388f3d libdyld.dylib`start + 1
frame #6: 0x00007fff20388f3d libdyld.dylib`start + 1
Using the autoreleasepool I can understand since the objects are already released (since release
is called manually on them), but why does the same issue occur when the code is running inside a thread? Is this related to pthreads
specifically? Is there a "hidden" autoreleasepool somewhere I am missing?
I understand using an autoreleasepool and not releasing manually will achieve the same result but I am trying to understand what is going on here.
// clang++ main.mm -lobjc -framework Metal
#define USE_THREAD 0
#define USE_AUTORELEASEPOOL 1
#import <Metal/Metal.h>
void * render(void *) {
#if USE_AUTORELEASEPOOL
@autoreleasepool {
#else
{
#endif
NSArray<id<MTLDevice>> * devices = MTLCopyAllDevices();
id<MTLDevice> device = devices[0];
id<MTLCommandQueue> command_queue = [device newCommandQueue];
MTLTextureDescriptor * texture_descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:640 height:480 mipmapped:NO];
texture_descriptor.usage = MTLTextureUsageRenderTarget;
id<MTLTexture> texture = [device newTextureWithDescriptor:texture_descriptor];
[texture_descriptor release];
texture_descriptor = NULL;
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
MTLRenderPassDescriptor * render_pass_descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
render_pass_descriptor.colorAttachments[0].texture = texture;
id<MTLRenderCommandEncoder> render_command_encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor];
[render_pass_descriptor release];
render_pass_descriptor = NULL;
[render_command_encoder endEncoding];
[render_command_encoder release];
render_command_encoder = nil;
[command_buffer commit];
[command_buffer waitUntilCompleted];
[command_buffer release];
command_buffer = nil;
[texture release];
texture = nil;
[command_queue release];
command_queue = nil;
}
return 0;
}
#include <pthread.h>
int main() {
#if USE_THREAD
pthread_t thread;
pthread_create(&thread, NULL, render, NULL);
pthread_join(thread, NULL);
#else
render(NULL);
#endif
return 0;
}