ref: b35328f248cab12ae0760c237d1544842200510c
dir: /kern/sema.c/
/* Semaphore implementation for Plan 9 emulator */
#include "u.h"
#include "lib.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
#include "user.h"
/* Semaphore wait queue entry */
typedef struct Semwait Semwait;
struct Semwait {
Semwait *next;
Proc *proc;
long *addr;
};
static Lock semlock;
static Semwait *semwait;
/*
* Semaphore sleep condition function
* Called by sleep() to check if we should continue sleeping
*/
int
semwakeup(void *arg)
{
long *addr = (long*)arg;
/* Return 0 to continue sleeping, 1 to wake up */
return *addr > 0;
}
/*
* semacquire - acquire semaphore
* addr: pointer to semaphore counter
* block: 1 to block if unavailable, 0 to return immediately
*
* Returns: 0 on success, -1 on error
*/
int
semacquire(long *addr, int block)
{
Semwait *w, **l;
if(addr == nil) {
werrstr("semacquire: nil address");
return -1;
}
lock(&semlock);
/* Try to decrement the semaphore */
if(*addr > 0) {
(*addr)--;
unlock(&semlock);
return 0;
}
/* Semaphore is 0, can't acquire */
if(!block) {
unlock(&semlock);
werrstr("semacquire: would block");
return -1;
}
/* Block until semaphore is available */
w = malloc(sizeof(Semwait));
if(w == nil) {
unlock(&semlock);
werrstr("semacquire: out of memory");
return -1;
}
w->addr = addr;
w->proc = up;
w->next = semwait;
semwait = w;
/* Use the existing sleep rendezvous mechanism */
up->state = Wakeme;
unlock(&semlock);
/* Sleep on the semaphore address as rendezvous point */
if(waserror()) {
/* Interrupted - remove from wait queue */
lock(&semlock);
for(l = &semwait; *l; l = &(*l)->next) {
if(*l == w) {
*l = w->next;
break;
}
}
unlock(&semlock);
free(w);
nexterror();
}
ksleep(&up->rsleep, semwakeup, addr);
poperror();
return 0;
}
/*
* semrelease - release semaphore
* addr: pointer to semaphore counter
* count: number of times to increment semaphore
*
* Returns: 0 on success, -1 on error
*/
int
semrelease(long *addr, long count)
{
Semwait *w, **l;
Proc *p;
if(addr == nil) {
werrstr("semrelease: nil address");
return -1;
}
if(count <= 0) {
werrstr("semrelease: bad count");
return -1;
}
lock(&semlock);
/* Increment the semaphore counter */
*addr += count;
/* Wake up waiting processes */
l = &semwait;
while(*l && count > 0) {
w = *l;
if(w->addr == addr) {
/* Remove from wait list */
*l = w->next;
/* Wake up the process */
p = w->proc;
wakeup(&p->rsleep);
free(w);
count--;
(*addr)--; /* Process will consume one count */
} else {
l = &w->next;
}
}
unlock(&semlock);
return 0;
}
/*
* Helper function to clean up semaphore waiters when process exits
* Call this from your process cleanup code
*/
void
cleansemwait(Proc *p)
{
Semwait *w, **l;
lock(&semlock);
l = &semwait;
while(*l) {
w = *l;
if(w->proc == p) {
*l = w->next;
free(w);
} else {
l = &w->next;
}
}
unlock(&semlock);
}
/*
* Initialize semaphore system - call during startup
*/
void
seminit(void)
{
semwait = nil;
}