shithub: drawcpu

ref: 446c14eaf9e886016e86a574e5cb7685f39ff851
dir: /kern/sema.c/

View raw version
/* 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;
}