papa@pollux.usc.edu (Marco Papa) (11/15/88)
I have started playing with signals and subtasks and I run into a very strange
problem.
Consider the following program, which is a combination of Sam Dicker's
program VTbeep that used a subtask to create a VT100-like beep, and
Ali Ozer's clock program. In my combination I let the subtask update
the "main" task menu bar once a minute. The main program has provisions
for killing the clock, using SIGBREAKF_CTRL_C, and signalling it to restart.
The problem is that when I signal the subtask to restart, with SIGF_CLOCK,
the timer always returns without Wait()-ing as requested. The code that
shows this is between #if 0 and #endif. Take those out to see what I mean.
You'll have to reboot your machine in this case since the sub-task never gets
pre-empted again. If anybody can see what I don't see, I'd appreciate a
comment. By the way, DateStamp() is done always in the main task and not in
the subtask since, as Rob Peck teaches, it is an AmigaDOS routine and cannot
be executed by a task, only by a process.
The entire source and makefile follows. This is compiled for MANX 3.60.
#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# taskclock.c
# makefile
# This archive created: Mon Nov 14 22:39:06 1988
# By: Marco Papa (Felsina Software, Los Angeles, CA)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'taskclock.c'" '(9556 characters)'
if test -f 'taskclock.c'
then
echo shar: over-writing existing file "'taskclock.c'"
fi
cat << \SHAR_EOF > 'taskclock.c'
/*
* taskclock.c
*/
/*
from:
VTbeep.c -- Copyright (C) 1986 Same Dicker, Commodore/Amiga Inc.
and
LedClock.c -- Copyright (C) 1987 Ali T. Ozer
*/
#include <exec/types.h>
#include <functions.h>
#include <intuition/intuition.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <devices/timer.h>
#include <graphics/gfxmacros.h>
#include "libraries/dos.h"
#define CLOCKNAME "VTClock"
#define SIGB_CLOCK 31
#define SIGF_CLOCK (1 << 31)
#define SIGF_PORT (1 << replyPort->mp_SigBit)
#define TWENTYFOURHOUR 1
#define FANCYPANIC 1
#define COPYRIGHT "TaskClock 1.0 -- Marco Papa, Ali T. Ozer, Sam Dicker"
#if FANCYPANIC
#define Panic(arg) WarnUser(arg)
#else
#define Panic(arg) CloseThings(1)
#endif
struct Window *MyWin;
struct Library *IntuitionBase;
struct Library *GfxBase;
struct timerequest *timerIOB;
unsigned long WindowSig, TimerSig;
int curminutes;
long qtime;
long timevec[3]; /* for DateStamp() */
/* OpenTimer will open the timer device and also do all the initialization
** necessary to send timer requests...
*/
OpenTimer (tr)
struct timerequest *tr;
{
struct MsgPort *timerport;
while ((timerport = CreatePort (NULL, 0L)) == NULL) Panic ("No port");
OpenDevice (TIMERNAME, UNIT_VBLANK, tr, 0L);
tr->tr_node.io_Message.mn_ReplyPort = timerport;
tr->tr_node.io_Command = TR_ADDREQUEST;
}
static struct NewWindow MyWinInfo =
{ 0, 0, 640, 20, /* Lft,Top,Wd,Hgt */
-1,-1, /* Detail pen, Block pen (-1 = use screens) */
CLOSEWINDOW, /* IDCMPflags */
SMART_REFRESH | WINDOWDEPTH | WINDOWDRAG | WINDOWCLOSE |
NOCAREREFRESH | ACTIVATE,
NULL, NULL, NULL, /* FirstGadget, Menu Checkmark, Title */
NULL, NULL, /* Screen, Bitmap */
0, 0, 0, 0, /* Min Width/Height Max Width/Height */
WBENCHSCREEN /* Type */
};
/* OpenThings opens the Amiga libraries, the window. and the timer.
** If something goes wrong, a requester will pop up to warn the user.
** (If FANCYPANIC is defined as zero, then no requester pops up --- the
** program just quits.)
*/
OpenThings ()
{
if (((IntuitionBase = OpenLibrary("intuition.library",0L)) == NULL) ||
((GfxBase = OpenLibrary("graphics.library",0L)) == NULL)) CloseThings (1);
while (!(MyWin = OpenWindow(&MyWinInfo))) Panic ("No window");
SetWindowTitles (MyWin, "JUNK", COPYRIGHT);
WindowSig = 1L << MyWin->UserPort->mp_SigBit;
}
/* SendTimerRequest sends a timer request for the indicated amount of seconds.
** Assumes the timer request block pointed to by tr has already been properly
** setup --- including the io_Command field.
*/
SendTimerRequest (tr, seconds)
struct timerequest *tr;
unsigned long seconds;
{
tr->tr_time.tv_micro = 0L;
tr->tr_time.tv_secs = seconds;
SendIO (tr);
}
#if FANCYPANIC
/* WarnUser is called when something goes wrong during initialization.
** WarnUser assumes Intuition is opened! (Now if that's not the case,
** then you're in trouble...)
*/
WarnUser (reason)
UBYTE *reason;
{
static struct IntuiText postxt = {3,1,COMPLEMENT,4,4,NULL,(UBYTE *)"Retry",NULL};
static struct IntuiText negtxt = {3,1,COMPLEMENT,4,4,NULL,(UBYTE *)"Sigh",NULL};
static struct IntuiText bodytxt = {3,1,COMPLEMENT,4,6,NULL,NULL,NULL};
bodytxt.IText = reason;
if (AutoRequest (NULL,&bodytxt,&postxt,&negtxt,0L,0L,300L,60L) == FALSE)
#if 0
CloseThings (1);
#else
;
#endif
}
#endif
/* CloseTimer closes the timer pointed to by tr. If no reply port is present,
** then CloseTimer assumes the timer was never opened.
*/
CloseTimer (tr)
struct timerequest *tr;
{
struct MsgPort *msgp;
if (msgp = tr->tr_node.io_Message.mn_ReplyPort) {
DeletePort (msgp);
CloseDevice (tr);
}
}
/* CloseThings releases all the resources obtained by the program.
*/
CloseThings(error_code)
int error_code;
{
if (MyWin) CloseWindow(MyWin);
if (GfxBase) CloseLibrary(GfxBase);
if (IntuitionBase) CloseLibrary(IntuitionBase);
exit (error_code);
}
char mytime[80];
char *fmt = "TaskClock - %d:%02d F:%ldK C:%ldK %d:%02d";
static int hourC = 0;
static int minC = 0;
/* WriteTime writes out the new time.
*/
WriteTime (minutes)
int minutes;
{
int hours, minute;
ULONG availfast, availchip;
#if TWENTYFOURHOUR
hours = minutes / 60;
#else
hours = (minutes / 60 + 11) % 12 + 1;
#endif
minute = minutes % 60;
/* AM if minutes < 720, but we don't worry about this... */
availfast = AvailMem(MEMF_FAST)/1024;
availchip = AvailMem(MEMF_CHIP)/1024;
sprintf(mytime, fmt, hours, minute, availfast, availchip, hourC, minC);
SetWindowTitles (MyWin, mytime, COPYRIGHT);
minC++;
if (minC == 60) { minC = 0; hourC++; }
}
/* ShowTheTime reads the time, updates the display, and sends a new timer
** request.
*/
ShowTheTime ()
{
WriteTime (curminutes);
/* Timer request for the next top of minute */
SendTimerRequest (timerIOB, qtime);
curminutes++;
qtime = 60L;
}
main ()
{
OpenThings ();
DateStamp (&timevec[0]);
curminutes = (int)(timevec[1]);
qtime = 60L - (long)(timevec[2] / 50L);
if (VTClock()) { /* start the clock task */
#if 0
/* -----> IF YOU ADD THIS CODE IT LOOPS FOREVER <------- */
/* reset after 1 minute */
printf("Waiting to reset...\n");
Delay(3000L); /* 1 minute */
printf("Resetting...\n");
if (VTClock() == NULL) {
printf("Reset!\n");
}
#endif
/* Now sit down, relax, and wait for an IDCMP event... */
Wait (WindowSig);
/* assume it is closewindow */
KillVTClock();
}
CloseThings (0);
}
/* Including the following two lines prevents the linker from bringing in
** the two functions _wb_parse and _cli_parse called by the initialization
** code in _main. Reduces code size by about 900 bytes for Manx 3.40a.
*/
void _wb_parse () {}
void _cli_parse () {}
VTClock()
{
struct Task *clockTCB;
VOID clockTask();
/* prevent clock child task, if it already exists, from going away before
* it is signaled */
Forbid();
/* find the task by name */
clockTCB = (struct Task *)FindTask(CLOCKNAME);
/* check if the task exists */
if (clockTCB == NULL)
{
/* it doesn't exist, so create it */
sava4(); /* $$$$$$$$ */
clockTCB = (struct Task *)CreateTask(CLOCKNAME, (long) 25, (long) clockTask, 1000L);
}
else
{
/* it already exist so signal it so restart it's timer */
DateStamp (&timevec[0]);
curminutes = (int)(timevec[1]);
qtime = 60L - (long)(timevec[2] / 50L);
Signal(clockTCB, SIGF_CLOCK);
/* ---------> IT NEVER GETS HERE !!! <-------------- */
clockTCB = NULL; /* fake it */
}
Permit();
printf("Clocktcb = %ld\n", (long) clockTCB);
/* return success */
return(clockTCB != NULL);
}
/* kill any clcok update in progress. This is necessary before exiting the
* main program; otherwise, if an update is playing, when the clock times out
* and the child task wakes up its code segment may be gone */
KillVTClock()
{
struct Task *clockTCB;
do {
/* prevent clock child task, if it already exists, from going away
* before it is signaled */
Forbid();
/* find the task by name */
clockTCB = (struct Task *)FindTask(CLOCKNAME);
/* check if the task exists */
if (clockTCB != NULL) {
/* it already exist so signal it so go away */
Signal(clockTCB, SIGBREAKF_CTRL_C);
/* give it a chance to wake up, if it is lower priority */
Delay(10);
}
Permit();
printf("Clocktcb = %ld\n", (long) clockTCB);
/* if it existed, kill it again */
} while (clockTCB != NULL);
}
/* update clock child task */
VOID clockTask()
{
struct MsgPort *replyPort;
ULONG signals;
geta4();
/* allocate signal used to re-start clock */
if (AllocSignal(SIGB_CLOCK) == SIGB_CLOCK) {
/* create reply port for timer and sound I/O block */
replyPort = (struct MsgPort *)CreatePort(NULL);
if (replyPort != NULL) {
/* create timer I/O block */
timerIOB = (struct timerequest *)
CreateExtIO(replyPort, sizeof(struct timerequest));
if (timerIOB != NULL) {
/* open timer device */
if (OpenDevice(TIMERNAME, UNIT_VBLANK, timerIOB, 0) == 0) {
timerIOB->tr_node.io_Command = TR_ADDREQUEST;
/* from this point on the task in cannot
be pre-empted. This prevents the
parent task from signaling it to
restart the timer while it is cleaning
up */
Forbid();
do {
/* start the timer */
ShowTheTime();
/* wait for:
. the timer to time out (SIGF_PORT) ,
. a signal to restart to timer (SIGF_CLOCK) , or
. a signal to quit (SIGBREAKF_CTRL_C) */
signals = Wait(SIGBREAKF_CTRL_C |
SIGF_CLOCK | SIGF_PORT);
if ((signals & SIGF_CLOCK) != 0) {
/* reset the time if signaled */
hourC = 10;
minC = 0;
}
/* if the timer is still going, kill it */
if (CheckIO(timerIOB) == 0) {
AbortIO(timerIOB);
}
WaitIO(timerIOB); /* otherwise just reply the message */
} while ((signals & SIGBREAKF_CTRL_C) == 0);
/* clean up */
CloseDevice(timerIOB);
} /* if OpenDevice */
DeleteExtIO(timerIOB, sizeof(struct timerequest));
} /* if timerIOB */
DeletePort(replyPort);
} /* if replyPort */
FreeSignal(SIGB_CLOCK);
} /* if AllocSignal */
}
#asm
a4sav dc.l 0
public _sava4
_sava4
lea a4sav,a0
move.l a4,(a0)
rts
public _geta4
_geta4
move.l a4sav,a4
rts
#endasm
SHAR_EOF
echo shar: extracting "'makefile'" '(74 characters)'
if test -f 'makefile'
then
echo shar: over-writing existing file "'makefile'"
fi
cat << \SHAR_EOF > 'makefile'
taskclock: taskclock.c
cc -n +L taskclock.c
ln +cd -g taskclock.o -lc32
SHAR_EOF
# End of shell archive
exit 0
-- Marco Papa 'Doc'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
uucp:...!pollux!papa BIX:papa ARPAnet:pollux!papa@oberon.usc.edu
"There's Alpha, Beta, Gamma and Diga!" -- Leo Schwab [quoting Rick Unland]
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=