-70- Flietext mit Tempus Editor



Des Rechners Pein, des Anwenders Lust.

oder wie man den Rechner mit RPMT an die Grenzen der Belastbarkeit 
fhrt.



Multitasking ist eine nette Idee fr die Leute, die MultiGEM, Mint 
oder sonstige Erweiterung Ihr Eigen nennen. Da kann im Hintergrund 
gedruckt werden, whrend der Rechner noch mit umfangreichen 
Berechnungen beschftigt ist. Allen den Usern, die keine solche 
Erweiterung kennen, ist dieser Artikel gewidmet. Sie knnen mmlich 
bei lngeren Ausdrucken gemtlich die eine oder andere Tasse Kaffee 
genieen, ehe der Rechner mit der Ausgabe beendet ist.



Damit dies in Zukunft nicht mehr sein mu, stelle ich an dieser 
Stelle das RPMT (= (R)alph's (P)seudo (M)ulti(T)asking ) vor. 



Was tut RPMT ?

Es bedient Sich der Funktionen des AES, die ein quasi-Multitasking 
ermglichen. Jeder, der schon einmal ein Accessory gesehen oder 
selbst programmiert hat wei, da ein Accessory permanent im Speicher 
verweilt und nur darauf wartet, da es aufgerufen wird. Dazu springt 
das Accessory nach dem Start eine evnt_mesag Schleife an, in der es 
auf die Mitteilung AC_OPEN wartet. Dies ist fr das Accessory der 
Startschu.



Diese Mglichkeit ist jedoch fr RPMT ungeeignet, da die einzelnen 
Tasks ( = Aufgaben ) ja aus einem Programm heraus gestartet werden 
sollen. Zudem wren dann ja auch nur maximal 6 Tasks mglich, da es 
unter GEM derzeit nur 6 Accessories geben darf. Die zentrale Routine 
in einem Programm, welches RPMT verwendet, ist daher die event_multi 
Schleife. Dort wird auf eine Timerereignis gewartet und, wenn dieses 
aufgetreten ist, wird der Task-Manager angesprungen, der den 
eigentlichen Kern des RPMT bildet.



Normalerweise ist in jeder GEM-Applikation eine event_multi Schleife 
vorhanden, in der das Programm auf alle wichtigen Ereignisse wartet.

Solche Ereignisse wren z.B. :



MU_SELECTED  : Es wurde ein Menu ausgewhlt.

MU_KEYBD     : Eine Taste wurde gedrckt

MU_TIMER     : Eine gewisse Zeitspanne ist abgelaufen.



Eine Solche Funktion knnte dahe so aussehen. :



int waiter( void )

   {

   FOREVER

      {

      evnt = evnt_multi( MU_MESAG|MU_BUTTON|MU_KEYBD|MU_TIMER,...,,);



      /* Reagiere auf weitere Ereignisse.... */



      if ( evnt & MU_TIMER )

         manage_tasks( );



      }

   }



An dieser Stelle hakt sich RPMT in das Geschehen ein. Bei einem 
Zeitereignis wird die Funktion manage_tasks angesprungen, die die 
Hauptarbeit des Multitasking erledigt. Zu Beginn des Programms mssen 
Sie nur die Funktion init_tasks() aufrufen, die die einzelnen Tasks 
initialisiert und so mit definierten Startwerten versorgt. Weitere 
nderungen sind im Hauptteil des Programms fast nicht ntig.



Wie der Taskmanager im Einzelenen jetzt genau funktioniert, da zeigt 
hoffentlich die folgende Beschreibung der Funktionen im Zusammenhang 
mit den abgedruckten Listings.

Damit RPMT durch die Fortschrittsanzeigen nicht den Bildschirminhalt 
zerstrt, sollten solche Ausgaben generell unterbleiben, oder in 
einem Fenster dargestellt werden. Ich habe allerdings aus 
Platzgrnden auf die Einbindung einer Windowlibary verzichtet. In den 
Listings sind jedoch an den markanten Stellen Kommentare eingefgt, 
so da ersichtlich sein wird, wo und wann eine Window-Ausgabe zu 
erfolgen hat. Im Demo-Programm druckt jede Task die Ausgabe an einer 
bestimmten Bildschirmposition.



Was oder was ist eine Task ?

Eine Task oder ein Job ist eine Aufgabe, die der Rechner zu 
bewltigen hat, whrend er noch etwas Anderes zu tun hat. Ein 
klassisches Beispiel ist die Darstellung des Mauszeigers in 
GEM-Applikationen. Auch wenn gerade ein Diskettenzugriff erfolgt, 
wird der Mauszeiger aktualisiert.



Jeder Job, der unter RPMT gestartet wurde, bekommt eine Struktur 
zugewiesen, in der alle wichtigen Informationen ber den Job 
enthalten sind. Auf diese Weise ist es dem Taskmanager mglich, die 
einzelnen Jobs effizient zu verwalten.



Zudem verwendet er noch eine globale Variable 'taskstop' mit deren 
Hife das RPMT gesteuert wird. Ist dieser Wert = TRUE, so wird kein 
Job ausgefhrt. Die Jobs werden quasi eingefroren. Erst wenn der Wert 
wieder FALSE ist, werden die Jobs wieder bearbeitet.



Die einzelnen Komponenten dieser Strukur sind im Listing 5 genau 
aufgefhrt. Die Bedeutung der einzelnen Felder wird im Folgenden 
erlutert.:



int t_id; 

Eine Integerzahl, die die Kennung eines Prozesses enthlt. Anhand 
dieser Identifikationsnummer kann der Taskmanager erkennen, ob eine 
Task schon belegt ist oder nicht. Die Kennung kann vom Programmierer 
frei gewhlt werden. Es ist dabei nur etwas zu beachten. 

Hat ein Job als ID den Wert '99' , so wird dieser Job ohne Rckfrage 
gestartet, da Ihn der Taskmanager als Systemjob einordnet. Ebenso 
kann ein 99'er-Job nicht vor dessen Ende gelscht werden. Alle 
anderen Jobs knnen whrend des Laufs abgebrochen werden.

Bei allen Werten zwischen 1 und 98 wird in der Funktion 'task_delete' 
versucht, das Fenster zu schlieen, auf das der Eintrag 'fen' zeigt.

Diesen Eintrag versucht die Routine 'task_start' mit einem Zeiger auf 
ein neu kreiertes Fenster zu belegen. Sind keine Fenster mehr frei, 
so wird hier eine NULL ( -> (void*)0L ) eingetragen.



Mgliche Werte :

0      : Diese Task ist frei und kann somit neu belegt werden.

1 - 98 : Diese Task hat ein Fenster zur Fortschrittsanzeige.

99     : Diese Task ist intern und kann daher nicht gelscht werden.

100 - n: Diese Task hat keine Fortschrittsanzeige oder Sie hat eine 
         spezielle Userdefinierte Updateroutine.



int t_min1,t_max1,t_akt1;



Diese Werte halten fest, wieviele Zyklen eine Task absolvieren mu.

z.B. beim Formatiern einer Diskette mit 80 Tracks mu die Schleife 
insgesamt 80 mal durchlaufen werden.



aus :  for ( i = 0 ; i < 80 ; i++ ) wird daher :



t_min1 =  0;

t_max1 = 79;

t_akt1 =  0;



wobei t_akt1 die Rolle des Parameters 'i' bernimmt.



int t_min2,t_max2,t_akt2;



Diese Werte enthalten die Grenzen fr eine weitere Schleife.

z.B.: Formatieren der Disk Seite 1 und 2:

t_min2 = 0;

t_max2 = 1;

t_akt2 = 0;



In t_akt1 oder t_akt2 hlt der Taskmanager fest, wie oft die Schleife 
schon durchlaufen worden ist.



int t_drive;

Hier ist Bitweise verschlsselt, auf welches Laufwerk die Task 
zugreifen mu. Da manche Aufgaben exklusiv auf ein Laufwerk 
beschrnkt sind mu, hier das entsprechende Bit fr das Laufwerk 
gesetzt werden, um es so fr weitere Jobs sperren zu knnen.



Dabei heit  Bit 0 gesetzt -> Laufwerk A

             Bit 1         -> Laufwerk B usw.



Der Sinn liegt darin, da sich verschiedene Jobs spinnefeind sein 
knnen. Dies wre dann der Fall, wenn z.B. gerade Laufwerk A 
formatiert wird, und eine weitere Task ebenfalls auf Laufwerk A 
zugreifen mchte, um die Quelldaten des Jahrhundertprogramms zu 
sichern. Das Chaos wre dann faszinierend. Ebenso knnten sich so 
wichtige Daten sang und klanglos in das Datennirwana verabschieden.



Die Belegung des Bitvektors ist wie folgt. :



Bit 0 = 1 --> Laufwerk A wird bentigt

Bit 1 = 1 -->    "     B  "      "

u.s.w.





int t_device

Dies ist ein Bitvektor fr weitere Gerte des Rechners. Einige sind 
in RPMT_DEF.H vordefiniert. Diese Gerte wren z.B. Drucker, Modem, 
Bildschirm, ein Pufferbereich innerhalb des Programms, eine bestimmte 
Datei oder eine andere Task, die erst fertig sein mu. Es knnen halt 
nicht zwei verschiedene Listen gleichzeitig gedruckt werden.





int t_status;

Dieser Wert bestimmt, was der Task-Manager mit diesem Job zu tun hat. 
Es sind im Programm drei Zustnde denkbar.



1 : T_RUN  , die Task wird momentan ausgefhrt.

2 : T_HOLD , die Task wartet noch sehnschtig auf Peripherie.

3 : T_WAIT , die Task ist vom User in den Wartezustand verbannt 
             worden und bleibt solange im HOLD-Modus, bis der User 
             diese Task wieder aktiviert.



FENSTER *fen;

Hier wird ein Zeiger auf eine Fensterstruktur oder ein Fenster 
eingetragen, in das die Task seine Ausgaben ttigen soll.

Im Demoprogramm ist dieser Eintrag nicht benutzt, da keine 
Windowlibary eingebunden ist.



void (t_funktion)(int index)

Die ist ein Zeiger auf eine Funktion, die vom Taskmanager aufgerufen 
wird. In dieser Funktion wird die eigentliche Aufgabe der Task 
programmiert. Im Wert 'index' wird der Funktion der Index des Array 
bergeben, in dem die Informationen zu diesem Job zu finden sind.

-> Im Listing 1 finden Sie dazu einige Beispiele.



Last not least.

char t_name[31].

Hier hat man 30 Stellen Platz, um der Task einen Namen zu geben.

Damit kann man dem User mitteilen, wer gerade was wo erledigt.





Soweit zu den Definitionen der Jobs, nun aber geht es an das 
Eingemachte, die Routinen des RPMT.



Die Funktion task_init()

In dieser Funktion werden alle 'T_ANZAHL' Elemente des Task-Arrays 
initialisiert und mit definierten Werten versehen.

Diese Funktion wird nur einmal zu Beginn des Programms aufgerufen.



Tip. : Als Modifikation kann man der Funktion die Anzahl der maximal 
mglichen Jobs bergeben. Dann braucht man nur noch in der Funktion 
task_init einen entsprechenden Speicherbereich zu reservieren.



Der Bequemlichkeit halber habe ich nmlich die Anzahl der Jobs global 
festgelegt, sorry.



Die Hauptfunktion 'task_manager'

Prozessor versteck' dich, ich neck' dich.



Kurz nach dem Aufruf stellt die Funktion fest, ob generell alle Tasks 
gesperrt sind. Dies ist dann der Fall, wenn die globale Variable 
'taskstop' den Wert TRUE hat. Die Verarbeitung wird dann sofort - 
sehr zur Freude des Prozessors- abgebrochen.



Ist das nicht der Fall, so werden alle Tasks daraufhin untersucht, ob 
die Identifikationsnummer ungleich 0 ist, dann ist nmlich dieser Job 
vorhanden und soll bearbeitet werden. Im nchsten Schritt wird 
untersucht, ob der Status der Task gleich T_RUN ist. Wenn ja, so wird 
die entsprechende Funktion aufgerufen. 

Ist der Status jedoch T_HOLD, so wird in der Routine used_device 
berprft, ob die geforderte Peripherie endlich frei ist. Erhlt der 
Manager darauf die sehnlichst erwartete Zustimmung, so wird die Task 
gestartet und Ihr Status wird auf T_RUN gesetzt. 

Die Routine task_start fragt noch einmal nach, ob diese Task denn 
berhaupt gestartet werden soll. Falls nicht, so wird die Task sofort 
in der Funktion task_delete wieder entfernt. Nachdem so alle Task 
sukzessive abgearbeitet worden sind, kehrt die Funktion wieder in die 
event_multi Schleife zurck. 



Es ist einleuchtend, das so relativ viel Rechenzeit verbraucht wird, 
aber leider geht es mit diesem Betriebssystem nicht anders, wenn man 
keine gemeinen Kniffe anwenden mchte. Es kann schon mal vorkommen, 
da bei einigen gleichzeitigen Jobs die Mausbewegungen etwas 
ruckartig werden. Dies kommt daher, da der task_manager alle 100 
Millisekunden ( -> time der Funktion multievent ) aufgerufen wird. 
Man kann allerdings die Jobbearbeitung etwas drosseln, indem man den 
Wert auf 200 oder 300 einstellt. Dann laufen zwar die einzelnen 
Jobabschnitte etwas verzgert ab, aber dafr bleibt der Rechner bei 
den anderen Aufgaben etwas schneller.



Hier ist dann auch schon der groe Unterschied zu einem 'richtigen' 
Multitasking erkennbar. Im 'richtigen' Multitasking bekommt jeder 
Prozess eine bestimmte Zeitspanne zugeteilt. Nach Ablauf dieser Zeit 
wird die Task erbarmungslos unterbrochen und einer anderen Task wird 
die Zeit zugeteilt. Da GEM und/oder AES nicht zu einem solchen 
Time-Sharing fhig sind, wird hier einem Prozess solange Zeit 
gegeben, bis dieser mit seiner Berechnung fertig ist. Daher handelt 
es sich auch nur um ein 'pseudo' Multitasking.



Die Funktion used_device



Sie gibt entweder ein TRUE ( = Peripherie ist frei ) oder ein FALSE ( 
= Peripherie ist belegt ) zurck. 



Es werden alle Jobs durchsucht und auf die verwendete Peripherie 
getestet. Dazu wird nach solchen Jobs Ausschau gehalten, die den 
Status T_RUN haben. Wird einer gefunden, so wird durch einfache 
Bitverknpfung festgestellt, ob die verlangte Peripherie frei ist.



Die Funktion task_delete



Diese Funktion bekommt den Index der Task bergeben, die gelscht 
werden soll. Falls die ID dann kleiner als 99 ist und ein Zeiger auf 
ein Fenster enthalten ist, so wird das Fenster geschlossen.

Anschlieend werden wieder alle Werte auf die Standarteinstellung 
gebracht. Ist die Task-ID aber 99 , so wird der Wunsch nach einer 
Lschung des Jobs geflissentlich berhrt, da es sich bei dieser Task 
um eine Systemroutine handelt. Im Demoprogramm ist das die Routine, 
die laufend alle aktiven Jobs anzeigt.



Die Funktion task_free sucht nach einer freien Task in dem Taskarray 
und gibt entweder den Index des freien Eintrags zurck, oder eine -1 
fr 'Kein Eintrag mehr frei'.



Die Funktion task_start.



In dieser Funktion wird zu jeder Task eine Meldung generiert, in der 
der User gefragt wird, ob diese Task tatschlich gestartet werden 
soll. Bei den internen Tasks, die die ID 99 haben, entfllt diese 
Meldung, sie werden sofort gestartet.



Die Funktion taskexist prft nach, ob eine Task doppelt vorhanden 
ist. Dazu ein Beispiel.



Sie haben in einem Speicherbereich Daten Bearbeitet, die Mittels 4 
Jobs (Jo1,Jo2...) bearbeitet werden sollen. Nach Beendigung aller 
Arbeiten soll der Speicherbereich wieder freigegeben werden.

Sie starten dann Jo1,Jo2, Jo3 und Jo4 und schicken sofort die Task 
'Speicher freigeben' los, die kontrolliert, ob noch die Jobs 
Jo1,Jo2.. aktiv sind. Erst wenn Sie mit taskexist feststellen, das 
alle vier Jobs fertig sind, kann der Speicher freigegeben werden.



Soweit zu dem Hauptteil des Programms. Die restlichen Listings 
enthalten eine kleine GEM-Umgebung fr den Test. Sie mssen nur noch 
eine kleine RSC-Datei anlegen, so wie Sie in der Abbildung 1 
dargestellt ist.



Ich habe die Quelltexte ausreichend kommentiert, so da eigentlich 
keine Fragen mehr offen sein sollten. 



Dies war die Erklrung zu RPMT. Getestet habe ich das abgedruckte 
Programm unter folgenden Bedingungen : 



Atari 520 ST+

TOS 1.4 und TOS 2.06 und unter MultiGEM.

(Das Demoprogramm ist fr den SM124 640*400 Pixel erstellt worden, es 
luft natrlich auf allen Auflsungen, sieht da aber etwas merkwrdig 
aus, da die Ausgaben mittels printf auf die Koordinaten meines 
Bildschirms berechnet sind.)



Fehler beim Test ?  Nein !?



Erfreulicherweise traten bei keinem System Probleme auf, wenn man die 
improvisierte Bildschirmausgabe mittels printf einmal auer Acht 
lsst. Lenkt man die Ausgaben korrekterweise in ein Fenster, so 
traten wirklich kaum Probleme auf.





Doch, wohl Fehler !!



Unter MultiGEM gab es herbe Abstrze, fr die allerdings keines der 
Programme die Schuld zu tragen scheint. Woher soll MultiGEM auch 
wissen, das PureC gerade auf Laufwerk A sichert, whrend mein 
Testprg1 das gleiche Laufwerk formatieren will, und es mein Testprg2 
ebenfalls formatiern mchte. Und woher sollen PureC, Testprg1 und 
Testprg2 dies voneinander wissen.



Ein Lsungsansatz zur Bewltigung dieses Problems wre die 
Einrichtung eines Cookies, ber den man den Zugriff auf einen 
Laufwerksvektor und einen Devicevektor erhlt. Die Belegung der 
Peripherie wre dann von jedem Programm zu erfragen und entspechend 
seiner Belange auszuwerten und zu bearbeiten. Erst dann wre meiner 
Meinung nach ein gefahrloses Multitasking mglich.



Einen solchen Cookie wollte ich noch nicht installieren, da ja nur 
meine Programme etwas sinnvolles damit anfangen knnten. Allen 
anderen Programmen wrde dieser Cookie nichts sagen. Aber wer wei, 
vielleicht wird so etwas ja bei dem neuen MultiTOS notwendig.



Zum Schlu sei mir noch ein Hinweis in eigener Sache gestattet.



Da ich ein entsetzlich neugieriger Mensch bin, wrde es mich 
interessieren, ob jemand diese Routinen in Seinen eigenen Programmen 
verwenden mchte. Dies kann er gerne tun, ich wrde mich jedoch ber 
eine kleine Nachricht oder ein Demoprogramm sehr freuen. Diese Bitte 
ist wirklich ganz uneigenntzig und frei von jedweder Raffgier. Aber 
nur aus den Erfahrungen und Hinweisen Anderer heraus kann ich diese 
Routinen weiter optimieren. 



Also, bitte schreibt an :



Ralph Lanfermann

Nordstr. 48

5102 Wrselen



- Stichwort RPMT -





****** ENDE ************



(C) by R.Lanfermann  27.04.1993





