amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > Task schläft ein [ - Suche - Neue Beiträge - Registrieren - Login - ]

-1- [ - Beitrag schreiben - ]

03.05.2008, 19:44 Uhr

Mad_Dog
Posts: 1944
Nutzer
Hallo,

Ich habe versucht bei meinem Programm "Monoscope" das Neuzeichnen der Grafik in einen extra Task zu packen (Funktion Update).

Der Fensterinhalt wird zwar eine Weile upgedatet, aber dann scheint der Task einzuschlafen.

Was mache ich falsch?

Hier das Hauptprogramm:
c code:
#include <stdio.h>
#include <stdlib.h>

#include <exec/types.h>
#include <exec/exec.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <graphics/gfx.h>
#include <libraries/gadtools.h>
#include <devices/timer.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>

#include <clib/alib_protos.h>

#include "requesters.h"
#include "requesters.c"
#include "menu.h"
#include "menu.c"
#include "parallel.h"             // Makros für CIA Low-Level Zugriff
#include "scope.h"
#include "scope.c"

#define WIDTH 400   // Breite des Fensters
#define HEIGHT 256  // Höhe des Fensters

struct Window *Fenster = NULL;               // Zeiger auf Window-Struktur
struct Screen *mysc = NULL;
struct IntuitionBase *IntuitionBase = NULL;  // Zeiger auf IntuitionBase-Struktur
struct GfxBase *GfxBase = NULL;              // Zeiger auf GfxBase-Struktur
struct Library *GadToolsBase = NULL;
void *vi = NULL;                             // Visual Info

struct Scope *sc;

struct Task *child = NULL;
char *childname = "Uptate Scope";

UBYTE TimerDevice;

UBYTE vers[] = "\0$VER: Monoscope 1.5";

// Resourcen freigeben
void ShutDown(void)
{
    if (mysc) UnlockPubScreen(NULL, mysc);
    if (GadToolsBase) CloseLibrary(GadToolsBase);
    if (Fenster) CloseWindow(Fenster);
    if (GfxBase) CloseLibrary((struct Library *) GfxBase);
    if (IntuitionBase) CloseLibrary((struct Library *) IntuitionBase);
}

extern void Update(void)
{
   while (1)
   {
     ReadFrame(sc);
     DrawScope(sc);
   }

}

int main(void)
{
   /* Variablen zur Message-Bearbeitung */
   struct MsgPort *Port;             // Zeiger auf Message Port Struktur
   struct IntuiMessage *Nachricht;   // Zeiger auf Intuition Message Struktur

   LONG black,green;  // Variablen für die Pen Nummern

   ULONG  klasse;
   USHORT code;

   BOOL Ende;

   Ende = FALSE;
   Freeze = FALSE;  // Anzeige einfrieren (TRUE/FALSE)

   // Intuition Library öffnen
   IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",36L);
   if (IntuitionBase == NULL) ShutDown();

   GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0L);
   if (GfxBase == NULL) ShutDown();

   // GadTools Library öffnen
   GadToolsBase = OpenLibrary("gadtools.library", 37L);
   if (GadToolsBase == NULL) ShutDown();

   mysc = LockPubScreen(NULL);

   // Fenster mittels Tags öffnen
   Fenster = OpenWindowTags(NULL,
                            WA_Left, 100,    // Abstand vom linken Rand
                            WA_Top, 100,     // Abstand vom oberen Rand
                            WA_InnerWidth, WIDTH,    // Innere Breite
                            WA_InnerHeight, HEIGHT,  // Innere Höhe
                            WA_Title, "Monoscope",          // Fenstertitel
                            WA_ScreenTitle,"Monoscope V1.5 © 2008 by Norman Walter",
                            WA_CloseGadget, TRUE,           // Close-Gadget
                            WA_DragBar, TRUE,               // Ziehleiste
                            WA_DepthGadget, TRUE,           // Depth-Gadget
                            WA_GimmeZeroZero, TRUE,         // Ursprung 0/0
                            WA_NewLookMenus, TRUE,
                            WA_IDCMP,
                            IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_INTUITICKS | IDCMP_REFRESHWINDOW,
                            WA_Activate, TRUE,              // Fenster aktivieren
                            TAG_DONE);

   if (Fenster==NULL) ShutDown();

   vi = GetVisualInfo(mysc, TAG_DONE);
   menu = CreateMenus(mynewmenu, TAG_DONE);
   LayoutMenus(menu, vi, GTMN_NewLookMenus, TRUE, TAG_DONE);
   SetMenuStrip(Fenster, menu);

   Port=Fenster->UserPort;

   /* Pens ermitteln: Schwarz und Grün */
   black = ObtainBestPen(Fenster->WScreen->ViewPort.ColorMap,
                         0x00000000,0x00000000,0x00000000,
                         OBP_Precision,PRECISION_GUI,
                         TAG_DONE);

   green = ObtainBestPen(Fenster->WScreen->ViewPort.ColorMap,
                         0x00000000,0xFFFFFFFF,0x00000000,
                         OBP_Precision,PRECISION_GUI,
                         TAG_DONE);

   /*  Oszilloskop erzeugen:
    *  32Khz Samplingrate,
    *  Zeitfenster 0.0125 Sekunden
    *  Zeichenfarbe grün, Hintergrund schwarz
    */
   sc = CreateScope(Fenster,32000,0.0125,green,black);

   /*  Wichtig: Für diesen Zweck brauchen wir UNIT_MICROHZ.
    *  Die anderen Timer sind zu ungenau.
    */
   TimerDevice = OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *)sc->TimerIO,0L);
   if (TimerDevice !=0) ShutDown();

   // Den Parallelport in den Lesemodus versetzen
   SET_PARALLEL_PORT_READ;

   child = CreateTask(childname,0L,Update,4000L);

   while (!Ende)
   {
      // Lese Zeitfenster
      //if (!Freeze) ReadFrame(sc);

      WaitPort(Port);

     /* Schleife läuft bis alle Ereignisse
      * abgearbeitet sind.
      */
      //while(Nachricht=(struct IntuiMessage *)GetMsg(Port))
      while((!Ende) && (Nachricht=GT_GetIMsg(Port)))
      {

        klasse = Nachricht->Class;
        code =  Nachricht->Code;
        GT_ReplyIMsg(Nachricht);

        /* Welches Ereignis ist eingetreten? */
        switch(klasse)
        {
          /* Close Gadget wurde angeklickt */
           case CLOSEWINDOW :
                Ende = TRUE; // Programmende
           break;

           case MENUPICK:
		          Ende = HandleMenuEvent(code);
		     break;

          /* Intuition hat sich gemeldet */
          case INTUITICKS :
               // Zeichne das Oszilloskop
               if (!Freeze)
               {
 						//Update();
               }
          break;

          case IDCMP_REFRESHWINDOW:
               GT_BeginRefresh(Fenster);
               GT_EndRefresh(Fenster,TRUE);
          break;

        } // Ende der switch-Verzweigung

       /* Wir sind mit der Bearbeitung fertig
        * und beantworten die Nachricht.
        */
       //ReplyMsg((struct Message *)Nachricht);

     }
   }

   Forbid();
   DeleteTask(child);
   Permit();

   // Die Pens wieder freigeben
   ReleasePen(Fenster->WScreen->ViewPort.ColorMap,black);
   ReleasePen(Fenster->WScreen->ViewPort.ColorMap,green);

   FreeScope(sc);

   ShutDown();

   return 0;
}

