Listing 4

#include <errno.h>           /*rwslock.c */
#include <limits.h>
#include <stdio.h>
#include <string.h>
#define PATHDLM ('/')   /* path name delimiter */
#include "rwsem.h"

/* function declarations */
int getcnt(char *file, int *cntp);
int putcnt(char *file, int cnt);

/* read/write semaphore set table definition */
static rwsset_t rwst[RWSOPEN_MAX];

/* rwslock: read/write semaphore lock */
int rwslock(rwsset_t *rwsp, int rwsno, int ltype)
{
    int wsem = 0;           /* write semaphore */
    int rsem = 0;           /* read semaphore */
    int rc = 0;         /* readcount */
    char rcpath[PATH_MAX + 1]; /* readcount file path name */

    /* identify write and read semaphores and read-count file */
    wsem = rwsno * 2;
    rsem = wsem + 1;
    sprintf(rcpath, "%s%cr%d", rwsp->rwsdir, (int)PATHDLM, rwsno);

    switch (ltype) {
    case RWS_UNLCK: /* unlock */
        switch (rwsp->lockheld[rwsno]) {
        case RWS_UNLCK: /* unlock */
            break;  /* case RWS_UNLCK: */
        case RWS_RDLCK: /* read lock */
            if (semlower(rwsp->ssp, rsem) == -1) {  /* allocate readcount */
                return -1;
            }
            getcnt(rcpath, &rc);       /* get readcount */
            rc--;             /* decrement readcount */
            if (rc == 0) {          /* if no other readers, */
                if (semraise(rwsp->ssp, wsem) == -1) {  /* free resource */
                    semraise(rwsp->ssp, rsem);
                    return -1;
                }
            }
            putcnt(rcpath, rc);          /* store new readcount */
            if (semraise(rwsp->ssp, rsem) == -1) {  /* free readcount */
                return -1;
            }
            break; /* case RWS_RDLCK: */
        case RWS_WRLCK: /* write lock */
            if (semraise(rwsp->ssp, wsem) == -1) {
                return -1;
            }
            break;   /* case RWS_WRLCK: */
        default:
            errno = RWSPANIC;
            return -1;
            break;  /* default: */
        };
        break;  /* case RWS_UNLCK: */
    case RWS_RDLCK: /* read lock */
        if (rwsp->lockheld[rwsno] == RWS_RDLCK) {
            errno = 0;
            return 0;
        }
        if (semlower(rwsp->ssp, rsem) == -1) {  /* allocate readcount */
            return -1;
        }
        getcnt(rcpath, &rc);        /* get readcount */
        rc++;                /* increment readcount */
        if (rc == 1) {           /* if no other readers, */
            if (semlower(rwsp->ssp, wsem) == -1) {  /* allocate resource */
                semraise(rwsp->ssp, rsem);
                return -1;
            }
        }
        putcnt(rcpath, rc);          /* store new readcount */
        if (semraise(rwsp->ssp, rsem) == -1) {  /* free readcount */
            if (rc == 1) semraise(rwsp->ssp, wsem);
            return -1;
        }
        break;  /* case RWS_RDLCK: */
    case RWS_WRLCK: /* write lock */
        if (semlower(rwsp->ssp, wsem) == -1) {  /* allocate resource */
            return -1;
        }
        break;  /* case RWS_WRLCK: */
    default:
        errno = EINVAL;
        return -1;
        break;  /* default: */
    };

    /* save type of lock held */
    rwsp->lockheld[rwsno] = ltype;

    errno = 0;
    return 0;
 }