rokicki@rocky.STANFORD.EDU (Tomas Rokicki) (12/02/87)
/* * bug.c * * The interlock demonstrated by this program took me over a day to find, * so I thought I'd pass it on to save someone else some pain. * * Be careful with functions that use the blitter in tasks that you plan * to RemTask() from the parent (or someone else!) If the blitter is * allocated when the RemTask() is executed, it will stay allocated and * the system will lock up tighter than a drum. To fix, simply Forbid()/ * Permit() around all the blitter using sections (like a sequence of * Move/Draws.) * * Compile and run this program (Manx). If you run it with the command: * bug * with no arguments, it locks up the machine. If you run it instead with * bug forbid * it will return correctly. The only difference between the two actions * is the void subroutine it launches; one has the Forbid()/Permit() pair, * the other doesn't, thus showing the problem. */ #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/memory.h> #include <exec/interrupts.h> #include <exec/ports.h> #include <exec/libraries.h> #include <exec/io.h> #include <exec/tasks.h> #include <exec/execbase.h> #include <intuition/intuition.h> #include <libraries/dos.h> #include <graphics/gfxmacros.h> #include "functions.h" struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; struct Task *task ; struct RastPort *rastport ; struct Screen *screen ; char *stackmem ; #define STACKSIZE (1000) static struct NewScreen newscreen = { 0, 0, 640, 400, 1, 0, 1, HIRES | LACE | SCREENQUIET, CUSTOMSCREEN, NULL, NULL, NULL, NULL } ; void task1() { geta4() ; while (1) { Move(rastport, 10L, 10L) ; Draw(rastport, 600L, 390L) ; } } void task2() { geta4() ; while (1) { Forbid() ; Move(rastport, 10L, 10L) ; Draw(rastport, 600L, 390L) ; Permit() ; } } main(argc, argv) int argc ; char *argv[] ; { if ((IntuitionBase= (struct IntuitionBase *)OpenLibrary("intuition.library",0L))==NULL || (GfxBase= (struct GfxBase *)OpenLibrary("graphics.library",0L))==NULL || (screen = OpenScreen(&newscreen))==NULL || (task = (struct Task *)AllocMem((long)sizeof(struct Task), MEMF_CLEAR | MEMF_PUBLIC))==NULL || (stackmem = (char *)AllocMem((long)STACKSIZE, MEMF_CLEAR))==NULL) cleanup() ; rastport = &(screen->RastPort) ; task->tc_Node.ln_Pri = -20 ; task->tc_Node.ln_Type = NT_TASK ; task->tc_Node.ln_Name = "Line" ; task->tc_SPLower = (APTR)stackmem ; task->tc_SPUpper = task->tc_SPReg = (APTR)(stackmem + STACKSIZE - 8) ; AddTask(task, (argc > 1 ? task2 : task1), 0L) ; Delay(50L) ; RemTask(task) ; cleanup() ; } cleanup() { if (task) FreeMem(task, (long)sizeof(struct Task)) ; if (stackmem) FreeMem(stackmem, (long)STACKSIZE) ; if (screen) CloseScreen(screen) ; if (IntuitionBase) CloseLibrary(IntuitionBase) ; if (GfxBase) CloseLibrary(GfxBase) ; exit(0) ; }
grant@hpindda.HP.COM (Grant Haidinyak) (12/03/87)
>* Be careful with functions that use the blitter in tasks that you plan >* to RemTask() from the parent (or someone else!) If the blitter is >* allocated when the RemTask() is executed, it will stay allocated and >* the system will lock up tighter than a drum. To fix, simply Forbid()/ >* Permit() around all the blitter using sections (like a sequence of >* Move/Draws.) Wouldn't it be nice, if instead of doing a Forbid/Permit and locking out the multitasking feature of the Amiga, have a pair of routines, something like SetCritical/ResetCritical, which wouldn't allow a task to be aborted Just a thought (my one for the week) Grant
ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (12/05/87)
In article <787@rocky.STANFORD.EDU> rokicki@rocky.STANFORD.EDU (Tomas Rokicki) writes: >/* > * bug.c > * > * Be careful with functions that use the blitter in tasks that you plan > * to RemTask() from the parent (or someone else!) If the blitter is > * allocated when the RemTask() is executed, it will stay allocated and > * the system will lock up tighter than a drum. To fix, simply Forbid()/ > * Permit() around all the blitter using sections (like a sequence of > * Move/Draws.) > * > * Compile and run this program (Manx). If you run it with the command: > * bug > * with no arguments, it locks up the machine. If you run it instead with > * bug forbid > * it will return correctly. The only difference between the two actions > * is the void subroutine it launches; one has the Forbid()/Permit() pair, > * the other doesn't, thus showing the problem. > */ Dale pointed this out to me when I asked if I could asynchronously longjmp() out of a graphics operation. Here are the things he enumerated: -------- Graphics may allocate tmp memory for some blit operations. In general we would like it to give it back. Graphics may own the blitter, we would want it to disown it also. Graphics calls LockLayer if the rastport has a layer attached to it. Actually it calls LockLayerRom which is a simple ObtainSemaphire [sic]. The Layerlibrary actually calls the graphics library to lock the layer so cosmic serenity is not disrupted. -------- It should also be noted that asynchronously RemTask()ing a running program is *NOT* kosher under any circumstances. Even if it's just a CPU-bound goodie that spins in place, it still isn't a good idea (in my opinion). The aforementioned Cosmic Serenity could be disrupted in ways we don't understand yet. That's why all sub-tasks that I write have some way of being terminated externally (via message or signal). Once I'm sure the task has completed, then I RemTask() it. I used to do a Wait(0L) to stop the task before RemTask()ing it, but the Manx CreateTask() function does things differently now, so I let the task exit, and it kills itself nicely (or something like that). God, I'm tired..... _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ Leo L. Schwab -- The Guy in The Cape ihnp4!ptsfa -\ \_ -_ Recumbent Bikes: dual ---> !{well,unicom}!ewhac O----^o The Only Way To Fly. hplabs / (pronounced "AE-wack") "Although there are technical differences between the quality of images created on the Amiga and on our system, we feel that viewers could be misled to believe otherwise, even with your disclaimers to the contrary." -- Ralph J. Guggenheim, Pixar "AMIGAS!!?? I thought we bought Pixars! Geez, I can't tell the difference." -- Max Headroom Producer
peter@sugar.UUCP (Peter da Silva) (12/07/87)
> >* If the blitter is allocated when the RemTask() is executed, it > >* will stay allocated and the system will lock up. To fix, simply > >* Forbid()/Permit() around all the blitter using sections... > Wouldn't it be nice, if instead of doing a Forbid/Permit and locking out > the multitasking feature of the Amiga, have a pair of routines, something > like SetCritical/ResetCritical, which wouldn't allow a task to be aborted... Even better: why don't you and your task use semaphores or signals to let the parent task know when it can remove the child task. Here's a scenario that works out just fine in real life: The parent sends the child a message. The child does its work and sends the message back. Then the parent RemTask()s the child. Just like the WorkBench does it. -- -- Peter da Silva `-_-' ...!hoptoad!academ!uhnix1!sugar!peter -- Disclaimer: These U aren't mere opinions... these are *values*.
dillon@CORY.BERKELEY.EDU (Matt Dillon) (12/08/87)
Assumptions: PopCLI-like program where child and parent are in the same segment. Where Parent must be able to kill the child and know exactly when the child no longer exists. Parent spawns child at low priority Child runs Parent wants to kill child: Parent sends child a prearranged signal and increases child's priority. Parent then Waits for a reply. Child sends Parent a prearranged signal when it is ready to die Child does a Wait(0). Parent kills child. (rather than child kills itself). As far as RemTask goes, it doesn't care if the task in question is waiting or running.... or in the middle of a critical library call! -Matt
grant@hpindda.HP.COM (Grant Haidinyak) (12/10/87)
>Even better: why don't you and your task use semaphores or signals >to let the parent task know when it can remove the child task. The reason you would want an OS supported way of setting a critical region it is that what if there is no explicit syncronization between the parent and the child (i.e. exec), then how do you abort the task/process. The scenario that I am talking about is: Joe Helpless User executes a program, for some reason, the program goes into a tight loop, Joe then types a control-c (or what ever), and the program is stopped, and ALL of the resources that it had allocated would be freed. Wouldn't it be nice.... Hey, if unix can do it why cann't we!! >-- Peter da Silva `-_-' ...!hoptoad!academ!uhnix1!sugar!peter Grant
ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (12/11/87)
In article <8712081929.AA17657@cory.Berkeley.EDU> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes: > Parent spawns child at low priority > Child runs > Parent wants to kill child: > Parent sends child a prearranged signal and increases child's > priority. Parent then Waits for a reply. > Child sends Parent a prearranged signal when it is ready to die > Child does a Wait(0). > Parent kills child. (rather than child kills itself). > A more general (and better, in my opinion) way to go about this is as follows: ------- PARENT CHILD Parent sends kill signal to child. Parent waits for reply. Child receives kill signal. Child cleans up anything it allocated. Child Forbid()s. Child replies signal. Child waits for Godot (Wait (0L)). Parent receives reply. Parent RemTask()s child. ------- For AmigaDOS processes, the procedure is slightly different: ------- PARENT CHILD Parent sends kill signal to child. Parent waits for reply. Child receives kill signal. Child cleans up anything it allocated. Child Forbid()s. Child replies signal. Child **exits**. This preserves DOS's sanity. Parent receives reply. Parent **UnloadSeg()s** child. -------- Comments? _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ Leo L. Schwab -- The Guy in The Cape ihnp4!ptsfa -\ \_ -_ Recumbent Bikes: dual ---> !{well,unicom}!ewhac O----^o The Only Way To Fly. hplabs / (pronounced "AE-wack") "Work FOR? I don't work FOR anybody! I'm just having fun." -- The Doctor
flynn@oscvax.UUCP (Flynn D. Fishman) (12/19/87)
In article <4696@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes: > A more general (and better, in my opinion) way to go about this is >as follows: >------- > PARENT CHILD > Parent sends kill signal to > child. > Parent waits for reply. > Child receives kill signal. > Child cleans up anything it > allocated. > Child Forbid()s. ^^^^^^^^^^^^^^^^^ > Child replies signal. > Child waits for Godot (Wait (0L)). > Parent receives reply. > Parent RemTask()s child. >------- My question is this, why should the Child Forbid(), and who does the Permit() is that Automatic. I do not see any reason why the Child should Forbid() since the parent is locked waiting for a reply anyways. Also. I am using Manx 3.4a, and have my Task in the same file as the parent. What I do is have my task check to see if he/she got a message directing him/her to die (exit nicely). When he/she gets the message (s)he clears all the memory (s)he allocated, etc. and just falls of the end of the world, either by letting her code run out or via return; I think this has its advantages in that a task can be killed by some other process if neccessary. As well since I am a Multitasking Novice, (ok novice), is RemTask neccessary? And what exactly does it do. On another note (c minor) if I spawn my task as indicated before, (using only local variables) and the mainline goes bye bye, does my system guru and start spouting smoke or does the memory used by my task still exist? (I vote for the guru but I have not had time to check and see) p.s. I just got a 1084, and do not seem to have any problems with it, In fact I like the pictures 10+ times better that the 2002 at work (but the 2002 seems to be worse than normal) -- Please ignore any spelling, or grammarical errors, I am using a really lousy keyboard. (The fact that I do not speak to goodlooking english does not help the matter much either) _Flynn D. Fishman, Esquire (Ontario Science Centre) ...{watmath,ihnp4,decvax,cbosgd}!utgpu!oscvax!flynn Soon to be (as I am going back to school) ...{watmath,ihnp4,decvax,cbosgd}!watcgl!fdfishman
ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (12/21/87)
In article <554@oscvax.UUCP> flynn@oscvax.UUCP (Flynn D. Fishman) writes: >In article <4696@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes: >> PARENT CHILD >> Parent sends kill signal to >> child. >> Parent waits for reply. >> Child receives kill signal. >> Child cleans up anything it >> allocated. >> Child Forbid()s. > ^^^^^^^^^^^^^^^^ >> Child replies signal. >> Child waits for Godot (Wait (0L)). >> Parent receives reply. >> Parent RemTask()s child. > >My question is this, why should the Child Forbid(), and who does the >Permit() is that Automatic. I do not see any reason why the Child should >Forbid() since the parent is locked waiting for a reply anyways. > The Permit() is implicit when you Wait() for Godot (#define GODOT 0L), or when you exit. Consider the following scenario. You are the parent. You spawn a child process with a priority lower than your own. While you (the parent) are waiting for events, the child gets to run. Eventually, you wish to kill off the child. So you send a pre-arranged signal to the child and wait for the reply signal. The child cleans up, Forbid()s, replys the signal, and then either Wait()s or exits. By calling Forbid(), the child prevents the parent from waking up immediately on the signal. If the Forbid() weren't there, the child would get pre-empted the instant it sent the signal off, and the parent would wake up and blissfully remove the child task before it had truly finished executing. >On another note (c minor) if I spawn my >task as indicated before, (using only local variables) and the mainline >goes bye bye, does my system guru and start spouting smoke or does the >memory used by my task still exist? (I vote for the guru but I >have not had time to check and see) > You have cast your ballot correctly. Since your child task is part of the parent's executable image, the DOS can't tell the difference when your parent exits, and will unload both of you. You will Guru either immediately or when the next program overlays the memory you're running out of. The latter case will be much more difficult to track down as a bug. Moral: Unless your subtask was LoadSeg()d, terminate all your children before you terminate yourself. _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ Leo L. Schwab -- The Guy in The Cape ihnp4!ptsfa -\ \_ -_ Recumbent Bikes: dual ---> !{well,unicom}!ewhac O----^o The Only Way To Fly. hplabs / (pronounced "AE-wack") "Work FOR? I don't work FOR anybody! I'm just having fun." -- The Doctor