--
http://www.norman-interactive.com

[ Dieser Beitrag wurde von Mad_Dog am 03.05.2008 um 19:44 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

03.05.2008, 23:46 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Was genau meinst du mit "Task einschlafen"? Er hängt irgendwo in ReadFrame() oder DrawScope() oder beendet er sich einfach?
Ich sehe auch keine direkte Syncronisation, da du aber ein Member in struct Scope *sc; mit dem IO-Port des timer.device befüllst gehe ich davon aus, dass der Task das als interne Uhr verwendet und keine sich im Leerlauf befindende Endlosschleife ist, richtig?

code:
extern void Update(void)
{
   while (1)
   {
     ReadFrame(sc);
     DrawScope(sc);
   }

}


Hier wundert mich allerdings das extern. Was bezweckt das hier? Die Funktion ist ja nicht extern und wenn sie es wäre kann man extern bei Funktionen sowieso weglassen.

Jedenfalls meine ich, dass dein Problem in ReadFrame/DrawScope zu suchen ist. Da du sc mit einer Funktion erzeugst die als Parameter einen Pointer deiner windows-struktur bekommt liegt die Vermutung nahe, dass du über sc in ReadFrame/DrawScope auf deren Daten zugreifst. Da ich keine Synchronisation in deiner Hauptschleife sehe hättest du dann eine Nebenläufigkeit, die eventuell deine Funktionen einfrieren lassen.


[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 00:14 Uhr

Mad_Dog
Posts: 1944
Nutzer
@woop:

Das ganze Programm ist ein einfaches Oszilloskop für Parallelportsampler, welches das Signal in einem Fenster auf der WB darstellt.

Die Funktion ReadFrame() liest dabei Daten vom Parallelport über ein vorgegebenes Zeitfenster ein - nimmt also ein Audio-Sample auf.

Die Funktion DrawScope() zeichnet dann die Grafik entsprechend den Werten des Audio-Samples in den RastPort des Fensters, welches im Hauptprogramm geöffnet wurde.

Bei dieser Endlosschleife habe ich mir folgendes gedacht:

Ursprünglich wurden diese zwei Funktionen nacheinender im Program an der stelle aufgerufen, wo im Moment noch case INTUITICKS steht. Das führt aber logischerweise dazu, daß der Fensterinhalt nur dann neu gezeichnet wird, wenn man das Fenster auch aktiviert hat. An dieser Stelle funktioniert das auch problemlos.

Das ändert sich aber, sobald ich diese beiden Funtionen inneralb dieser Endlosschleife in einem eienen Task aufrufe. Dann aktualiesiert sich der Fensterinhalt eine Weile und anschließend nicht mehr.

Eine Synchronisation habe ich nicht eingabaut, weil der Kind-Task (also diese Endlosschleife) bis zum Programmende laufen soll und dabei einfach das Audiosignal im Fenster darstellen soll.

Hier mal ein Link zur letzten Version, die noch ohne seperaten Task funktioniert: http://w3.norman-interactive.com/files/Monoscope_1_41.lha

In dieser Version wird das Neuzeichnen des Fensterinhalts von INTUITICKS angestoßen.

Zu meiner Entschuldigung muß ich ganz ehrlich eingestehen, daß ich mich noch nicht tiefgreifend mit dem Tasking des AmigaOS auseinandergesetzt habe. Mit Multitasking vom Automotive-Betriebssystem OSEC ja - aber AmigaOS Exec nein.
Also bitte nicht schlagen, falls ich da groben Unfug gemacht habe. :glow:

--
http://www.norman-interactive.com

[ Dieser Beitrag wurde von Mad_Dog am 04.05.2008 um 00:17 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 00:21 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

noch ein paar Fallstricke, die mir da einfallen:

1. Du verwendest floats und damit vermutlich auch die math-libraries. Diese verlangen es allerdings, dass sie für jeden Task geöffnet werden und dort auch jeweils die Task-eigenen Library-Pointer bei Funktionsaufrufen benutzt werden. Keine Ahnung wie übel die das nehmen, wenn man sich nicht daran hält :)

2. Leider ist es eine Designschwäche des AmigaDOS, dass ein Task quasi die Übermenge eines Prozesses ist und nicht ein Prozess aus einer Menge von Tasks besteht. Das bedeutet u.a. in der Praxis, dass du keine AmigaDOS-Funktionen in einem reinen Task verwenden darfst, auch nicht indirekt, z.B. indem du Fonts benutzt. Leider habe ich auch hier keine Ahnung, wie sich das Nichtbeachten hier äußern kann...


[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 00:31 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Speziell diese float-Problematik lässt sich mit einem C-Compiler wohl nur sehr umständlich und mit ein paar eigenen Assembler-Routinen umgehen. Von daher wäre es vielleicht besser, wenn du ReadFrame/DrawScope in deiner äußeren Schleife des Hauptprogramm aufrufen würdest und stattdessen auf das WaitPort verzichtest.
Damit verbrätst du natürlich 100% CPU aber das macht dein Kind-Task so ja auch schon :)

Ansonsten ließe der sich vielleicht noch systemkonform bremen wenn du auf Windows-Messages _und_ Timer-Events warten könntest.

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 00:48 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Hat zwar nichts mit deinem schlafenden Task zu tun, aber du solltest in deiner inneren Schleife nicht auf !Ende testen, damit beim Beenden auch sicher alle Nachrichten aus dem MSGPort gelesen werden.

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 00:52 Uhr

Mad_Dog
Posts: 1944
Nutzer
Zitat:
Original von woop:
@Mad_Dog:

Damit verbrätst du natürlich 100% CPU aber das macht dein Kind-Task so ja auch schon :)


Nun ja "fast" 100% ;)

ReadFrame() funktioniert so:

Ich lese einen Wert vom Parallelport, warte bis zur nächsten Periode lese wieder einen Wert usw. bis ich alle Werte habe.

Zum besseren Verständnis hier noch mein Include "scope.c":
c code:
/*  scope.c
 *
 */

#include "scope.h"

/*  Scope
 *
 *  Oszilloskop erzeugen:
 *
 *  Eingabeparameter: ULONG f  - Die Samplingfrequenz
 *                    double t - Dauer des Samples in Sekunden
 *                    LONG p   - Pen Nummer zum Zeichnen
 *                    LONG b   - Pen Nummer für Hintergrund
 *  Rückgabe:         struct Scope* - Zeiger auf Scope Struktur
 *
 */
struct Scope *CreateScope(struct Window *win, ULONG f,double t,LONG p, LONG b)
{
   struct Scope *sc;

   sc = (struct Scope *) AllocVec(sizeof(struct Scope),MEMF_CLEAR);

   sc->Window = win;

   sc->raster = AllocRasterBitmap((unsigned int)win->GZZWidth,(unsigned int)win->GZZHeight);
   SetColor(sc->raster,p);

   // Temporärern RastPort initialisieren
   sc->tmprp = *win->RPort;
   sc->tmprp.Layer = NULL;
   sc->tmprp.BitMap = AllocBitMap (win->GZZWidth,1,8,0,NULL);

   sc->SamplingFreq = f;
   sc->Time = t;
   sc->Period = (int)1E6/f;
   sc->Size = (ULONG)(f*t);
   sc->Data = (UBYTE *)AllocVec(sc->Size,MEMF_CLEAR);
   sc->Pen = p;
   sc->Background = b;
   sc->TimerMP = CreateMsgPort();
   sc->TimerIO = (struct timerequest *) CreateIORequest(sc->TimerMP,sizeof(struct timerequest));

   return sc; 
}

/*  FreeScope
 *
 *  Oszilloskop Struktur freigeben
 *
 *  Eingabeparameter struct Scope *sc - Zeiger auf Scope Struktur
 *  Rückgabe:        void
 *
 */ 
void FreeScope(struct Scope *sc)
{
   AbortIO((struct IORequest *)sc->TimerIO);
   WaitIO((struct IORequest *)sc->TimerIO);
   if (sc->TimerMP) DeleteMsgPort(sc->TimerMP);
   if (sc->TimerIO) DeleteIORequest((struct IORequest *)sc->TimerIO);
   FreeVec(sc->Data);
   FreeVec(sc);
   FreeRasterBitmap(sc->raster);
}


// Ein Zeitfenster lesen.
void ReadFrame(struct Scope *sc)
{
   unsigned int t;

   /*  Hier werden die Werte gesammelt
    */
   for (t=0;t<sc->Size;t++)
   {
      sc->TimerIO->tr_node.io_Command = TR_ADDREQUEST;
      sc->TimerIO->tr_time.tv_secs = 0;
      sc->TimerIO->tr_time.tv_micro = sc->Period;

      READ_PAR(sc->Data[t]);  // Wert vom Parallelport kopieren

      SendIO((struct IORequest *)sc->TimerIO);
      WaitIO((struct IORequest *)sc->TimerIO);

   }
}

/*  DrawScope
 *
 *  Zeichnet das Oszilloskop in ein Fenster
 *
 *  Eingabe: struct Scope  *sc  - Zeiger auf Oszilloskop Struktur
 */
void DrawScope(struct Scope *sc)
{
   register int t;
   register struct RastPort *rp;     // RastPort des Fensters
   register int zero;

   rp = sc->Window->RPort;

   //zero = sc->Window->GZZHeight/2;
   zero = 128;

   SetColor(sc->raster,sc->Background);
   ClearRaster(sc->raster);

   SetColor(sc->raster,sc->Pen);

   // Linien in Pixel Array eintragen
   for (t=0;t<sc->Size;t++)
   {
     rasterLine(sc->raster,t,zero,t,sc->Data[t]);
   }

   // Inhalt der Pixel Array auf RastPort des Fensters übertragen
   WritePixelArray8 (sc->Window->RPort,
	             0,0,
		     sc->Window->GZZWidth-1,sc->Window->GZZHeight-1,
		     (UBYTE *)sc->raster->data,
		     &sc->tmprp);

}




--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 01:27 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Ich weiß nicht genau was READ_PAR() macht, aber ich hätte es eigentlich nach SendIO/WaitIO erwartet (welches du durch ein DoIO() ersetzen kannst)

Ansonsten kann ich morgen nochmal drüber schauen, gute Nacht :D

Edit:
*gna* Denkfehler von mir, so passt das natürlich mit dem READ_PAR, ich hatte das gestern nacht wohl irgendwie mit Daten-vom-parallel.device-anfordern und der Benutzung des timer.device durcheinander geworfen %-)


[ Dieser Beitrag wurde von woop am 04.05.2008 um 09:44 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 02:32 Uhr

whose
Posts: 2156
Nutzer
@Mad_Dog:

Kann sein, daß ich das jetzt (in Anbetracht der fortgeschrittenen Stunde) verkehrt sehe, aber wenn ich es richtig sehe, hast Du ein kleines Problem mit dem tmpRastPort für WritePixelArray8().

Edit: Hmpf, ja, das mit dem RastPort sehe ich in der Tat verkehrt. Man soll nicht mehr so komplexe Probleme wälzen, wenns so spät in der Nacht ist und schon gar nicht, wenn man sich nicht daran gewöhnen kann, daß der RastPort nicht als Zeiger in der Window-Struktur auftaucht, sondern als "himself". :D

Nichts für ungut, ich geh jetzt schlafen, vielleicht komme ich später drauf, wo es hakt. Ich glaube aber nicht, daß es das Timing an sich ist, was da Probleme bereitet, das müßte "sicher" für einen Task sein.

Grüße

--
---

:boing: µA1 PPC 750GX-800
:boing: A4000 PPC 604e-233


[ Dieser Beitrag wurde von whose am 04.05.2008 um 02:38 Uhr geändert. ]


[ Dieser Beitrag wurde von whose am 04.05.2008 um 04:15 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 10:08 Uhr

woop
Posts: 67
Nutzer
Zitat:
Original von whose:

Edit: [...] Man soll nicht mehr so komplexe Probleme wälzen, wenns so spät in der Nacht ist [...]


Da hast du absolut recht, außerdem glaube ich zu wissen, was Mad_Dogs Problem hier ist.

Der Message-Port fürs timer.device wird im Hauptprozess angelegt, aber im Kindtask per SendIO/WaitIO bedient. Das sig-bit des Kindtasks wird wohl noch entsprechend gesetzt obwohl es hier nicht sauber alloziert werden dürfte, aber das GetMsg() des Hauptprozesses kann die Message entfernen bevor WaitIO() das im Task für dich erledigt. Damit würde dann dein WaitIO deadlocken.


[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 14:43 Uhr

Mad_Dog
Posts: 1944
Nutzer
Zitat:
Original von woop:

Der Message-Port fürs timer.device wird im Hauptprozess angelegt, aber im Kindtask per SendIO/WaitIO bedient. Das sig-bit des Kindtasks wird wohl noch entsprechend gesetzt obwohl es hier nicht sauber alloziert werden dürfte, aber das GetMsg() des Hauptprozesses kann die Message entfernen bevor WaitIO() das im Task für dich erledigt. Damit würde dann dein WaitIO deadlocken.


Danke, das war's.

Ich hab's jetzt so:
c code:
void Update(void)
{
   /*  Oszilloskop erzeugen:
    *  32Khz Samplingrate,
    *  Zeitfenster 0.0125 Sekunden
    *  Zeichenfarbe grün, Hintergrund schwarz
    */
   sc = CreateScope(Fenster,32000,0.0125,green,black);

  /*  Wichtig: Für diesen Zweck brauchen wir UNIT_MICROHZ.
    *  Die anderen Timer sind zu ungenau.
    */
   TimerDevice = OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *)sc->TimerIO,0L);
   if (TimerDevice !=0) ShutDown();

	while (1)
   {
     ReadFrame(sc);
     DrawScope(sc);
   }

}


Wobei ich mir jetzt noch was besseres anstelle der Endlosschleife einfallen lassen muß. :)
--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

04.05.2008, 20:46 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Fein dass das jetzt geht, die Endlosschleife finde ich so schon okay, da du ja zwischendurch den Task sauber per timer.device schlafen legst.
Ich würde der Ordung halber aber noch ein beenden-Event einführen, welches vom Hauptprozess getriggert werden kann, so dass sich der Task sauber beendet oder in Wait(0); geht, so dass er ohne das Forbid/Disable entfernt werden kann.

Edit:
Mir ist gerade eingefallen, dass du das timer.device nur dazu benutzt um die Daten vom Parallelport in bestimmten -- sehr kurzen Zeitabständen -- auszulesen. Das ist für einen sich sparsam benehmenden Task natürlich zuwenig. Ein zweiter Timer, welcher einen kompletten Durchlauf von ReadFrame() und DrawScope() maximal alle 1/25 Sekunden zulässt wäre wohl noch sinnvoll :D

Edit2:
Nicht nur der Ordnung halber solltest du das beenden-Event einführen sondern auch um das timer.device in deinem Task wieder korrekt schließen zu können. So hast du ein Speicherleck.
Außerdem verwendest du jetzt Fließkommazahlen in deinem Task, was wegen der oben genannten Gründe für Probleme sorgen kann. Da ich in deiner bisherigen Implementierung keine Benutzung von sc->time sehen konnte sondern du ihn nur zur Berechnung eines Integers in CreateScope heranziehst solltest du darüber nachdenken ihn zu entfernen.


[ Dieser Beitrag wurde von woop am 04.05.2008 um 22:06 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

05.05.2008, 20:39 Uhr

Mad_Dog
Posts: 1944
Nutzer
Hi,

Um das Prinzip Task per Message beenden (in diesem Fall sogar ein Prozeß) anzutesten, habe ich mal ein kleines Beispielprogramm geschrieben. Der Kindtask besteht dabei ebenfalls wieder aus einer Schleife, die in dem Fall solange läuft, bis eine Nachricht am MessagePort anliegt.

Könntet Ihr mal schauen, ob das so korrekt ist?
c code:
#include <stdio.h>
#include <stdlib.h>

#include <exec/types.h>
#include <exec/semaphores.h>
#include <dos/dostags.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

struct ReadyMessage {
  struct Message rdy_Msg;
  BOOL Ready;
};

struct Process *mainprocess, *childprocess;
BPTR output;

ULONG i;

void child(void)
{
  ULONG j;

  struct MsgPort *port;
  struct Message *Msg;

  port = CreateMsgPort();
  if (port)
  {
     port->mp_Node.ln_Name="ChildPort";
     port->mp_Node.ln_Pri=0;
     AddPort(port);
  }

  while (!(Msg = GetMsg(port)))
  {
    PutStr("Kindprozeß aktiv!\n");
    Delay(50);
  }

  //ReplyMsg(Msg);

  RemPort(port);
  DeleteMsgPort(port);

}

int main (void)
{
  struct MsgPort *port;
  struct ReadyMessage *rdy;

  if (rdy = (struct ReadyMessage *) AllocVec(sizeof(struct ReadyMessage), MEMF_PUBLIC | MEMF_CLEAR))
  {
     rdy->rdy_Msg.mn_Node.ln_Type = NT_MESSAGE;
     rdy->Ready = TRUE;
  }

  /* Den Hauptprozess finden */
  mainprocess = (struct Process *)FindTask(NULL);

  /* Öffne Konsolenfenster für Ausgabe */
  output = Open("*",MODE_OLDFILE);

  /*  Kindprozeß starten:
   *  Falls dieser eine höhere Priorität wie der Hauptprozeß hat,
   *  verdrängt der Kindprozeß den Hauptprozeß (preemtives Multitasking)
   */
  childprocess = CreateNewProcTags( NP_Entry, child,    // Code
                                    NP_Name, "child",   // Name
                                    NP_Output, output,  // Ausgabe
                                    NP_Priority, 0,     // Priorität
                                    TAG_DONE
                                  );
  Delay(250);

  Forbid();
  port=FindPort("ChildPort");

  if (port)
  {
    PutStr("ChildPort gefunden\n");
    PutMsg(port,(struct Message *)rdy);
  }

  Permit();

  Delay(250);

  FreeVec(rdy);

  PutStr("Programmende\n");

  return 0;
}


P.S.: Daß das böses Polling ist, ist mir klar. Darum geht es mir aber bei diesem Beispiel garnicht.

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

05.05.2008, 21:34 Uhr

Mad_Dog
Posts: 1944
Nutzer
Wo wir schonmal dabei sind, können wir das hier auch gleich durchsprechen:

Ich habe vor einiger Zeit ein kleines Beispuielprogramm geschrieben, um mich ein wenig in die Verwendung von Semaphoren unter AmigaOS einzuarbeiten.

Dazu habe ich mir ein einfaches Problem überlegt, bei dem zwei Prozesse derart gemeinsam auf eine Resource zugreifen, daß Race Conditions auftreten könnten. Bei diesem Beispiel versuchen zwei Prozesse auf eine Konsole zu schreiben. Die Konsole wird dabei durch eine Semaphore geschützt.
c code:
/*  Prozesse.c
 *  Ein Multitasking Beispiel
 *
 *  Autor: Norman Walter
 *  Datum: 27.5.2006
 *
 *  Ein Hauptprozeß startet einen Kindprozeß.
 *  Die gemeinsam genutzte Resource - das Konsolenfenster - wird
 *  über eine Semaphore geschützt. Die beiden Prozesse kommunizieren
 *  dabei über den MessagePort des Hauptprozesses. Der Kindprozeß setzt
 *  eine Nachricht ab, sobald er fertig ist. Auf diese Nachricht wartet
 *  der Hauptprozeß, bevor er sich beendet.
 *
 */

#include <stdio.h>
#include <stdlib.h>

#include <exec/types.h>
#include <exec/semaphores.h>
#include <dos/dostags.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

struct ReadyMessage {
  struct Message rdy_Msg;
  BOOL Ready;
};

struct Process *mainprocess, *childprocess;
BPTR output;
struct SignalSemaphore *LockSemaphore;

void child(void)
{
  unsigned long i,j;
  struct MsgPort *port;
  struct ReadyMessage *rdy;

  if (rdy = (struct ReadyMessage *) AllocMem(sizeof(struct ReadyMessage), MEMF_PUBLIC | MEMF_CLEAR))
  {
     rdy->rdy_Msg.mn_Node.ln_Type = NT_MESSAGE;
     rdy->Ready = TRUE;
  }

  ObtainSemaphore(LockSemaphore);

  PutStr("Kindprozeß gestartet\n");
  Flush(Output());

  for (i=0;i<20;i++)
  {
     for (j=0;j<5000000;j++);
     PutStr(".");
     Flush(Output());
  }

  PutStr("\n");

  Forbid();
  port=FindPort("MainPort");

  if (port)
  {
     PutStr("Kindprozeß setzt Nachricht ab\n");
     Flush(Output());
     PutMsg(port,(struct Message *)rdy);
  }
  else
  {
     printf("Fehler: Konnte MainPort nicht finden\n");
  }

  Permit();

  PutStr("Kindprozeß endet\n");
  Flush(Output());

  ReleaseSemaphore(LockSemaphore);

}

int main (void)
{
  struct MsgPort *port;
  struct ReadyMessage *reply;

  port = CreateMsgPort();
  if (port)
  {
     port->mp_Node.ln_Name="MainPort";
     port->mp_Node.ln_Pri=0;
     AddPort(port);
  }
  else
  {
     printf("Fehler: Konnte MainPort nicht erzeugen\n");
  }

  /* Semaphore zum Schützen des Konsolenfensters */
  LockSemaphore = AllocMem(sizeof(struct SignalSemaphore),MEMF_ANY);
  InitSemaphore(LockSemaphore);

  /* Den Hauptprozess finden */
  mainprocess = (struct Process *)FindTask(NULL);

  /* Öffne Konsolenfenster für Ausgabe */
  output = Open("*",MODE_OLDFILE);

  if(output==NULL)
  {
     printf("Fehler: Konnte Konsole nicht öffnen\n");
  }
  else

  printf("Hauptprozeß gestartet\n");

  /*  Kindprozeß starten:
   *  Falls dieser eine höhere Priorität wie der Hauptprozeß hat,
   *  verdrängt der Kindprozeß den Hauptprozeß (preemtives Multitasking)
   */
  childprocess = CreateNewProcTags( NP_Entry, child,    // Code
                                    NP_Name, "child",   // Name
                                    NP_Output, output,  // Ausgabe
                                    NP_Priority, 0,     // Priorität
                                    TAG_DONE
                                  );
  if (childprocess)
  {
     /*  Falls der Kindprozeß eine höhere Priorität wie der Hauptprozeß
      *  hat, wird dieser zuerst die Semaphore setzen und die
      *  Ausgabe erfolgt erst, wenn er die Semaphore wieder freigegeben hat
      */
     ObtainSemaphore(LockSemaphore);
     PutStr("Hauptprozeß versucht etwas auf die Konsole zu schreiben\n");
     ReleaseSemaphore(LockSemaphore);
  }
  else
  {
     printf("Fehler: Konnte Kindprozeß nicht starten\n");
  }

  /*  Der Hauptprozeß wartet nun auf Nachricht vom Kindprozeß */
  WaitPort(port);

  if (reply = (struct ReadyMessage *)GetMsg(port))
  {
     printf("Die Nachricht enthält: %ld\n",reply->Ready);
  }

  printf("Hauptprozeß endet\n");

  if (port) RemPort(port);
  if (LockSemaphore) FreeMem(LockSemaphore,sizeof(struct Semaphore));

  return 0;
}


Ich hoffe, daß ich dabei alles richtig gemacht habe? :dance3:

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

05.05.2008, 22:02 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Ohne es getestet zu haben sollte das so funktionieren. Ich nehme an, dass du das Polling mit einem simplen
code:
Wait(1 << port->mp_SigBit);

wegkriegst weißt du.
Mit AddPort() machst du den Port systemweit bekannt; damit es nicht so leicht Namenskollisionen gibt ist es sinnvoll dem Portnamen ein programmspezifisches Präfix anzuhängen "MeinProgramm_ChildPort", z.B.
In diesem Fall brauchst du das allerdings gar nicht und könntest auch einfach den Portzeiger in deinem Programm zugänglich machen.
Da du deine eigenen Messagetyp erzeugst den du "ReadyMessage" nennst, gehe ich davon aus, dass du vorhast noch weitere Messagetypen für andere Zwecke zu erzeugen. Beachte hier, dass beim Child erstmal nur ein Pointer ankommt, dem du nicht ansiehst, welcher Typ sich dahinter verbirgt. Falls du das so vorhaben solltest könntest du einen Basistyp definieren, der als einziges Member eine enum hat, von dem auf dem Messagetyp geschlossen werden kann, z.B. so:

code:
enum eMessageType
{
  Readymessage,
  DoSomethingMessage
};

struct CommonMessage
{
  struct Message msg;
  eMessageType type;
};

struct ReadyMessage
{
  struct CommonMessage base;
  BOOL Ready;
};

struct DoSomethingMessage
{
  struct CommonMessage base;
  char woop[42];
};


Beim Message-zusammenbauen im Sendertask dann CommonMessage::type entsprechend befüllen und das im Empfängertask entsprechend auswerten bevor du auf den tatsächlichen Typ castest.

Wenn es allerdings nur um das Beenden geht, dann könntest du durchaus auch mit einem AllocSignal/Signal/Wait zurecht kommen.



[ - Antworten - Zitieren - Direktlink - ]

05.05.2008, 22:19 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Auch dein Semaphorenbeispiel sieht funktionstüchtig aus, allerdings beachte, dass I/O-Funktionen ein Forbid() vorübergehend aufheben:

code:
Forbid();
port=FindPort("MainPort");

if (port)
{
  PutStr("Kindprozeß setzt Nachricht ab\n");
  Flush(Output());
  PutMsg(port,(struct Message *)rdy);
}


Hier kann es durchaus passieren, dass dir zwischen FindPort und PutMsg noch ein anderer Task dazwischenfunkt.

[ - Antworten - Zitieren - Direktlink - ]

05.05.2008, 23:02 Uhr

Mad_Dog
Posts: 1944
Nutzer
Zitat:
Original von woop:
@Mad_Dog:

Auch dein Semaphorenbeispiel sieht funktionstüchtig aus


Klar - funktionieren tut's. Aber zur Sicherheit lass ich es nochmal Leute sehen die (hoffentlich) mehr Erfahrung in Sachen Exec haben, als ich. Schließlich kann man auch Betriebssystemmechanismen in einer Weise anwenden, wie es sich er Erfinder eigentlich nicht gedacht hat (z.B. weil man es nicht richtig verstanden hat) und es funktioniert (mehr oder weniger zufällig) trotzdem.

Wenn das korrekt ist, dann kann ich es auch anderen Usern als Beispielcode anbieten. :)


