dispatch_semaphore crash on deletion
dispatch_semaphore crash on deletion
- Subject: dispatch_semaphore crash on deletion
- From: Jens Alfke <email@hidden>
- Date: Mon, 28 Apr 2014 12:53:25 -0700
Looks like a dispatch_semaphore intentionally crashes (a sort of assertion failure) on deletion if its current value is less than its initial value. The crash is an EXC_BAD_INSTRUCTION in _dispatch_semaphore_dispose:
0x7fff896cb279: leaq 0x1037b(%rip), %rcx ; "BUG IN CLIENT OF LIBDISPATCH: Semaphore/group object deallocated while in use"
0x7fff896cb280: movq %rcx, -0x169deb0f(%rip) ; gCRAnnotations + 8
0x7fff896cb287: ud2
It took me a while to actually take a close look at the disassembly, but then I noticed it’s trying to convey the message "Semaphore/group object deallocated while in use”. I think that string would show up in a crash report, but Xcode & LLDB don't display it so it’s rather obscure :/
Back to the actual issue: semaphores don’t want to be disposed while their current value is less than the initial value. But I don’t see why that would be a problem; it happens naturally in some use cases. For example, from the docs, "Passing a value greater than zero [for the initial value] is useful for managing a finite pool of resources, where the pool size is equal to the value.” Which is basically what I’m doing (see below; the ‘resource’ in this case is available space in the queue.) But if this pool gets dealloced while it has resources still in it, which is fine, the semaphore will (intentionally) crash.
I’m working around this by having my object’s -dealloc method call dispatch_semaphore_signal once per remaining resource, but that’s a hack. I’d like to know if I’m misusing dispatch semaphores, or overlooking a problem in my code. I’m thinking of just tossing this out and rewriting it using an NSCondition.
—Jens
// This is a simple thread-safe limited-capacity queue
@implementation Queue
{
NSUInteger _capacity;
NSMutableArray* _array;
dispatch_queue_t _q;
dispatch_semaphore_t _availableCount, _usedCount;
BOOL _closed;
}
- (instancetype) initWithCapacity: (NSUInteger)capacity {
self = [super init];
if (self) {
_capacity = capacity;
_array = [[NSMutableArray alloc] initWithCapacity: capacity];
_q = dispatch_queue_create("Queue", DISPATCH_QUEUE_SERIAL);
_availableCount = dispatch_semaphore_create(capacity);
_usedCount = dispatch_semaphore_create(0);
}
return self;
}
- (void)dealloc {
// Here’s the nasty workaround for the dispatch_semaphore crash:
for (NSUInteger i = _array.count; i>0; i--)
dispatch_semaphore_signal(_availableCount);
}
- (BOOL) push: (id)value {
dispatch_semaphore_wait(_availableCount, DISPATCH_TIME_FOREVER);
__block BOOL success;
dispatch_sync(_q, ^{
if (_closed) {
success = NO;
} else {
[_array addObject: value];
dispatch_semaphore_signal(_usedCount);
success = YES;
}
});
return success;
}
- (id) pop {
dispatch_semaphore_wait(_usedCount, DISPATCH_TIME_FOREVER);
__block id value;
dispatch_sync(_q, ^{
if (_array.count > 0) {
value = [_array firstObject];
[_array removeObjectAtIndex: 0];
dispatch_semaphore_signal(_availableCount);
} else {
value = nil;
dispatch_semaphore_signal(_usedCount); // so next -pop call won't block
}
});
return value;
}
- (void) close {
dispatch_sync(_q, ^{
if (!_closed) {
_closed = YES;
dispatch_semaphore_signal(_usedCount); // so that -pop calls will never block
}
});
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden