/* $Cambridge: hermes/src/prayer/lib/mymutex_sem.c,v 1.3 2008/09/16 09:59:57 dpc22 Exp $ */

/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2008 */
/* See the file NOTICE for conditions of use and distribution. */

#include <sys/types.h>
#include <sys/ipc.h>

#ifndef __USE_GNU
#define __USE_GNU       /* Evil hack to get prototype for semtimedop */
#include <sys/sem.h>
#undef __USE_GNU
#endif

union semun {
    int val;
    struct semid_ds *buf;
    ushort *array;
};

struct mymutex {
    struct pool *pool;
    int sem_id;
    struct sembuf *op_on;
    struct sembuf *op_off;
};

struct mymutex *
mymutex_create(struct pool *pool, char *lockdir, char *name)
{
    union semun ick;
    struct mymutex *mymutex = pool_alloc(pool, sizeof(struct mymutex));

    mymutex->pool   = pool;
    mymutex->op_on  = pool_alloc(pool, sizeof(struct sembuf));
    mymutex->op_off = pool_alloc(pool, sizeof(struct sembuf));

    if ((mymutex->sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600)) < 0) {
        log_panic("[mymutex_create] semget() failed: %s", strerror(errno));
        return(NIL);
    }

    log_misc("Assigned semaphore %d for mymutex\n", mymutex->sem_id);

    ick.val = 1;
    if (semctl(mymutex->sem_id, 0, SETVAL, ick) < 0) {
        log_panic("[mymutex_create] semctl() failed: %s", strerror(errno));
        return(NIL);
    }

    /* pre-initialize these */
    mymutex->op_on->sem_num = 0;
    mymutex->op_on->sem_op = -1;
    mymutex->op_on->sem_flg = SEM_UNDO;
    mymutex->op_off->sem_num = 0;
    mymutex->op_off->sem_op = 1;
    mymutex->op_off->sem_flg = SEM_UNDO;

    return(mymutex);
}

void
mymutex_free(struct mymutex *mymutex)
{
    union semun ick;

    if (mymutex->sem_id >= 0) {
        ick.val = 0;
        semctl(mymutex->sem_id, 0, IPC_RMID, ick);
        mymutex->sem_id = -1;
    }

    if (!mymutex->pool) free(mymutex);
}

BOOL
mymutex_slave_init(struct mymutex *mymutex)
{
    return(T);
}

void
mymutex_slave_cleanup(struct mymutex *mymutex)
{
}

BOOL
mymutex_on(struct mymutex *mymutex, int timeout)
{
    struct timespec  timespec;
    struct timespec *timespecp = &timespec;
    int rc;

    if (timeout > 0) {
        timespec.tv_sec  = timeout;
        timespec.tv_nsec = 0;
        timespecp = &timespec;
    } else
        timespecp = NIL;

    do {
        rc = semtimedop(mymutex->sem_id, mymutex->op_on, 1, timespecp);
    } while ((rc < 0) && (errno == EINTR));

    if ((rc < 0) && (errno != EAGAIN))
        log_fatal("[mymutex_on] semop() failed: %s", strerror(errno));
    
    return((rc < 0) ? NIL : T);   /* NIL => timed out */
}

BOOL
mymutex_off(struct mymutex *mymutex)
{
    int rc;

    do {
        rc = semop(mymutex->sem_id, mymutex->op_off, 1);
    } while ((rc < 0) && (errno == EINTR));

    if (rc < 0)
        log_fatal("[mymutex_off] semop() failed: %s", strerror(errno));

    return(T);
}