--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

05.05.2008, 23:18 Uhr

woop
Posts: 67
Nutzer
Zitat:
Original von Mad_Dog:

Wenn das korrekt ist, dann kann ich es auch anderen Usern als Beispielcode anbieten. :)


Uhm, ohne dir zu nahe treten zu wollen, aber _dazu_ solltest du deine Beispiele noch ein wenig überarbeiten so dass z.B. Fehler sauber abgefangen werden und alle Ressourcen wieder freigegeben werden.
Außerdem solltest du dich bei Variblen/Funktionsnamen und Kommentaren auf eine Landessprache einigen :)

[ - Antworten - Zitieren - Direktlink - ]

06.05.2008, 00:02 Uhr

woop
Posts: 67
Nutzer
@Mad_Dog:

Zu deinem Semaphorenbeispiel solltest du noch einige Dinge beachten, bevor du es als Lehrbeispiel auf angehende Amigaprogrammierer loslässt.

1. Zu jedem GetMsg() gehört auch ein ReplyMsg(). In deinem Beispiel bewirkt das zwar nichts, weil kein Antwortport in die Messages eingetragen ist, aber ReplyMsg ist schlau genau um damit umgehen zu können, also in jedem Fall aufrufen.

2. Genau dein Beispiel benötigt allerdings einen Antwortport, weil der Senderprozess ja wissen will, wann er die mit AllocMem allozierte Message wieder freigeben (oder anderweitig benutzen) kann. Du umgehst das Problem hier einfach, indem der Speicher nie freigegeben wird. Bitte nicht auf die tolle Idee kommen und diesen im Empfängerprozess freigeben :D

