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

struct MsgPort *CreatePort();
struct MsgPort *FindPort();
struct XSemaphore *CreateSemaphore();
void DeleteSemaphore();
void *AllocMem();
void cleanup();

struct XSemaphore {
	struct Semaphore s;
	WORD Users;
};

struct MsgPort *port = 0;
struct XSemaphore *sem = 0;
struct Message msg;

/* 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 XSemaphore *)FindPort("my semaphore")))
		sem = CreateSemaphore("my semaphore", 0);
	else
		sem->Users++;

	Permit();

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

	port = CreatePort(0,0);
	if (!port) {
		printf("Error, can't create my reply port\n");
		cleanup();
	}

	msg.mn_Node.ln_Type = NT_MESSAGE;
	msg.mn_Length	    = sizeof(struct Message);
	msg.mn_ReplyPort    = port;

	/* for P & V fans, the Procure & Vacate names are mnemonic */
	/*		       -         -		           */

	for (i=0;i<5;i++) {
		if (!Procure(sem, &msg)) {
			WaitPort(port);	/* wait for the message to come */
			GetMsg(port);	/* get it */
		}

		/* 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 */
		Vacate(sem);

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

	}

	cleanup();

}

void cleanup()
{
	if (port) {
		DeletePort(port);
		port = 0;
	}

	/*
	 * we'd like to use the semaphore to lock out other users here 
	 * but we'd have to somehow P and then not do a V but delete
	 * the port instead for this to work... That hurts me more than
	 * the Forbid() Permit() pair...
	 */

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

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

		sem = 0;
	}
}

struct XSemaphore *CreateSemaphore(name,pri)
char *name;
int pri;
{
	struct XSemaphore *s;
	char *buf;

	s = AllocMem(sizeof(struct XSemaphore), MEMF_PUBLIC|MEMF_CLEAR);
	if (!s)
		return(0);

	s->s.sm_Bids = -1;
	NewList(&s->s.sm_MsgPort.mp_MsgList);
	s->s.sm_MsgPort.mp_Flags	= PA_IGNORE;
	s->s.sm_MsgPort.mp_Node.ln_Type	= NT_SEMAPHORE;
	s->s.sm_MsgPort.mp_Node.ln_Pri	= pri;
	s->Users			= 1;

	/* 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(s, sizeof(struct XSemaphore));
			return(0);
		}
		strcpy(buf,name);
		s->s.sm_MsgPort.mp_Node.ln_Name = buf;
		AddPort(s);	
	}
	return(s);
}

void DeleteSemaphore(s)
struct XSemaphore *s;
{
	char *name;

	if (!s) return;

	name =  s->s.sm_MsgPort.mp_Node.ln_Name;

	if (name) {
		RemPort(s);	/* remove from list of public semaphores */
		FreeMem(name,strlen(name)+1);
	}

	FreeMem(s, sizeof(struct XSemaphore));
}
