I have some code where I am using dispatch_semaphore_t to signal operation completion. When the semaphore is a member variable, it does not seem to behave correctly. I will show example code that works and an example that does not seem to work:
@implementation someClass
{
dispatch_semaphore_t memberSem;
dispatch_semaphore_t* semPtr;
NSThread* worker;
BOOL taskDone;
}
- (id)init
{
// Set up the worker thread and launch it - not shown here.
memberSem= dispatch_semaphore_create(0);
semPtr= NULL;
taskDone= FALSE;
}
- (void)dealloc
{
// Clean up the worker thread as needed - not shown here.
if((NULL != semPtr) && (NULL != *semPtr))
disptatch_release(*semPtr);
dispatch_release(memberSem);
}
- (void)doSomethingArduous
{
while([self notDone]) // Does something like check a limit.
[self doIt]; // Does something like process data and increment a counter.
taskDone= TRUE; // I know this should be protected, but keeping the example simple for now.
if((NULL != semPtr) && (NULL != *semPtr))
dispatch_semaphore_signal(*semPtr); // I will put a breakpoint here, call it "SIGNAL"
}
- (BOOL)getSomethingDoneUseLocalSemaphore
{
taskDone= FALSE; // I know this should be protected, but keeping the example simple for now.
dispatch_semaphore_t localSem= dispatch_semaphore_create(0);
semPtr= &localSem;
[self performSelector:doSomethingArduous onThread:worker withObject:nil waitUntilDone:NO];
dispatch_time_t timeUp= dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(2.5 * NSEC_PER_SEC));
dispatch_semaphore_wait(localSem, timeUp);
semPtr= NULL;
dispatch_release(localSem);
// I know I could just return taskDone. The example is this way to show what the problem is.
if(taskDone) // Again with thread safety.
return TRUE;
return FALSE;
}
- (BOOL)getSomethingDoneUseMemberSemaphore
{
taskDone= FALSE; // I know this should be protected, but keeping the example simple for now.
semPtr= &memberSem; // I will put a breakpoint here, call it "START"
[self performSelector:doSomethingArduous onThread:worker withObject:nil waitUntilDone:NO];
dispatch_time_t timeUp= dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(2.5 * NSEC_PER_SEC));
dispatch_semaphore_wait(memberSem, timeUp);
semPtr= NULL;
// I know I could just return taskDone. The example is this way to show what the problem is.
if(taskDone) // Again with thread safety.
return TRUE; // I will put a breakpoint here, call it "TASK_DONE"
return FALSE; // I will put a breakpoint here, call it "TASK_NOT_DONE"
}
- (void)hereIsWhereWeBringItTogether
{
BOOL gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
BOOL gotItDoneMember= [self getSomethingDoneUseMemberSemaphore]; // Will return TRUE. I will put a breakpoint here, call it "RUN_TEST"
gotItDoneMember= [self getSomethingDoneUseMemberSemaphore]; // Will return FALSE.
}
So, given that code and the results I get/got, I put the breakpoints as described in my real code: One in the main function, one to start in the work function, one where the member semaphore is signaled, and two after the wait.
What I found was in the case where I use the member semaphore, in the first round I stop at breakpoint "RUN_TEST", run and hit breakpoint "START", run then hit breakpoint "SIGNAL", run then hit breakpoint "TASK_DONE" - all as expected.
When I continue to run, I hit breakpoint "START", run then hit breakpoint "TASK_NOT_DONE", run then hit breakpoint "SIGNAL"
That is, when I run the sequence using a semaphore that is a member, and do what looks like proper signal/wait, the second time I try to wait on that semaphore I seem to blow by and it gets signaled after I have exited the wait.
I seem to either be not managing the counting right (signal/wait pairings) or that member semaphore will not go back to an un-signaled state.
My feeling is there is something fundamental I am missing here. Any input would be appreciated.
EDIT: Ultimately what I seemed to be missing was due to my actual code being a bit more complicated. Instead of a clean return from the arduous task, there are multiple threads involved and a postNotification. I replaced the postNotification with the code in the notification handler - it sets a flag and signals the semaphore. That way any delay that might have been introduced by the notification handler is eliminated.