IluTov
IluTov

Reputation: 6862

Trouble with `dispatch_semaphore_wait()` on some devices?

I'm using the open source software TMCache. It saves expensive data to cache asynchronously. There is also a synchronous method.

It uses dispatch_semaphore_wait() to wait until the operation is over.


Source

- (id)objectForKey:(NSString *)key
{
    if (!key)
        return nil;

    __block id objectForKey = nil;

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    [self objectForKey:key block:^(TMCache *cache, NSString *key, id object) {
        objectForKey = object;
        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    #if !OS_OBJECT_USE_OBJC
    dispatch_release(semaphore);
    #endif

    return objectForKey;
}

This works fine on my machine. On a colleague's machine it does not. The program stops working at dispatch_semaphore_wait(). It's absolutely not reproducible for me.

The method above is called in tableView:viewForTableColumn:row:,
so it's executed in the main queue.

Any idea why this is happening? Must I use the method in another queue?

Upvotes: 0

Views: 3212

Answers (1)

Alex Pavlov
Alex Pavlov

Reputation: 1001

Most likely you are running out of threads. The dispatch_semaphore_signal(semaphore) , which should release the dispatch_semaphore_wait() needs to be executed in a new thread (see objectForKey:block: for details). If OS fails to dispatch that new thread, you stuck, as nobody will send you the dispatch_semaphore_signal.

How often and when it happens depends on computer/device speed, how fast you scroll the table etc. That's why you can't reproduce this issue on your computer.

The quick solution here is to keep number of threads low, by employing the same dispatch semaphore approach with timeout set to DISPATCH_TIME_NOW, as you may not block the main queue.

I would prefer changing the way TMCache.m works, though. I believe that dispatch semaphores approach is not justified in this case - gaining the code brevity (wrapping async method into a synchronous counterpart) at the expense of reliability does not seem right to me. I used to wrap synchronous methods with async ones, but not vice versa.

Here is the fix

https://github.com/rushproject/TMCache

Note that only synchronous objectForKey methods were patched.

Upvotes: 6

Related Questions