3. Zu CreateMsgPort gehört auch ein DeleteMsgPort

4. Statt der Warte-for-Schleife ist ein Delay(25); hier viel schöner und aussagekräftiger: "Obwohl der Kindprozess schläft kann der Elternprozess wegen der gelockten Semaphore nicht weitermachen."

5. Statt mit WaitPort solltest du deine Schützlinge imho gleich mit Wait konfrontieren, damit kannst du auch prima zeigen, wie ein Task auf mehrere Dinge gleichzeitig warten kann :)

6. Nach GetMsg machen wir uns erstmal eine Kopie der interessanten Daten und schicken dann sofort das ReplyMsg, bevor wir die Daten verarbeiten (hier: ausgeben)

7. Die Semaphore sollte in MEMF_PUPLIC liegen, auch wenn das auf klassischen Amigas keinen Unterschied macht.

8. Portnamen sollten als Prefix den Programmnamen haben.



[ - Antworten - Zitieren - Direktlink - ]

06.05.2008, 00:40 Uhr

Mad_Dog
Posts: 1944
Nutzer
Zitat:
Original von woop:

4. Statt der Warte-for-Schleife ist ein Delay(25); hier viel schöner und aussagekräftiger: "Obwohl der Kindprozess schläft kann der Elternprozess wegen der gelockten Semaphore nicht weitermachen."


Diese for-Schleife ohne Anweisung ist natürlich eine bitterböse CPU-Zeit-verbraten Sache. Ehrlich gesagt habe ich das ursprünglich deshalb gemacht, weil mich der Hinweis aus den RKRMs, man dürfe aus einem Task die dos.library nicht benutzen, etwas irritiert hat (Delay() gehört ja schließlich auch zur dos.library). Aber ich verwende hier ja einen Prozeß und keinen Task, von daher müsste die Verwendung von Delay() innerhalb des Kind-Tasks klar gehen.

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

06.05.2008, 01:02 Uhr

Mad_Dog
Posts: 1944
Nutzer
Zitat:
Original von woop:
Zitat:
Original von Mad_Dog:

Wenn das korrekt ist, dann kann ich es auch anderen Usern als Beispielcode anbieten. :)


Uhm, ohne dir zu nahe treten zu wollen, aber _dazu_ solltest du deine Beispiele noch ein wenig überarbeiten so dass z.B. Fehler sauber abgefangen werden und alle Ressourcen wieder freigegeben werden.


Stimmt natürlich auch wieder.

Ein typisches Beispiel dafür, was passieren kann, wenn man sich auf eine Maschine verlässt. Dazu mußt Du wissen, daß ich diese Beispiele mit StormC gemacht habe. Und aus Faulheit kommt man eben dann zwangsläufig auf den Gedanken "wenn der Resource-Tracker nach Beendigung des Programms nicht meckert, wird's schon o.k. sein". Da kann es schonmal passieren, daß man etwas übersieht, bzw. sich auf gewisse Automatismen verlässt. I-)

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]


-1- [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > Task schläft ein [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.