schwager@m.cs.uiuc.edu (Michael Schwager) (05/24/91)
Hi, I'm looking for guidelines about using tasks within C programs. C code examples would help. I've got Aztec C 5.0d, and their little beep.c example only goes so far. For example, I wonder: Can I call other functions from within my task? What do I need to be careful of (besides changing system variables)? Also, can I pass variables to a Task, like this: CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK); I'm getting random (I think) address error Guru's when I try to get too fancy with Tasks. Too fancy means: I want to call another function from within it. Here's a concrete example (something I'm trying now). Given a program, source code in at least two files. The first file contains a coupla functions: ************ file 0 *********** func0 () /* main () or whatever */ { blah; } func1 (number) int number; { struct Task *myTCB; blah; myTCB = func2 (); loop: { blah; if (some_condition) DeleteTask (myTCB); } } ************ file 0 *********** The second, function(s) and a Task: ************ file 1 *********** struct Task * func2 () { struct Task *someTCB; blah; someTCB = CreateTask ("Name", 0, func3, BIGSTACK); blah; return (someTCB); } func3 () /* It's a task! */ { blah; func1 (number); /* Oooo... can I do this? Say func1() only has automagic variables. */ blah; } ************ file 1 *********** Thanks for the help. -Mike Schwager | Machine: Amiga 500, 3 MB RAM/30 HD INTERNET:schwager@cs.uiuc.edu | Bike: '83 Kawasaki KZ750 LTD UUCP:{uunet|convex|pur-ee}!uiucdcs!schwager| Band: Poi Dog Pondering // BITNET:schwager@mike.cs.uiuc.edu | Hero: Robert Bly \\ // University of Illinois, Dept. of Comp. Sci.| DoD: #0301 \X/Amiga
markv@kuhub.cc.ukans.edu (05/25/91)
In article <1991May24.160440.374@m.cs.uiuc.edu>, schwager@m.cs.uiuc.edu (Michael Schwager) writes: > > Hi, > I'm looking for guidelines about using tasks within C programs. C code > examples would help. I've got Aztec C 5.0d, and their little beep.c > example only goes so far. For example, I wonder: Can I call other > functions from within my task? Yes, you can call other functions and access global variables. If using base-relative addressing, do a geta4(); at the beginning of your task code. Also, stack checking should be off in any module that has code that will get run in the context of a sub-task. > What do I need to be careful of (besides changing system variables)? Don't call DOS!!! This also means don't call things like OpenLibrary() unless you know the library is in memory. Many DOS functions that used to let you get away with being a task in 1.3 will barf in 2.0. So don't call DOS or only OpenXXX call that could require something to be loaded off the disk. If you do need access to such devices or libs, have the parent open it first to load it, and hold it open, then when the child opens it, it will be in memory. > Also, can I pass variables to a Task, like this: > CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK); Not normally. Create just just creates the task struct, allocates the stack, and jumps to the beginning PC. Parameters are before offset to the stack base, so they contain garbage. If you manually create a task and add it with AddTask(), you could fudge the stack and put a dummy parameter list at the base of the stack. Since the task code allocates the auto variables, all local variables are okay (of course static variables are still that). > I'm getting random (I think) address error Guru's when I try to get too > fancy with Tasks. Too fancy means: I want to call another function from > within it. Hmmm, this should work. It works fine for me under SAS with both PC rel and 32 bit abs function jumps. > func3 () /* It's a task! */ > { > blah; > func1 (number); /* Oooo.can I do this? Say func1() only has automagic > variables. */ No, this is okays long as you have enough stack. The code in func3() will allocate and clean up the parameters on the stack. Code in func1() at begin and end will allocate and cleanup auto variables (usually via LINK and UNLINK). > blah; Zapp. If I read this right your task is running right off the end. If it does this it hits a return. Return to where? There isn't a valid address, so it RETs into space. A task function should be an endless loop with some kind of check for an exit condition. Since RemTask(called by DeleteTask) can be picky about conditions, it should call Wait(0L) (wait forever) to put itself in a safe state to be deleted by the parent. Alternately a task can RemTask(0L) itself, but this doesn't clean up the allocations for the stack and TCB. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mark Gooderum Only... \ Good Cheer !!! Academic Computing Services /// \___________________________ University of Kansas /// /| __ _ Bix: mgooderum \\\ /// /__| |\/| | | _ /_\ makes it Bitnet: MARKV@UKANVAX \/\/ / | | | | |__| / \ possible... Internet: markv@kuhub.cc.ukans.edu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
schwager@m.cs.uiuc.edu (Michael Schwager) (05/25/91)
schwager@m.cs.uiuc.edu (Michael Schwager) writes: Oops I made a mistake in my pseudo-code. My task really looks like this: func3 () /* It's a task! */ { endless_loop: /* depends on the parent task to annihilate this task when the user hits a gadget */ { blah; func1 (number); /* Oooo... can I do this? Say func1() only has automagic variables. */ Forbid (); DrawImage (in_some_Window_declared_globally); blah; Permit (); } } ************ file 1 *********** Also, I RTFRKM's and know about the problems with calling DOS functions; I shan't do that! Thanks for all the replies so far; I'm beginning to understand these blasted things.... -Mike
bj@cbmvax.commodore.com (Brian Jackson) (05/25/91)
In article <1991May24.120133.31035@kuhub.cc.ukans.edu> markv@kuhub.cc.ukans.edu writes: >In article <1991May24.160440.374@m.cs.uiuc.edu>, schwager@m.cs.uiuc.edu (Michael Schwager) writes: >> >> Hi, >> I'm looking for guidelines about using tasks within C programs. > ... >> What do I need to be careful of (besides changing system variables)? > >Don't call DOS!!! This also means don't call things like >OpenLibrary() unless you know the library is in memory. Many DOS >functions that used to let you get away with being a task in 1.3 will >barf in 2.0. So don't call DOS or only OpenXXX call that could >require something to be loaded off the disk. If you do need access to >such devices or libs, have the parent open it first to load it, and >hold it open, then when the child opens it, it will be in memory. Yes, and don't call anything that might, in turn, call DOS. Be aware that there are some libraries that want 'task-specific' information and the above trick will be less than optimal in those cases. :) It's really better in this case to just create a new Process instead. Under 1.3 this is a tad arcane (but not really that hard) and under 2.0 it's cake. >> Also, can I pass variables to a Task, like this: >> CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK); Ths easiest way to do this (for me, anyway :)) is to have the child task create a Message Port for such things. That way, you can make up your own data structure in the parent, create the task, fill in the structure with the data that the child needs and then PutMsg() the thing to the child. You pretty much have to do this anyway since there needs to be some mechanism to tell the child "we're going away now." >Mark Gooderum Brian ----------------------------------------------------------------------- / Brian Jackson Software Engineer, Commodore-Amiga Inc. GEnie: B.J. \ \ bj@cbmvax.cbm.commodore.com or ...{uunet|rutgers}!cbmvax!bj / / "Anyone considering the purchase of a high-end Macintosh or IBM- \ \ compatible computer should try an Amiga 3000 for a few hours before / / making the final decision." - Consumer Reports ('91 Buying Guide) \ -------------------------------------------------------------------------
ronald@ecl014.UUCP (Ronald van Eijck) (05/26/91)
In article <1991May24.192517.9935@m.cs.uiuc.edu> schwager@m.cs.uiuc.edu (Michael Schwager) writes: >schwager@m.cs.uiuc.edu (Michael Schwager) writes: > >Oops I made a mistake in my pseudo-code. My task really looks like this: > >func3 () /* It's a task! */ >{ > endless_loop: /* depends on the parent task to annihilate this task > when the user hits a gadget */ > { > blah; > func1 (number); /* Oooo... can I do this? Say func1() only has automagic > variables. */ > Forbid (); > DrawImage (in_some_Window_declared_globally); > blah; > Permit (); > } >} >************ file 1 *********** > >Also, I RTFRKM's and know about the problems with calling DOS functions; I >shan't do that! > >Thanks for all the replies so far; I'm beginning to understand these >blasted things.... >-Mike If you use globals you should start your task by calling geta4() this fixes your a4 register so it points to the middle of your global data area. another solution is to use the large code/data memory model. Hope this helps, -- +-------------------------------------------------------------------------+ | Ronald van Eijck {eunet!}hp4nl!cbmnlux!ecl014!ronald | | | | We do the impossible at once for a miracle we need a little more time | +-------------------------------------------------------------------------+
fjrei@kbsaar.UUCP (Franz-Josef Reichert) (05/27/91)
In article <1991May24.160440.374@m.cs.uiuc.edu> schwager@m.cs.uiuc.edu (Michael Schwager) writes: > >Hi, >I'm looking for guidelines about using tasks within C programs. C code >examples would help. I've got Aztec C 5.0d, and their little beep.c >example only goes so far. For example, I wonder: Can I call other >functions from within my task? What do I need to be careful of (besides >changing system variables)? Also, can I pass variables to a Task, like >this: > > CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK); No, you can't do that. But you may pass arguments on the task's stack before calling exec's AddTask(). So you can't use plain CreateTask() as given by the library. Do the following modification to drop your arguments (assuming they are ULONG's): *(--((ULONG*)newTask->tc_SPReg)) = var2; *(--((ULONG*)newTask->tc_SPReg)) = var1; *(--((ULONG*)newTask->tc_SPReg)) = var0; >I'm getting random (I think) address error Guru's when I try to get too >fancy with Tasks. Too fancy means: I want to call another function from >within it. No problem at all. Provide a synchronisation between parent and child because parent task has to wait for the child task to complete. Otherwise, it will unload the child's code and data space on parent's exit, which will probably make your program fail. Any global data must be accessed properly. Use absolute adressing or 'geta4()' on function entry. >-Mike Schwager | Machine: Amiga 500, 3 MB RAM/30 HD -- Best regards, Franz-Josef Reichert VOICE: +49 6805 7417 Kuchlingerstrasse 13 UUCP: ...uunet!cbmvax!cbmehq!cbmger!kbsaar!fjrei D-6601 Kleinblittersdorf GERMANY
trantow@csd4.csd.uwm.edu (Jerry J Trantow) (05/28/91)
In article <1991May24.160440.374@m.cs.uiuc.edu> schwager@m.cs.uiuc.edu (Michael Schwager) writes: > >Hi, >I'm looking for guidelines about using tasks within C programs. C code >examples would help. I've got Aztec C 5.0d, and their little beep.c >example only goes so far. For example, I wonder: Can I call other >-Mike Schwager | Machine: Amiga 500, 3 MB RAM/30 HD I would be very wary of the beep.c example if it is the original by Sam Dicker. I looked at that about 1 1/2 years ago and found several problems with it. I don't recall all of the problems, but here is my modified version of it. I used this as a starting point for a program using a task to animate a pointer and I have what I feel is a much better task example, but I'm still hoping it will show up in AC. (Submitted 1 year ago in July, accepted in December, and I'M STILL WAITING!) In the meantime, here is my modified beep.c. /***************************************************************************** * VT100-like Beep Sound Example 22 February 1986 * Copyright (c) 1986 Sam Dicker, Commodore/Amiga Inc. 25 Jan 90 Heavily hacked by JJT since Sam Dicker programs for shit * * Calling VTBeep() starts the sound which stops when it times out. * Calling it again, while it is playing, only restarts the timer. * Call KillVTBeep() to kill any beeps in progress before exiting your * main program. *****************************************************************************/ #include "functions.h" #include "exec/types.h" #include "exec/errors.h" #include "exec/memory.he "devices/timer.h" #include "devices/audio.h" #include "libraries/dos.h" #define BEEPNAME "JJTBeep" #define BEEPSIZE 10L #define BEEPFREQ 1000L #define COLORCLOCK 3579545L #define SIGB_BEEP 31L #define SIGF_BEEP (ULONG)(1L << 31L) #define SIGF_PORT (ULONG)(1L << replyPort->mp_SigBit) #define NOT_DONE (LONG)(FALSE) /* useful for CheckIO() */ #defONG CreatePort(); */ /* channel allocation map (try left channel; if unavailable try right) */ UBYTE allocationMap[] = { 1, 8, 2, 4 }; /* make beep sound */ LONG VTBeep() { struct Task *beepTCB; VOID beepTask(); Forbid(); if ((beepTCB = (struct Task *)FindTask(BEEPNAME))== NULL) { beepTCB = (struct Task *)CreateTask(BEEPNAME, 0L,(LONG)beepTask,500L); /* printf("Spawned\n"); */ } else { Signal((struct Task *)beepTCB,SIGF_BEEP); /* printf("Signalled\n"); */ } Permit(); return((LONG)(beepTCB != NULL)); } /* kill any beep sounds in progress. This is necessary before exiting the * main program; otherwise, if a beep is playing, when the beep times out * and the t may be gone */ LONG KillVTBeep() { struct Task *beepTCB; do { Forbid(); if ((beepTCB = (struct Task *)FindTask((char *)BEEPNAME)) != NULL) { Signal((struct Task *)beepTCB,(LONG)SIGBREAKF_CTRL_C); /* printf("Kill the task\n"); */ Delay(10L); } Permit(); } while (beepTCB != NULL); /* if it existed, look for it again (Another copy perhaps)*/ } /* beep sound child task */ VOID beepTask() { struct MsgPort *replyPort; struct timerequest *timerIOB; struct IOAudio *audioIOB; UBYTE *beepWave; struct Message *myIO; ULONG signals; geta4(); if ((LONG)AllocSignal(SIGB_BEEP) == SIGB_BEEP) { replyPort = (struct MsgPort *)CreatePort((char *)NULL,0L); if (replyPort != NULL) { timerIOB = (structt *)CreateExtIO((APTR)replyPort,(LONG)sizeof(struct timerequest)); if (timerIOB != NULL) { if ((LONG)OpenDevice((char *)TIMERNAME,(LONG)UNIT_VBLANK,(APTR)timerIOB,0L) == 0L) { timerIOB->tr_node.io_Command = TR_ADDREQUEST; beepWave = (UBYTE *)AllocMem((LONG)BEEPSIZE,(LONG)MEMF_CHIP|MEMF_CLEAR); if (beepWave != NULL) { beepWave[0] = 127; beepWave[1] =-128; audioIOB=(struct IOAudio *)CreateExtIO((struct MsgPort *)replyPort,(LONG)sizeof(struct IOAudio)); if (audioIOB != NULL) { audioIOB->ioa_Request.io_Message.mn_Node.ln_Pri =85; audioIOB->ioa_Data = allocationMap; audioIOB->ioa_Length = sizeof(allocationMap); /* allocate */ if ((LONG)OpenDevice(AUDIONAME, 0L, audioIOB, 0L) == 0L) { audioIOB->ioa_Request.io_Command = CMD_WRITE; audioIOB->ioa_Request.io_Flags = ADIOF_PERVOL; audioIOB->ioa_Data = beepWave; audioIOB->ioa_Length = BEEPSIZE; audioIOB->ioa_Period =COLORCLOCK / (BEEPSIZE * BEEPFREQ); audioIOB->ioa_Volume = 64; audioIOB->ioa_Cycles=0; timerIOB->tr_time.tv_secs = 0; timerIOB->tr_time.tv_micro = 1L; SendIO(timerIOB); /* initializes timer IO */ do { Forbid(); signals=(LONG)Wait((LONG)(SIGBREAKF_CTRL_C|SIGF_BE GetMsg(replyPort); /* doesn't matter what order they come off */ AbortIO(audioIOB); GetMsg(replyPort); } } else { if ((signals&SIGF_BEEP)!=0L) /* start or restart a beep */ { if (CheckIO(tim* these values get destroyed */ timerIOB->tr_time.tv_micro = 250000L; SendIO(timerIOB); /* start a new timer */ } else /* Signal came from the ReplyPort */ { /* timerIOB returned */ myIO=(struct Message *)CheckIO(timerIOB); if (myIO != NULL) { Remove(replyPort->mp_MsgList,myIO); AbortIO(audioIOB); } else /* audioIOB returned */ { AbortIO(timerIOB); GetMsg(replyPort); } } } Permit(); } while ((signals & SIGBREAKF_CTRL_C) == 0L); /* clean up */ /* closing the audio device kills the sound * and frees the channels */ CloseDevice(audioIOB); } DeleteExtIO(audioIOB); } FreeMem((UBYTE *)beepWave, BEEPSIZE); } CloseDevice(timerIOB); } DeleteExtIO(timerIOB); } DeletePort(replyPort); } } } main() { VTBeep(); VTBeep(); KillVTBeep(); } Jerry J. Trantow | I swear by my life and my love of it, 8967 N. Pelham Parkway | that I will never live for the sake of another man, Milwaukee, Wi 53217-1954 | nor ask another man to live for mine. (414) 228-4689 | Ayn Rand _____________________________________________________________________________
phorgan@cup.portal.com (Patrick John Horgan) (05/30/91)
Mark Gooderum said: >A task function should be an endless loop with some kind of check for >an exit condition. Since RemTask(called by DeleteTask) can be picky >about conditions, it should call Wait(0L) (wait forever) to put itself >in a safe state to be deleted by the parent. Alternately a task can >RemTask(0L) itself, but this doesn't clean up the allocations for the >stack and TCB. CreateTask and DeleteTask are not system calls, but rather library routines provided by your friendly C compiler folks. I rewrote mine to allocate the memory for the stack, the TCB and the name of the task as part of the tc_MemEntry list so that when a RemTask is done the memory is freed by the operating system. I originally started mucking around with it because Manx's routine didn't allocate memory for the name pointer passed to it, it just stored the string address passed to it into the Nodes name. This non-reentrency broke my code which spawned many tasks (alright it only made all the tasks have the same name, but debugging was weird!). I sent the new code to Manx, but haven't checked since 5.0 came out to see if they included it. I also sent them Create/Delete Port code to replace theirs with the same problem. I haven't checked to see if they used it either. It's easy (just a few minutes after using the CreateTask in the RKM's as a starting point), to create the CreateTask that hangs all the neccessary memory allocations off of the Task structure, and it's so elegant. Then the Task can just RemTask itself:) Patrick Horgan pjh70@zeus.ras.amdahl.com -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mark Gooderum Only... \ Good Cheer !!! Academic Computing Services /// \___________________________ University of Kansas /// /| __ _ Bix: mgooderum \\\ /// /__| |\/| | | _ /_\ makes it Bitnet: MARKV@UKANVAX \/\/ / | | | | |__| / \ possible... Internet: markv@kuhub.cc.ukans.edu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
peter@sugar.hackercorp.com (Peter da Silva) (05/31/91)
In article <42787@cup.portal.com> phorgan@cup.portal.com (Patrick John Horgan) writes: > It's easy (just a few minutes after using the CreateTask in the RKM's as > a starting point), to create the CreateTask that hangs all the neccessary > memory allocations off of the Task structure, and it's so elegant. Then > the Task can just RemTask itself:) So how about posting the code? A lot of times people talk about this neat code they have, but never post it for the world to benefit. Since you intended the world should have this code (at least you gave it to Manx), how about it? -- Peter da Silva. `-_-' <peter@sugar.hackercorp.com>.