From: caw@miroc.Chi.IL.US (Christopher A. Wichura)
Newsgroups: comp.sys.amiga.programmer
Subject: Re: IPC and shared memory
Distribution: world
X-NewsSoftware: GRn-beta 1.16g (28.12.92) by Michael B. Smith & Mike Schwartz
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
Date: 31 Dec 92 09:02:22 CST

In article <Jean-Guy_Speton.056u@tvbbs.wimsey.bc.ca> Jean-Guy_Speton@tvbbs.wimsey.bc.ca (Jean-Guy Speton) writes:
> I am looking for information on inter-process communications (IPC) and

IPC is real easy.  The Amiga has had message passing functions since day one.

> shared memory functions.  Do the Amiga libraries have any functions along

Shared memory is a little trickier.  The best way to go about this (that I've
come up with) is to allocate a chunk of memory and protect it with a
semaphore.  Subsequent instances of the program would not allocate but rather
simply find the semaphore and use that as the chunk base.  When you free the
semaphore, if you are the last instance then you free the memory chunk.

Here's some code I wrote to do this a little while back:

/* structure for handling our public semaphores. */
struct MyPublicSema {
	struct SignalSemaphore mps_Sema;
	UBYTE *mps_SemaData;
	ULONG mps_UseCount;
	UBYTE mps_SemaName[4];
};

 /*
  * this routine sets up a public semaphore.  If it already exists we just
  * bump its use count up by one.  Otherwise we have to allocate a new one.
  * semaDataSize is a number of bytes to allocate with the semaphore that can
  * be shared by those locking the semaphore.  Let's us have "global" data
  * between seperate processes.  These routines don't care what is put in
  * this data space.  It will be cleared to 0s when first allocated, though.
  */

struct MyPublicSema *InitMyPublicSemaphore(STRPTR semaName, ULONG semaDataSize)
{
	struct MyPublicSema *theSema;
	ULONG semaNameLen;

	if (!semaName)
		return (struct MyPublicSema *) NULL;

	/*
	 * we want the semaphore's name to be a longword in length, at least
	 * in space allocated for it
	 */
	semaNameLen = ((strlen(semaName) + 3) / 4) * 4;

	Forbid();
	if (theSema = (struct MyPublicSema *) FindSemaphore(semaName))
		theSema->mps_UseCount++;	/* only safe way to diddle
						 * with this is under
						 * Forbid()... sigh */
	else {

		/*
		 * we didn't find the semaphore around already so we have to
		 * allocate and initialize it
		 */
		if (!(theSema = AllocVec(sizeof(struct MyPublicSema) + semaNameLen + semaDataSize, MEMF_CLEAR | MEMF_PUBLIC))) {
			Permit();
			return (struct MyPublicSema *) NULL;
		}

		strcpy(theSema->mps_SemaName, semaName);
		theSema->mps_Sema.ss_Link.ln_Name = theSema->mps_SemaName;
		if (semaDataSize)
			theSema->mps_SemaData = &theSema->mps_SemaName[semaNameLen];
		theSema->mps_UseCount = 1;

		AddSemaphore(&theSema->mps_Sema);
	}

	Permit();

	return theSema;
}

 /*
  * this dealocates what we got from InitMyPublicSemaphore().  If there are
  * others still using it then just down the use count.  Otherwise remove the
  * semaphore from the public lists and down the use count.
  */

void FreeMyPublicSemaphore(struct MyPublicSema * mySema)
{
	if (mySema) {
		ObtainSemaphore(&mySema->mps_Sema);
		Forbid();
		ReleaseSemaphore(&mySema->mps_Sema);
		if (!--mySema->mps_UseCount) {
			RemSemaphore(&mySema->mps_Sema);
			FreeVec(mySema);
		}
		Permit();
	}
}

To use this, early on in your program you would call InitMyPublicSemaphore()
with the name you want to refer to your shared memory chunk as.  It returns
null if it failed to init.

Anytime you want to access shared memory you would go something like:

	ObtainSemaphore(handle->mps_Sema);
	/* twiddle with the memory by referencing mps_SemaData */
	ReleaseSemaphore(handle->mps_Sema);

Note that semaphores are very quick when there is noone else currently
holding the lock.  Of course, good multitasking ethics dictate that you
ObtainSemaphore() for as little time as possible.

By using Semaphore()s, you protect your public memory from being stomped on
by multiple instances at the same time.  By not using Forbid(), you don't
cause the rest of the system to suffer.

Note that I only needed this memory to be initialized to all zeros for my
program.  If you need something in it initialized to something other than
that, the only safe way to do this is to modify the routines to take a
pointer to a chunk of memory you have already allocated and filled in.  This
eliminates the possibility of you creating the semaphore and then a second
instance started right after it coming along and using the data before you
have had a chance to initialize the data area.  Doing this, however, requires
that you always allocate a chunk of memory (and you must use the exec
allocators to be safe when the process that created the public memory
terminates before the last process that is using it terminates), even when
you probably won't need it (only the first instance would).  If
InitMyPublicSempahore() returns a handle to you, you'd have to check to see
if the mps_SemaData in the handle is the same as the chunk you allocated and
if it isn't you'd free the chunk you allocated.  Not good if you have a large
public chunk.

A better alternative would probably be to have InitMyPublicSemaphore() do an
ObtainSemaphore() after the } and before the Permit().  This way a second
instance couldn't use the sempahore until after you had initialized the data
section.  This requires that you always call ReleaseSemaphore() sometime
shortly after calling InitMyPublicSemaphore(). Actually, this isn't too bad
since you could do something like:

	/* assume struct MyShared is defined somewhere to give easy access
	   to the elements you need in the public data section */

	handle = InitMyPublicSemaphore("name", sizeof(struct MyShared));
	if (!handle)
		panic();

	pubmem = (struct MyShared *) handle->mps_SemaData;

	if (!pubmem->Initialized) {
		/* initialized the data section */
		pubmem->Initialized = TRUE;
	}

	ReleaseSemaphore(handle->mps_Sema);

Then anytime you wanted to use data in the public section it would be easy to
go:

	ObtainSemaphore(handle->mps_Sema);
	pubmem->Foo = BAR;
	ReleaseSemaphore(handle->mps_Sema);

Hope this helps.

-=> CAW

--

Christopher A. Wichura                Multitasking.  Just DO it.
caw@miroc.chi.il.us  (my amiga)                          ...the Amiga way...
u12401@uicvm.uic.edu (school account)
Newsgroups: comp.sys.amiga.programmer,comp.sys.amiga.hardware