3

I have a problem with synchronization using posix semaphores. Here's the code I wrote

sem_t *sem1, *sem2;

void* test1() {
    int rv;

    while(1) {
        rv = sem_wait(sem1);
        printf("sem_wait(&sem1) = %d\n",rv);
        printf("test1\n");

        rv = sem_post(sem2);
        printf("sem_post(&sem2) = %d\n\n",rv);
    }
    return NULL;
}

void* test2() {

    while(1) {

        rv = sem_wait(sem2);
        printf("sem_wait(&sem2) = %d\n",rv);
        printf("test2\n");

        rv = sem_post(sem1);
        printf("sem_post(&sem1) = %d\n\n",rv);
    }
    return NULL;
}

int main (int argc, const char * argv[]) {

pthread_t t1,t2;

sem1 = sem_open("sem1", O_CREAT | O_EXCL, 0644, 1);

if (sem1 == SEM_FAILED){
    perror("sem1_init");
    sem_close(sem1);
    return;
}   

sem2 = sem_open("sem2", O_CREAT | O_EXCL, 0644, 0);
if (sem2 == SEM_FAILED){
    perror("sem2_init");
    sem_close(sem1);
    sem_close(sem2);
    return;
}

pthread_create(&t1, NULL, &test1, NULL);
pthread_create(&t2, NULL, &test2, NULL);

pthread_join(t1, NULL);
pthread_join(t2, NULL);

return 0;
}

What I expected is that, since I initialized sem1 to 1 and sem2 to 0, test1 would be the first function to run, and then they will alternate till the end of time.
Instead it's not working, I mean in the log I read many times "test1", many times "test2", for a while alternated and then again without any order. Can someone tell me where is my error?

ps. don't know if it can be useful but I'm running MacOSX 10.6.7


EDIT: I updated my code, removing all calls to sem_init and sem_getvalue (both not present under MacOS), using sem_open to init the semaphores and it seems to work.
I found, however, a strange problem, probably because of my miscomprehension of sem_open call: every time I restart the program if I reuse the same name for semaphores I get the error File exists. How can I force to reuse the same identifier?

Besides. according to man pages, sem_wait should return 0 if successful, -1 if not. What does it mean if I receive 1 (this happen always in test2 )?

Manlio
  • 10,768
  • 9
  • 50
  • 79
  • 5
    Firstly, compile the program with warnings enabled. Also, check the return value of all your calls for errors. Then, see if e.g this http://stackoverflow.com/questions/1413785/sem-init-on-os-x and http://stackoverflow.com/questions/4136181/program-using-semaphores-runs-fine-on-linux-unexpected-results-on-mac-osx is your problem. – nos Jun 03 '11 at 17:57
  • Seconding @nos here: **Check for errors after all library calls.** Just because your code compiles, that does not mean it will run without errors; ignoring error information is inviting a shot in the foot. – Jeremy W. Sherman Jun 03 '11 at 18:10
  • 2
    @nos Wow, if I could give a -1 to Apple for not implementing this, I would. :P (And of course, I agree: always check for errors...) – asveikau Jun 04 '11 at 00:08
  • 1
    asveikau was right, it seems there are not `sem_init` and `sem_getvalue` under MacOS... I expected at least a warning for this, instead I even receive code completion... Anyway, I edited the original post with some corrections. Not sure this is a real solution to the problem but it seems to work. Check if some of you knows the answer to the question I added at the end of the post – Manlio Jun 04 '11 at 01:13
  • I think all you have to do is remove the O_EXCL argument from your call to sem_open. That flag explicitly tells it to fail if the semaphore name exists. – Summit Guy Jun 04 '11 at 02:18
  • 1
    @Summit Guy: If he removes `O_EXCL`, then it will open the existing semaphore objects with indeterminate starting values. – Adam Rosenfield Jun 04 '11 at 04:12
  • 1
    @Saphrosit: Your updated code works fine for me (on the first run). Named semaphores persist in the kernel after your program dies. You need to call `sem_unlink` to fully remove the semaphore. If your program is only terminated by signals such as Ctrl-C (SIGINT), then you need to set a signal handler with `signal(2)` or `sigaction(2)` that will call `sem_unlink` before exiting. – Adam Rosenfield Jun 04 '11 at 04:16

1 Answers1

1

If what you want is for the two functions to alternate, you should just use a single semaphore.

When one of your threads gets scheduled, for example t1, it locks sem1 and then keeps it and runs in a while loop until it gets interrupted. Because it's not doing a sem_post(&sem1), it doesn't give control to the other thread.


Update: Looking at this again, this code should work. I think the sem_init not being implemented on OSX is probably your issue.


Update: To answer your question about sem_wait returning 1, it looks like the real error value is returned in errno. If the return value != 0, then check there. My shot in the dark guess would be EINTR is what you are seeing.

Summit Guy
  • 305
  • 3
  • 7
  • Absolutely not sure about that. When t1 starts it calls `sem_wait(&sem1)`. `sem1` was initialized to 1, so `sem1`'s value is set to 0 and t1 enter the crical section (or simply pass over the `sem_wait` call). It prints "test1", then `sem_post(&sem2)`, setting `sem2`'s value to 1 (so that t2 may enter in the critical section if it was waiting at the semaphore, else it'll enter when it reaches that point). When t1 starts again the cycle it'll find `sem1` to 0 ('cause no `sem_post` where called on `sem1`) and it starts wait on the semaphore queue until t2 unlock it. At least in theory. – Manlio Jun 04 '11 at 00:53