How to create Semaphores in c?

拜拜、爱过 提交于 2021-02-07 18:44:58

问题


Im trying to recreate a "Blackbox" library. In my CS class when we are supposed to use Semaphores (in our on paper final) we are given a "sem.h" file. there are 3 functions one for creating a new Semaphore with an inital number of tokens, one for taking a token out of a semaphore and one for placing a token into a semaphore. at 0 tokes any thread using the blocking funktion has to wait for a token.

for better understanding ive been trying to recreate this sem.h and sem.c based on some exams asking for an implementation of single funktions. since this is all intended to be done on paper, it donst compile but I feel im close

sem.h

typedef struct SEM SEM;
struct SEM *semCreate(int);
void P(struct SEM*); //tokens--
void V(struct SEM*); //tokens++

sem.c

#include "sem.h"
#include <pthread.h>
#include <errno.h>
typedef struct SEM{
    volatile int val; //number of tokens
    pthread_mutex_t m;
    pthread_cond_t c;
}SEM;


/*create new semaphore with #initVal tokens */
SEM *semCreate(int initVal){
    static SEM *sem ={
        .val=initVal
    };
    errno = 0;
    if((errno = pthread_mutex_init(&sem->m,NULL))!=0){
        return NULL;
    }
    if((errno = pthread_cond_init(&sem->c,NULL))!=0){
        return NULL;
    }
    return sem;
}
//take a token from the semaphore
void P(SEM *sem){
    if((errno = pthread_mutex_lock(&sem->m))!=0){
        return;
    }
    while(sem->val <=0){
        if((errno=pthread_cond_wait(&sem->c,&sem->m))!=0){
            pthread_mutex_unlock(&sem->m);
            return;
        }
        sem->val--;
        if(errno = pthread_mutex_unlock(&sem->m)!=0)return;
    }
}
//put a token into the semaphore
void V(SEM *sem){
    if((errno = pthread_mutex_lock(&sem->m))!=0){
        return;
    }

    sem-> val++;

    if((errno = pthread_cond_broadcast(&sem->c))!=0)return;
    if((errno=pthread_mutex_unlock(&sem->m)!=0)) return;
}


Incase it isnt clear what this is for: The functions should limit how many threads can access a segment of code at the same time example

//global
static SEM *sem = semCreate(1);
/.../
//critical segment in threadfunction
P(sem);
doReadAndWriteGlobalList();
V(sem);

as soon as the 1st thread passes P() any subsequent calls of P wont be able to pass it until V was called on the same sem

on compiling I get the following error:

sem.c: In function ‘semCreate’:
sem.c:14:3: error: field name not in record or union initializer
   .val=initVal
   ^
sem.c:14:3: note: (near initialization for ‘sem’)
sem.c:14:8: error: initialization makes pointer from integer without a cast [-Werror=int-conversion]
   .val=initVal
        ^~~~~~~
sem.c:14:8: note: (near initialization for ‘sem’)
sem.c:14:8: error: initializer element is not constant
sem.c:14:8: note: (near initialization for ‘sem’)
cc1: all warnings being treated as errors

回答1:


You don't want a static var. You want to create a new object (memory allocation) every time semCreate is called. As such,

static SEM *sem ={
    .val=initVal
};

should be

SEM *sem = malloc(sizeof(SEM));
sem->val = initVal;

Don't forget to free the semaphore after you're done with it. That includes on error!

SEM *semCreate(int initVal){
    SEM *sem = malloc(sizeof(SEM));
    if (!sem)
        goto Error1;

    sem->val = initVal;

    errno = pthread_mutex_init(&sem->m, NULL);
    if (!errno)
       goto Error2;

    errno = pthread_cond_init(&sem->c, NULL);
    if (!errno)
       goto Error3;

    return sem;

Error3:
    pthread_mutex_destroy(&sem->m);
Error2:
    free(buf);
Error1:
    return NULL;
}

Aside from that, your code has multiple problems. In short, P is completely wrong.

  • P can call pthread_cond_signal with an unlocked mutex.
  • P can return with the mutex still locked.
  • P decrements the value when it's non-positive when it's suppose to decrement it when it's positive.

And there's the issue that both P and V perform pointless and even harmful error handling. Skipping unlocking the mutex if the broadcast fails? Yeah, let's not do that.

Let's start from scratch with a basic solution, one with no regards for safety or efficiency.

void V(SEM *sem) {
   ++sem->val;
}

void P(SEM *sem) {
   // Wait for the semaphore to have a positive value.
   while (sem->val < 1) {
      // This is where another thread could change sem->val.
   }

   --sem->val;
}

Now, let's make it thread-safe through mutual exclusion.

void V(SEM *sem) {
   pthread_mutex_lock(&sem->m);
   ++sem->val;
   pthread_mutex_unlock(&sem->m);
}

void P(SEM *sem) {
   pthread_mutex_lock(&sem->m);

   // Wait for the semaphore to have a positive value.
   while (sem->val < 1) {
      pthread_mutex_unlock(&sem->m);
      // This is where another thread could change sem->val.
      pthread_mutex_lock(&sem->m);
   }

   --sem->val;
   pthread_mutex_unlock(&sem->m);
}

But that's a busy wait. Let's use a condition var to sleep until the semaphore changes. (Remember that cond_wait unlocks the provided mutex on entry and relocks it before returning.)

void V(SEM *sem) {
   pthread_mutex_lock(&sem->m);

   ++sem->val;

   // Wake up a thread that's waiting, if any.
   if (sem->val > 0)
      pthread_cond_signal(&sem->c);

   pthread_mutex_unlock(&sem->m);
}

void P(SEM *sem) {
   pthread_mutex_lock(&sem->m);

   // Wait for the semaphore to have a positive value.
   while (sem->val < 1)
      pthread_cond_wait(&sem->c, &sem->m);

   --sem->val;

   // Wake up a thread that's waiting, if any.
   if (sem->val > 0)
      pthread_cond_signal(&sem->c);

   pthread_mutex_unlock(&sem->m);
}

Tada!


Notes:

  • There's no point in calling pthread_cond_broadcast since only one thread can be modify the semaphore at a time. By having both V and P call pthread_cond_signal when appropriate, we avoid waking up threads for nothing.
  • We can leave out checking if pthread_mutex_lock, pthread_mutex_unlock and pthread_cond_signal fail in working code as these only fail as the result of a coding error.



回答2:


Your Procure is wrong. Inside the loop, you release the mutex; which invalidates both the test at the top of the loop and the cond wait.

You probably want something like:

   lock(&s->mutex);
   while (s->val <= 0) {
        wait(&s->cv, &s->mutex);
   }
   s->val--;
   unlock(&s->mutex);

A wakeup from a condition variable is just an indication you should recheck your condition; not an assertion that your condition has arrived.



来源:https://stackoverflow.com/questions/60224504/how-to-create-semaphores-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!