I have code in which I use dispatch_semaphore_t to complete an input operation. When a semaphore is a member variable, it does not seem to behave correctly. I will show an example of code that works, and an example that doesn't 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 this code and the results that I get / get, I put breakpoints as described in my real code: one in the main function, one to run in the work function, one where the member semaphore is signaled, and two after waiting.
What I found was when I use a semaphore member, in the first round I stop at the breakpoint "RUN_TEST", start and hit the breakpoint "START", start, then hit the breakpoint "SIGNAL", TASK_DONE "- all as expected.
When I continue to work, I press the "START" breakpoint, then start, hit the "TASK_NOT_DONE" breakpoint, start and hit the "SIGNAL" breakpoint
That is, when I run a sequence using a semaphore that is a member, and do what looks like the correct signal / wait, the second time I try to wait on this semaphore, I seem to explode and it receives a signal after that as I came out of expectation.
It seems that I either do not control the counting rule (pairs of signals / expectations), or the semaphore of the members does not return to the state without signaling.
I feel that there is something fundamental here. Any input would be appreciated.
EDIT: Ultimately, what seemed to me missing was due to the fact that my actual code was a bit more complicated. Instead of a pure return from a difficult task, there are several threads and postNotification. I replaced postNotification with code in the notification handler - it sets a flag and signals a semaphore. This eliminates any delay that might have been sent by the notification handler.