Path: 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 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