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] -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=