This question requires that two semaphores be used, one as a mutex, and one as a counting semaphore, and that the pair be used to simulate the interaction between students and a teacher's assistant.
I have been able to utilize the binary semaphore easily enough, however I cannot seem to find many examples that show the use of a counting semaphore, so I am quite sure I have it wrong, which is causing my code to not execute properly.
My code is below
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <sys/types.h>
void *taThread();
void *student();
sem_t taMutex;
sem_t semaphore;
int main()
{
pthread_t tid1;
srand(time(NULL));
sem_init(&taMutex,0,1);
sem_init(&semaphore,1,3);
pthread_create(&tid1, NULL, &taThread, NULL);
pthread_join(tid1, NULL);
return 0;
}
void *taThread()
{
pthread_t tid2[10];
int it = 0;
printf("Teacher's Assistant taking a nap.\n");
for (it = 0; it < 10; it ++)
{
pthread_create(&tid2[it], NULL, &student, NULL);
}
for (it = 0; it < 10; it ++)
{
pthread_join(tid2[it], NULL);
}
}
void *student()
{
int xTime;
xTime = rand() % 10 + 1;
if (sem_wait(&taMutex) == 0)
{
printf("Student has awakened TA and is getting help. This will take %d minutes.\n", xTime);
sleep(xTime);
sem_post(&taMutex);
}
else if (sem_wait(&semaphore) > 2 )
{
printf("Student will return at another time.\n");
}
else
{
sem_wait(&semaphore);
printf("Student is working on their assignment until TA becomes available.\n");
sem_wait(&taMutex);
sem_post(&semaphore);
printf("Student is entering the TA's office. This will take %d minutes", xTime);
sleep(xTime);
sem_post(&taMutex);
}
}
My main question is: how can I get the threads to poll the counting semaphore concurrently?
I am trying to get a backup, with some students being forced to leave (or exit the thread) unhelped, with others waiting in the semaphore. Any assistance is appreciated, and any clarifications will be offered.
I'm not sure if your class / teacher wants to make special distinctions here, but fundamentally, a binary semaphore is mostly equivalent to a copunting semaphore initialized to 1,1 so that when you count it down ("P") to zero it becomes "busy" (locked like a mutex) and when you release it ("V") it counts up to its maximum of 1 and it's now "un-busy" (unlocked). A counting semaphore typically starts with a higher initial value, typically for counting some resource (like 3 available chairs in a room), so that when you count it down there may still be some left. When you're done using the counted resource (e.g., when the "student" leaves the "TA's office") you count it back up ("V").
With POSIX semaphores, the call:
sem_init(&semaphore,1,3);
says that this is a process-shared semaphore (2nd argument nonzero), rather than a thread-shared semaphore; you don't seem to need that, and I'm not sure if some systems might give you an error—a failing sem_init call, that is—if &semaphore is not in a process-shared region. You should be able to just use 0, 3. Otherwise, this is fine: it's saying, in effect, that there are three "unoccupied chairs" in the "office".
Other than that, you'll need to either use sem_trywait (as #pilcrow suggested), sem_timedwait, or a signal to interrupt the sem_wait call (e.g., SIGALRM), in order to have some student who tries to get a "seat" in the "office" to discover that he can't get one within some time-period. Just calling sem_wait means "wait until there's an unoccupied chair, even if that takes arbitrarily long". Only two things stop this potentially-infinite wait: either a chair becomes available, or a signal interrupts the call.
(The return value from the various sem_* functions tells you whether you "got" the "chair" you were waiting for. sem_wait waits "forever", sem_trywait waits not-at-all, and sem_timedwait waits until you "get the chair" or the clock runs out, whichever occurs first.)
1The real difference between a true binary semaphore and a counting semaphore is that a binary semaphore doesn't offer you the ability to count. It's either acquired (and an acquire will block), or not-acquired (and an acquire will succeed and block other acquires). Various implementations may consider releasing an already-released binary semaphore a no-op or an error (e.g., runtime panic). POSIX just doesn't offer binary semaphores at all: sem_init initializes a counting semaphore and it's your responsibility to set it to 1, and not over-increment it by releasing when it's already released. See also comments below.
Related
I just started learning about threads, mutexes and condition variables and I have this code:
#include <pthread.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mutex;
static pthread_mutexattr_t attr;
volatile int x = 0;
void *thread_one_function (void *dummy) {
printf("In func one.");
while (true) {
pthread_mutex_lock (&mutex);
x = rand ();
pthread_cond_signal (&cond);
pthread_mutex_unlock (&mutex);
}
}
void *thread_two_function (void *dummy) {
printf("In func two.");
pthread_mutex_lock (&mutex);
while (x != 10) {
pthread_cond_wait (&cond, &mutex);
}
printf ("%d\n", x);
pthread_mutex_unlock (&mutex);
printf ("%d\n", x);
}
int main (void){
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init (&mutex, &attr);
pthread_t one, two;
pthread_create(&one,NULL,thread_one_function,NULL);
pthread_create(&two,NULL,thread_two_function,NULL);
//pthread_join(one,NULL); //with this program will not end.
//pthread_join(two,NULL);
return 0;
}
I compile it as gcc prog.c -lpthread -o a.exe
And I am getting no output. Not even that my threads get into those two functions...
What am I doing wrong ? My code is created as a combination from multiple documentations.
Thanks for any help.
The most likely reason for you getting no output is that the main thread returns from the initial call to main() immediately after starting the threads. That terminates the program, including the newly-created threads.
It does not help that neither do the threads' initial messages end with newlines nor do the threads flush the standard output after the printf calls. The standard output is line-buffered when connected to a terminal, so actual delivery of those messages to the terminal will be deferred in your example.
The main thread should join the other two before it terminates. Your code comments indicate that you had other issues when you did that, but going without joining those threads is not a correct solution if in fact you want the additional threads to run to completion. The problem is not joining the threads, but rather that the threads are not terminating.
There's a pretty clear reason why the thread_one_function thread does not terminate, which I am sure you will recognize if you look for it. But what about the thread_two_function thread? The reasons for that taking a long time to terminate are at least twofold:
the more obvious one is the one hinted in #dbush's comment on the question. Consider the range of the rand() function: how many calls do you think it might take before it returns the one, specific result that thread_two_function is looking for?
moreover, how often is thread_two_function even going to get a chance to check? Consider: thread_one_function runs a tight loop in which it acquires the mutex at the top and releases it at the bottom. Pthreads mutexes do not implement any kind of fairness policy, so when thread_one_function loops around and tries to reacquire the mutex immediately after releasing it, it has a very high probability of succeeding immediately, even though thread_two_function may be trying to acquire the mutex too.
It would make more sense for your threads to take turns. That is, after generating a new number, thread_one_function would wait for thread_two_function to check it before generating another. There are several ways you could implement that. Some involve using the same condition variable in thread_one_function that you do in thread_two_function. (Details left as an exercise.) I would suggest also providing a means by which thread_two_function can tell thread_one_function that it doesn't need to generate any more numbers, and should instead terminate.
Finally, do be aware that volatile has no particular place here. It is not useful or appropriate for synchronization, whereas the mutex alone is entirely sufficient for that. It's not exactly wrong to declare x volatile, but it's extraneous.
I have a program in which multiple threads are in a loop where they acquire a binary semaphore and then increase a global counter. However, by printing out the thread IDs, I notice that only one thread ever acquires the semaphore. Here's my MRE:
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM_THREADS 10
#define MAX_COUNTER 100
struct threadCtx {
sem_t sem;
unsigned int counter;
};
static void *
threadFunc(void *args)
{
struct threadCtx *ctx = args;
pthread_t self;
bool done = false;
self = pthread_self();
while (!done) {
sem_wait(&ctx->sem);
if ( ctx->counter == MAX_COUNTER ) {
done = true;
}
else {
sleep(1);
printf("Thread %u increasing the counter to %u\n", (unsigned int)self, ++ctx->counter);
}
sem_post(&ctx->sem);
}
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
struct threadCtx ctx = {.counter = 0};
sem_init(&sem.ctx, 0, 1);
for (int k=0; k<NUM_THREADS; k++) {
pthread_create(threads+k, NULL, threadFunc, &ctx);
}
for (int k=0; k<NUM_THREADS; k++) {
pthread_join(threads[k], NULL);
}
sem_destroy(&ctx.sem);
return 0;
}
The output is
Thread 1004766976 increasing the counter to 1
Thread 1004766976 increasing the counter to 2
Thread 1004766976 increasing the counter to 3
...
If I remove the call to sleep, the behavior is closer to what I would expect (i.e., the threads being woken up in a seemingly indeterminate manner). Why would this be?
David Schwartz's answer explains what is happening at a low level. That is to say, he's looking at it from the perspective of an OS developer or a hardware designer. Nothing wrong with that, but let's look at your program from the perspective of a Software Architect:
You've got multiple threads all executing the same loop. The loop locks the mutex,* it does some "work," and then it releases the mutex. OK, but what does it do next? Almost the very next thing that your loop does after releasing the mutex is it locks the mutex again. Your loop spends practically 100% of its time doing "work" with the mutex locked.
So, what's the point of running that same loop in multiple threads when there's never any opportunity for two or more threads to work at the same time?
If you want to use threads to do a parallel computation, you need to find/invent safe ways for the threads to do most of their work with the mutex unlocked. They should only lock a mutex for just long enough to post a result or, to take another assignment.
Sometimes that means writing code that is less efficient than single threaded code would be. But suppose that program (A) has a single thread that makes almost 100% use of a CPU, while program (B) uses eight CPUs but only uses them with 50% efficiency. Which program is going to win?
* I know, your example uses a sem_t (semaphore) object. But "semaphore" is what you are using. "Mutex" is the role in which you are using it.
Why would this be?
Context switches are expensive and your implementation is, wisely, minimizing them. Your threads are all fighting over the same resource, trying to schedule them closely will make performance much worse, probably for the entire system.
Since the thread that keeps getting the semaphore never uses up its timeslice, it will keep getting the resource. It is your responsibility to write code to do the work that you want done. It's the implementation's responsibility to execute your code as efficiently as it can, and that's what it's doing.
Most likely, what's going under the hood is this:
The thread that keeps getting the sempahore can always make forward progress except when it is sleeping. But when it is sleeping, no other thread that needs the sempahore can make forward progress.
The thread that keeps getting the semaphore never exhausts its timeslice because it sleeps before that happens.
So there is no reason for the implementation to ever block this thread other than when it is sleeping, meaning that no other thread can get the semaphore. If you don't want this thread to keep sleeping with the semaphore and blocking other threads, then write different code.
This question already has an answer here:
Pthread_create() incorrect start routine parameter passing
(1 answer)
Closed 3 years ago.
I tried to build a program which should create threads and assign a Print function to each one of them, while the main process should use printf function directly.
Firstly, I made it without any synchronization means and expected to get a randomized output.
Later I tried to add a mutex to the Print function which was assigned to the threads and expected to get a chronological output but it seems like the mutex had no effect about the output.
Should I use a mutex on the printf function in the main process as well?
Thanks in advance
My code:
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
pthread_t threadID[20];
pthread_mutex_t lock;
void* Print(void* _num);
int main(void)
{
int num = 20, indx = 0, k = 0;
if (pthread_mutex_init(&lock, NULL))
{
perror("err pthread_mutex_init\n");
return errno;
}
for (; indx < num; ++indx)
{
if (pthread_create(&threadID[indx], NULL, Print, &indx))
{
perror("err pthread_create\n");
return errno;
}
}
for (; k < num; ++k)
{
printf("%d from main\n", k);
}
indx = 0;
for (; indx < num; ++indx)
{
if (pthread_join(threadID[indx], NULL))
{
perror("err pthread_join\n");
return errno;
}
}
pthread_mutex_destroy(&lock);
return 0;
}
void* Print(void* _indx)
{
pthread_mutex_lock(&lock);
printf("%d from thread\n", *(int*)_indx);
pthread_mutex_unlock(&lock);
return NULL;
}
All questions of program bugs notwithstanding, pthreads mutexes provide only mutual exclusion, not any guarantee of scheduling order. This is typical of mutex implementations. Similarly, pthread_create() only creates and starts threads; it does not make any guarantee about scheduling order, such as would justify an assumption that the threads reach the pthread_mutex_lock() call in the same order that they were created.
Overall, if you want to order thread activities based on some characteristic of the threads, then you have to manage that yourself. You need to maintain a sense of which thread's turn it is, and provide a mechanism sufficient to make a thread notice when it's turn arrives. In some circumstances, with some care, you can do this by using semaphores instead of mutexes. The more general solution, however, is to use a condition variable together with your mutex, and some shared variable that serves as to indicate who's turn it currently is.
The code passes the address of the same local variable to all threads. Meanwhile, this variable gets updated by the main thread.
Instead pass it by value cast to void*.
Fix:
pthread_create(&threadID[indx], NULL, Print, (void*)indx)
// ...
printf("%d from thread\n", (int)_indx);
Now, since there is no data shared between the threads, you can remove that mutex.
All the threads created in the for loop have different value of indx. Because of the operating system scheduler, you can never be sure which thread will run. Therefore, the values printed are in random order depending on the randomness of the scheduler. The second for-loop running in the parent thread will run immediately after creating the child threads. Again, the scheduler decides the order of what thread should run next.
Every OS should have an interrupt (at least the major operating systems have). When running the for-loop in the parent thread, an interrupt might happen and leaves the scheduler to make a decision of which thread to run. Therefore, the numbers being printed in the parent for-loop are printed randomly, because all threads run "concurrently".
Joining a thread means waiting for a thread. If you want to make sure you print all numbers in the parent for loop in chronological order, without letting child thread interrupt it, then relocate the for-loop section to be after the thread joining.
I was trying to implement a multi-threaded program using binary semaphore. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
int g = 0;
sem_t *semaphore;
void *myThreadFun(void *vargp)
{
int myid = (int)vargp;
static int s = 0;
sem_wait(semaphore);
++s; ++g;
printf("Thread ID: %d, Static: %d, Global: %d\n", myid, s, g);
fflush(stdout);
sem_post(semaphore);
pthread_exit(0);
}
int main()
{
int i;
pthread_t tid;
if ((semaphore = sem_open("/semaphore", O_CREAT, 0644, 3))==SEM_FAILED) {
printf("semaphore initialization failed\n");
}
for (i = 0; i < 3; i++) {
pthread_create(&tid, NULL, myThreadFun, (void *)i);
}
pthread_exit(NULL);
return 0;
}
Now, when I opened the sempahore, I made the count 3. I was expecting that this wouldnt work, and I would get race condition, because each thread is now capable of decrementing the count.
Is there something wrong with the implementation? Also, if I make the count 0 during sem_open, wouldnt that initiate a deadlock condition, because all the threads should be blocked on sem_wait.
Now, when I opened the sempahore, I made the count 3. I was expecting that this wouldnt work, and I would get race condition, because each thread is now capable of decrementing the count.
And how do you judge that there isn't any race? Observing output consistent with what you could rely upon in the absence of a data race in no way proves that there isn't a data race. It merely fails provide any evidence of one.
However, you seem to be suggesting that there would be a data race inherent in more than one thread concurrently performing a sem_wait() on a semaphore whose value is initially greater than 1 (otherwise which counter are you talking about?). But that's utter nonsense. You're talking about a semaphore. It's a synchronization object. Such objects and the functions that manipulate them are the basis for thread synchronization. They themselves are either completely thread safe or terminally buggy.
Now, you are correct that you open the semaphore with an initial count sufficient to avoid any of your threads blocking in sem_wait(), and that therefore they can all run concurrently in the whole body of myThreadFun(). You have not established, however, that they in fact do run concurrently. There are several reasons why they might not do. If they do run concurrently, then the incrementing of shared variables s and g is indeed of concern, but again, even if you see no signs of a data race, that doesn't mean there isn't one.
Everything else aside, the fact that your threads all call sem_wait(), sem_post(), and printf() induces some synchronization in the form of memory barriers, which would reduce the likelihood of observing anomalous effects on s and g. sem_wait() and sem_post() must contain memory barriers in order to function correctly, regardless of the semaphore's current count. printf() calls are required to use locking to protect the state of the stream from corruption in multi-threaded programs, and it is reasonable to suppose that this will require a memory barrier.
Is there something wrong with the implementation?
Yes. It is not properly synchronized. Initialize the semaphore with count 1 so that the modifications of s and g occur only while exactly one thread has the semaphore locked.
Also, if I make the count 0 during sem_open, wouldnt that initiate a deadlock condition, because all the threads should be blocked on sem_wait.
If the semaphore has count 0 before any of the additional threads are started, then yes. It is therefore inappropriate to open the semaphore with count 0, unless you also subsequently post to it before starting the threads. But you are using a named semaphore. These persist until removed, and you never remove it. The count you specify to sem_open() has no effect unless a new semaphore needs to be created; when an existing semaphore is opened, its count is unchanged.
Also, do have the main thread join all the others before it terminates. It's not inherently wrong not to do so, but in most cases it's required for the semantics you want.
I'll get to the code in a bit that proves you had a race condition. I'll add a couple of different ways to trigger it so you can see how this works. I'm doing this on Linux and passing -std=gnu99 as a param to gcc ie
gcc -Wall -pedantic -lpthread -std=gnu99 semaphore.c -o semtex
Note. In your original example (assuming Linux) one fatal mistake you had was not removing the semaphore. If you run the following command you might see some of these sitting around on your machine
ls -la /dev/shm/sem.*
You need to make sure that there are no old semaphore sitting on the filesystem before running your program or you end up picking up the last settings from the old semaphore. You need to use sem_unlink to clean up.
To run it please use the following.
rm /dev/shm/sem.semtex;./semtex
I'm deliberately making sure that the semaphore is not there before running because if you have a DEADLOCK it can be left around and that causes all sorts of problems when testing.
Now, when I opened the sempahore, I made the count 3. I was expecting
that this wouldn't work, and I would get race condition, because each
thread is now capable of decrementing the count.
You had a race condition but sometimes C is so damned fast things can appear to work because your program can get everything it needs done in the time the OS has allocated to the thread ie the OS didn't preempt it at an important point.
This is one of those cases, the race condition is there you just need to squint a little bit to see it. In the following code you can tweak some parameters to see the a deadlock, correct usage and Undefined Behavior.
#define INITIAL_SEMAPHORE_VALUE CORRECT
The value INITIAL_SEMAPHORE_VALUE can take three values...
#define DEADLOCK 0
#define CORRECT 1
#define INCORRECT 2
I hope they're self explanatory. You can also use two methods to cause the race condition to blow up the program.
#define METHOD sleep
Set the METHOD to spin and you can play with the SPIN_COUNT and find out how many times the loop can run before you actually see a problem, this is C, it can get a lot done before it gets preempted. The code has most of the rest of the information you need in it.
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEADLOCK 0 // DEADLOCK
#define CORRECT 1 // CORRECT
#define INCORRECT 2 // INCORRECT
/*
* Change the following values to observe what happen.
*/
#define INITIAL_SEMAPHORE_VALUE CORRECT
//The next value provides to two different ways to trigger the problem, one
//using a tight loop and the other using a system call.
#define METHOD sleep
#if (METHOD == spin)
/* You need to increase the SPIN_COUNT to a value that's big enough that the
* kernel preempts the thread to see it fail. The value set here worked for me
* in a VM but might not work for you, tweak it. */
#define SPIN_COUNT 1000000
#else
/* The reason we can use such a small time for USLEEP is because we're making
* the kernel preempt the thread by using a system call.*/
#define USLEEP_TIME 1
#endif
#define TOT_THREADS 10
static int g = 0;
static int ret = 1729;
sem_t *semaphore;
void *myThreadFun(void *vargp) {
int myid = (int)vargp;
int w = 0;
static int s = 0;
if((w = sem_wait(semaphore)) != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
abort();
};
/* This is the interesting part... Between updating `s` and `g` we add
* a delay using one of two methods. */
s++;
#if ( METHOD == spin )
int spin = 0;
while(spin < SPIN_COUNT) {
spin++;
}
#else
usleep(USLEEP_TIME);
#endif
g++;
if(s != g) {
fprintf(stderr, "Fatal Error: s != g in thread: %d, s: %d, g: %d\n", myid, s, g);
abort();
}
printf("Thread ID: %d, Static: %d, Global: %d\n", myid, s, g);
// It's a false sense of security if you think the assert will fail on a race
// condition when you get the params to sem_open wrong It might not be
// detected.
assert(s == g);
if((w = sem_post(semaphore)) != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
abort();
};
return &ret;
}
int main(void){
int i;
void *status;
const char *semaphore_name = "semtex";
pthread_t tids[TOT_THREADS];
if((semaphore = sem_open(semaphore_name, O_CREAT, 0644, INITIAL_SEMAPHORE_VALUE)) == SEM_FAILED) {
fprintf(stderr, "Fatal Error: %s\n", strerror(errno));
abort();
}
for (i = 0; i < TOT_THREADS; i++) {
pthread_create(&tids[i], NULL, myThreadFun, (void *) (intptr_t) i);
}
for (i = 0; i < TOT_THREADS; i++) {
pthread_join(tids[i], &status);
assert(*(int*)status == 1729);
}
/*The following line was missing from your original code*/
sem_unlink(semaphore_name);
pthread_exit(0);
}
I have 2 programs that need to communicate with each other, the first one must output what the second one puts into the shared memory, but I've removed everything but the semaphores, because there is a problem with synchronization.
This is a school assignment, so I must use semaphores in exactly the way shown below, don't propse anything else because I can't use it.
#include <sys/shm.h>
#include <errno.h>
#include <sys/sem.h>
int main()
{
int semid;
struct sembuf sobs;
semid=semget(9999,1,IPC_CREAT|0600);
semctl(semid, 0, SETVAL,1);
sobs.sem_num=0;
sobs.sem_op= 0;
sobs.sem_flg=0;
semop(semid,&sobs,1);
/* DO SOMETHING */
sobs.sem_op=1;
semop(semid,&sobs,1);
shmctl(shmid1,IPC_RMID,0);
return 0;
}
And the second program:
#include <sys/shm.h>
#include <errno.h>
#include <sys/sem.h>
int main()
{
int semid;
struct sembuf sobs;
semid=semget(9999,1,0600);
sobs.sem_num=0;
sobs.sem_op=-1;
sobs.sem_flg=0;
semop(semid,&sobs,1);
/* DO SOMETHING */
sobs.sem_op=1;
semop(semid,&sobs,1);
shmctl(shmid1,IPC_RMID,0);
return 0;
}
So the problem is that if I put a sleep() above the DO SOMETHING in the second program, the first one will still go into the critical section, and it will finish before the second one, but the first one mustn't go into the critical section before the second one exits it, what can I do to prevent the first one from entering?
Typically program A should set the semaphore to 0 initially using semctl. Then program A calls semop with sobs.sem_op=-1, to wait on the semaphore.
Program B doesn't need to do anything with the semaphore before "do something". (Note that A has to be run first, so that the semaphore is created and setup before B starts.) After B is done it should signal A by calling semop with sobs.sem_op=1.
In short A sets up the semaphore and waits for it before "doing something". B "does something" and then signals the semaphore to wake A.
You're using so-called SysV (System V) semaphores, as opposed to POSIX semaphores. You might want to look up tutorials for that. Your first mistake is the first argument to semget: you aren't supposed to pass it a value that you've defined, you should use ftok(3) instead (if you chose the key yourself collisions are pretty likely: too many people will simply use 1 or 42 as key value).
Next, some explanations on how semaphores work:
A semaphore has a value.
You can increase its value (foo.sem_op > 0). This is a non-blocking operation.
You can decrease its value (foo.sem_op < 0). This may block (which is what it's used for). Explanation below.
You can wait for it to become 0 (foo.sem_op == 0). If the semaphore is already 0 it doesn't block, otherwise it blocks.
When you decrease the semaphore, the following scenarios can happen (simplified):
If decreasing the semaphore would not make it become negative the call does not block.
If decreasing the semaphore by the value in foo.sem_op would make the semaphore negative it will block until the semaphore's value has become large enough so that the decreasing won't make the semaphore negative any more.
You'd like to use the semaphore as a mutex. In this case you only want values of "1". The pattern now usually looks like this:
Initialize the semaphore, set it to 1.
The first worker decreases the semaphore by -1. This immediately succeeds.
The second worker tries to decrease the semaphore by -1. This blocks as it would make the semaphore negative.
The first worker finishes and increases the semaphore.
This wakes up the second worker which now decreases the semaphore.
When the second worker is done it also increases the semaphore.
So we have:
Lock mutex: decrease the semaphore by -1.
Unlock mutex: increase the semaphore by 1.