/*
 * This program plays around with signal semaphores...
 *
 * Thanks to C-A for providing the new bindings & AddSemaphore code
 *
 */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/semaphores.h>
#include <exec/memory.h>
#include <exec/execbase.h>

struct XSSemaphore {
	struct SignalSemaphore s;
	WORD Users;
};

void *AllocMem();
void cleanup();
void DeleteSSemaphore();
struct SignalSemaphore *myFindSemaphore();
struct XSSemaphore *CreateSSemaphore();

struct XSSemaphore *sem = 0;

/* I made this extended semaphore so that there is an easy way to
 * keep track of when it is safe to delete it.  If anyone can think
 * of a better way to do this, I'd love to hear it.
 */

extern int Enable_Abort;

main()
{
	int i;
	Enable_Abort = 0;

	/* the existance check/creation must be atomic --> Forbid/Permit */

	Forbid();

	if (!(sem = (struct XSSemaphore *)myFindSemaphore("sigSemaphore")))
		sem = CreateSSemaphore("sigSemaphore",0);
	else
		sem->Users++;

	Permit();

	if (!sem) {
		printf("Error, can't find *or* create semaphore\n");
		cleanup();
		exit(100);
	}

	for (i=0;i<5;i++) {

		/* get use of the semaphore */
		ObtainSemaphore(sem);

		/* we now have exclusive access */

		printf("\nTask $%06x owns the semaphore\n",FindTask(0));

		/* pretend to so some action requiring semaphore */
		Delay(1 + (rand()&31));

		printf("\t\t...semaphore now released\n");

		/* yield control to someone else */
		ReleaseSemaphore(sem);

		/* pretend to so some action not requiring semaphore */
		Delay(1 + (rand()&31));

	}

	cleanup();
}

void cleanup()
{
	if (sem) {
		/* the user check/delete must be atomic --> Forbid/Permit */

		Forbid();
		if (--sem->Users == 0)
			DeleteSSemaphore(sem);
		Permit();

		sem = 0;
	}
}

struct XSSemaphore *CreateSSemaphore(name,pri)
char *name;
int pri;
{
	struct XSSemaphore *sem;
	char *buf;

	sem = AllocMem(sizeof(struct XSSemaphore), MEMF_PUBLIC|MEMF_CLEAR);

	if (!sem) return(0);

	sem->s.ss_Link.ln_Type = NT_SIGNALSEM;
	sem->s.ss_Link.ln_Pri  = pri;

	/* Note that the name must be copied as the original creator
	 * of the semaphore might exit.  We can't leave a pointer to
	 * the original data segment lying around after the program
	 * has exited.  
	 */
	if (name) {
		buf = AllocMem(strlen(name)+1, MEMF_PUBLIC);
		if (!buf) {
			FreeMem(sem, sizeof(struct XSSemaphore));
			return(0);
		}
		strcpy(buf,name);
		sem->s.ss_Link.ln_Name = buf;
		myAddSemaphore(sem);
	}
	else
		InitSemaphore(sem);

	sem->Users = 1;

	return(sem);
}

void DeleteSSemaphore(sem)
struct XSSemaphore *sem;
{
	char *name;

	if (!sem) return;

	name = sem->s.ss_Link.ln_Name;

	if (name) {
		myRemSemaphore(sem);
		FreeMem(name, strlen(name) + 1 );
	}

	FreeMem(sem, sizeof(struct XSSemaphore));
}


/* The bindings for "AddSemaphore" are broken in 1.2 Amiga.lib
 *
 * Dale's handcrafted AddSemaphore(). 
 */

myAddSemaphore(ss)
struct SignalSemaphore *ss;
{
    extern struct ExecBase *SysBase;

    InitSemaphore(ss);
    Forbid();
    Enqueue(&SysBase->SemaphoreList,ss);
    Permit();
}

/* The "C" interface code for the following semaphore routines is broken in
 * Amiga.lib and in the Aztec C release 3.4a.
 *
 * @ Lattice people should cut and paste the assembler into a separate file.
 */
 
#if AZTEC_C     
#asm
; The exec.library function "AddSemaphore" is broken in KickStart rel. 33.180
;
; The Aztec bindings think that they should be using a0 to pass the
; argument instead of a1.  This is likely the problem with the Lattice
; bindings.  The Exec AddSemaphore function seems to be just plain broken.
;
;		-Rico
        
        XREF    _SysBase
        XREF    _LVOFindSemaphore
	XREF    _LVORemSemaphore


	XDEF    _myFindSemaphore
	XDEF    _myRemSemaphore

_myFindSemaphore:
	move.l 	4(sp),a1
        move.l 	_SysBase,a6
	jmp 	_LVOFindSemaphore(a6)

_myRemSemaphore:
	move.l 	4(sp),a1
        move.l 	_SysBase,a6
	jmp 	_LVORemSemaphore(a6)
#endasm
#endif
