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 protected])

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to