Article View: comp.sys.amiga.tech
Article #11933Re: CBM, Why did you make it so hard?
From: Michael "Ford" Ditto
Date: Thu, 23 Mar 1989 17:16
Date: Thu, 23 Mar 1989 17:16
157 lines
7788 bytes
7788 bytes
In article <933@dover.azsps.mot.com> fullmer@dover.UUCP (Glen Fullmer) writes:
>The essential
>question was: How does one maintain session context (reentrancy) in an
>Amiga run-time library when multiple sessions use a set of changing
>globals? There was a couple of suggestions: (1) To require the user
>to create a session context pointer (like Raster-Pointer in Intuition)
>or (2) to maintain a session context table within the library.
Since the reason given for prefering (2) was to eliminate the need for
a user to carry around this useless (as far as the program can see)
pointer, why not solve all the problems mentioned by hiding that
detail in the link part of the library (the part that would normally
just jsr to the shared library routine). Since this is already done
anyway with the library base pointer itself, just make this "context"
pointer a "hidden" variable as well.
>OK, let's try a concrete example. I have a graphics system that
>is a collection of library routines that can be called by the
>user. The user must call "gstart" first. He must call "gend"
>when finished using the library. There is a function called
>"gdraw" that the user can call to draw a line.
/* simplified code -- this is linked with the user's program: */
static struct Library *__glibbase; /* library base */
static void *__context; /* "session handle" for this task */
int gstart()
{
if (!__glibbase) /* open the library */
__glibbase = OpenLibrary("glib.library", 0);
if (__context) /* only one session at a time in this task */
return -1;
__context = _gstart(); /* _gstart is the "glue" function */
return __context != 0;
}
void gend()
{
_gend(__context); /* another glue function */
__context=0; /* we could now gstart() again if desired */
/* perhaps CloseLibrary(...) */
}
void gdraw(arg1, arg2)
{
_gdraw(__context, arg1, arg2); /* another glue function */
}
With that method, you get the best of both worlds:
+ The glib.library itself is effecient.
+ Users have a convenient interface.
+ Users can directly call the _functions if they want to
have multiple sessions.
>So the problem on the Amiga is how to set up a library with
>task-dependent global/persistent storage that, after a
>time-share interrupt or other interrupt, remembers where its
>particular task-dependent global/persistent storage area is when
>it again receives control from the scheduler. I so far have
>placed all of the global/persistent storage items in one
>structure. I can use MemAlloc to allocate storage for the
>area. The problem is how to preserve that pointer through a
>time-share interrupt (or any other sort of asynchronous
>interrupt that takes control away from me, runs something else,
>then returns to me.)
You're trying real hard to find a way to do things the hard way. :-)
Just don't worry about interrupts. You'll never know they happened.
If you have the session pointer in an automatic (stack or register)
variable, you can access it as you like, even if another task is
also running your library. Each task has its own stack and registers,
and you don't need to worry about what happens to yours during or
after an interrupt or task switch.
>During an interrupt, 70 bytes are pushed onto the stack
>representing all of the data registers, address registers,
>status register, and the program counter. When you find
>yourself back in your code after an interrupt, those 70 bytes
>are all you really know.
Well, those bytes + everything else you ever knew. None of your
data will change during an interrupt or task switch unless you
change them.
> (You wouldn't even know where the
>stack was unless one of the registers was dedicated to holding a
>pointer to it.)
No, you could keep a copy of it in any register you like. There
are no "dedicated" registers as far as context switches go. ALL
your registers will be saved. And besides, IT DIDN'T MOVE, so
you know where it is just as well as you did before the interrupt.
> Clearly, the best way to save a pointer to a
>task-dependent global/persistent storage area is to DEDICATE one
>of the registers, say A3, to always holding the pointer to the
>task-dependent global/persistent storage area.
You can do this if you like; but nobody but your code will ever
know or care. You might as well just put it in an automatic
variable, any one will do. Dedicating a register to hold that
pointer whenever in your library code is just another way of
passing a parameter to all the functions in your library. You
still need to load that pointer when your library is called
from the user program, and restore the registers when you return.
> This is how
>Multics achieved what I consider to be true reentrancy -
>reentrancy with per process global/persistent data - and spoiled
>me beyond words... (See page 68 of "Timesharing System Design
>Concepts", Richard Watson, McGraw-Hill, 1970. The Multics
>operating system, by convention, dedicates a hardware register
>called a "link pointer" to the "linkage information", i.e., the
>task-dependent storage. This is always restored after an
>interrupt so the task ALWAYS knows where its own storage is.
ALL registers are restored after an interrupt (in Exec and any
other OS I've used; I really can't imagine that Multics is any
different). I think what you are asking for has nothing to do
with interrupts; you just want this magic pointer to always be
there for you. The way to make this happen is to pass it in
as an argument to all your functions. The user doesn't have to
do this, you could do it at the entry to the library or in the
glue routines that the user links with.
[ description of using FindTask(0) to recognize a previous
caller and search for its state information ]
This would work also, but it seems that you are stuck on the idea
of closely tying a "session" to a task, limiting each task to
only one active session. Why go to the trouble of "True
Reentrancy" without allowing multiple sessions within a task? If
you want to provide a convenient interface, that's great, but keep
the basic structure as flexible and efficient as possible, and put
the convenience as a layer on top. That way when a user comes
along who prefers flexibility to convenience, your library won't
be in the way.
>Is there an easier way to do what I want to do?
I think you're trying to do many unnecessary things. It's obvious
that you know about the complexities of reentrant programming, but
I think you must have learned them on a very strange system. :-)
Perhaps it would help to get the source code to a library and see
how things are typically done.
--
-=] Ford [=-
"The number of Unix installations (In Real Life: Mike Ditto)
has grown to 10, with more expected." ford@kenobi.commodore.com
- The Unix Programmer's Manual, ...!sdcsvax!crash!kenobi!ford
2nd Edition, June, 1972. ditto@cbmvax.commodore.com
Technical Details
Message-ID:
<6379@cbmvax.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!oliveb!amiga!cbmvax!dittoReferences:
<913@dover.uucp> <3623@amiga.UUCP> <933@dover.azsps.mot.com>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!oliveb!amiga!cbmvax!ditto From: ditto@cbmvax.UUCP (Michael "Ford" Ditto) Newsgroups: comp.sys.amiga.tech Subject: Re: CBM, Why did you make it so hard? Summary: It's not *that* difficult Keywords: Graphics Libraries, reentrancy Message-ID: <6379@cbmvax.UUCP> Date: 23 Mar 89 17:16:02 GMT References: <913@dover.uucp> <3623@amiga.UUCP> <933@dover.azsps.mot.com> Reply-To: ditto@cbmvax.UUCP (Michael "Ford" Ditto) Organization: Commodore Technology, West Chester, PA Lines: 157