/*
 * sync.c
 * a synchronized interface for C using semaphores
 * this should not be used for threads, but rather for shared memory access among processes
 */

/*   usage:
 *
 *   void *s = sync_on( <semaphore key> );
 *   // All processes which want to be synchronized to a single resource should call
 *   //  sync_on() with the same <semaphore key>
 *
 *   synchronize (s) {
 *       // This code is atomic with respect to <semaphore key>
 *   }
 *
 *   The keywords break and return should not be used inside of the synchronize construct,
 *   otherwise the lock will not be released. If you want to exit from the synchroinze 
 *   early, use continue.
 *
 *   You can nest the synchronize() constructs, but of course, nesting with the same
 *   <semaphore key> would lead to starvation caused by the process itself.
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>

#include "sync.h"
#include "func_private.h"
#include "debug.h"

static char const   rcsid[] = "$Id: sync.c,v 1.2 1998/03/09 22:45:44 abakun Exp $";

static struct sembuf acquire =
{0, -1, SEM_UNDO};
static struct sembuf release =
{0, 1, SEM_UNDO};

struct semitem_s {
    key_t               key;
    int                 semid;
    int                 is_acquired;
};

func_public void   *sync_on(key_t thekey)
{
    int                 sync_semid = 1;
    struct semitem_s   *semitem;
    static ushort       start_val[] =
    {1};
    union semun         arg;

    if ((sync_semid = semget(thekey, 1, 0600)) < 0) {
	/* FIXME: unable to bind to semset */
	DB(-1, (stderr, "unable to bind to semaphore set with key 0x%x\n", thekey));
	perror("semget");
	exit(1);
    }

    DB(-1, (stderr, "initializing semaphores, this shouldn't happen!\n"));
    arg.array = start_val;
    semctl(sync_semid, 0, SETALL, arg);
    /* we assume that the semaphores were created and initialized already and the key was passed to us */

    semitem = (struct semitem_s *) malloc(sizeof(struct semitem_s));
    if (semitem == NULL) {
	/* FIXME: out of mem */
	DB(-1, (stderr, "out of memory during malloc in sync_on\n"));
	exit(1);
    }

    semitem->key = thekey;
    semitem->semid = sync_semid;
    semitem->is_acquired = 0;

    return (void *) semitem;

}


func_public void    sync_off(void *ptr)
{
    struct semitem_s   *semitem;

    if (ptr == NULL)
	return;

    semitem = (struct semitem_s *) ptr;

    if (semctl(semitem->semid, 0, IPC_RMID, 0) == -1) {
	/* FIXME: unable to remove sems, is this a fatal error? */
	DB(-1, (stderr, "unable to remove semaphore %d, key 0x%x\n", semitem->semid, semitem->key));
    }
    free(ptr);

}


func_public int     sync_acquire_lock(void *ptr)
{
    struct semitem_s   *semitem;

    semitem = (struct semitem_s *) ptr;

    semitem->is_acquired++;

    if (semitem->is_acquired) {
	DB(10, (stderr, "getting sem\n"));
	semop(semitem->semid, &acquire, 1);
	DB(10, (stderr, "sem %d is_acquired = %d\n", semitem->semid, semitem->is_acquired));
    }
    return semitem->is_acquired;
}


func_public int     sync_release_lock(void *ptr)
{
    struct semitem_s   *semitem;

    semitem = (struct semitem_s *) ptr;
    DB(10, (stderr, "sem %d releasing\n", semitem->semid));
    semop(semitem->semid, &release, 1);
    semitem->is_acquired = -1;
}


/*
 * $Log: sync.c,v $
 * Revision 1.2  1998/03/09 22:45:44  abakun
 * added comments about the dangers of using break and return inside the synchronize construct
 *
 * Revision 1.1  1998/03/09 16:18:06  abakun
 * Initial revision
 *
 */
