Amiga.org

Operating System Specific Discussions => Amiga OS => Amiga OS -- Development => Topic started by: Jose on September 24, 2004, 04:24:21 PM

Title: Why doesn't this code starts automatically (full example with ports and message)...Also SAS linking
Post by: Jose on September 24, 2004, 04:24:21 PM
Ok, I resourt to ask you why this doesn't work.   :-o
This painfully was stripped down from some program I'm doing, or should I say, trying to...

Here's the deal. Two programs, the MainTask and the OtherProcess. MainTask loads and creates the OtherProcess, wich is compiled sperately, from Ram: using CreateNewProc() (or CreateProc() wich older AOS versions). After loading OtherProcess Maintask sets up a message and a port to receive a reply.

The problem is the MainTask simply stops and hangs and I can't close the window. Given the code it should stop print a message and exit if it doesn't find the OtherProcess port. So I take it it finds it? But it just stops and the OtherProcess doesn't even output the initial message.
I managed to make it work like this. Runing the OtherProcess first from workbench(haven't tried from another shell but i guess it should work too) and then running the MainTask.
But in this case two instances of OtherProcess are running right?

So I take it that the MainTask is missing something to make the OtherProcess run. I wanted also know what's missing for it to display the messages in the same Window as the MainTask, cause that's what was intended when I wrote the code.

Here's the code for both, no clean up is included for simplicity, don't bother I just want to know why it doesn't work, my main program has cleanup for this part of the ini process.

MainTask

Code: [Select]

#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <exec/execbase.h>
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/exec.h>
#include <utility/utility.h>
#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <stdio.h>
#include <stdlib.h>

struct Library *UtilityBase;

struct MsgPort *ReplyPort;
struct MsgPort *OtherProcessPort;

BPTR OtherProcessSegList = 0;

struct Message_to_OtherProcess
  { struct Message MesstoOP;
    struct Task *MainTask;
    struct Task *OtherProcess;
  };

struct Message_to_OtherProcess *Message_to_OtherProcess, *OtherProcessResponse;

struct Task *MainTask; /*This task*/
struct Task *OtherProcess;



int main (int argc, char **argv)
{                
 extern struct ExecBase *SysBase;
 
 char *OtherProcessName = &quot;Ram:OtherProcess&quot;;
 struct MsgPort *OtherProcess35;
 struct Process *OtherProcess;
 BPTR MainTaskOutputHandle;
 
 if (!(Message_to_OtherProcess  = (struct Message_to_OtherProcess *)AllocMem (sizeof(Message_to_OtherProcess),MEMF_PUBLIC|MEMF_CLEAR)))
   {printf (&quot;Couldn't allocate memory for Message to OtherProcess\n&quot;);
    exit (0);}
 
 if (!(ReplyPort =  CreatePort (0,0)))
   {printf (&quot;Couldn't create ReplyPort\n&quot;);
    exit (0);}
 Message_to_OtherProcess->MesstoOP.mn_ReplyPort = ReplyPort;
 
 
 if (!(OtherProcessSegList=LoadSeg(OtherProcessName)))
      {printf (&quot;Couldn't find OtherProcess\n&quot;);
       exit (0);}

 MainTaskOutputHandle = Output();
 
 printf(&quot;Exec Version is %d.%d\n&quot;,SysBase->LibNode.lib_Version,SysBase->LibNode.lib_Revision);
 
 if (SysBase->LibNode.lib_Version<=36)
   
    {if (!(OtherProcess35 = CreateProc(&quot;OtherProcess&quot;, 0L, OtherProcessSegList, 1000L)))
      {printf (&quot;Couldn't find OtherProcess35\n&quot;);
      exit(0);}
    printf (&quot;Loaded OtherProcess35\n&quot;);
    }
 
 /* else Kickstart is >=v36, add OtherProcess using CreateNewProc */
 else
 {
   struct TagItem TagsforCrtNewPr[5];
   
   /*open utility library*/
   if (!(UtilityBase = OpenLibrary (&quot;utility.library&quot;,0L)))
     {printf (&quot;Couldn't open utility.library\n&quot;);
      exit(0);}
     
   TagsforCrtNewPr[0].ti_Tag = NP_Seglist;
   TagsforCrtNewPr[0].ti_Data = OtherProcessSegList;
   TagsforCrtNewPr[1].ti_Tag = NP_Name;
   TagsforCrtNewPr[1].ti_Data = (ULONG)&quot;OtherProcess&quot;;
   TagsforCrtNewPr[2].ti_Tag = NP_Cli;
   TagsforCrtNewPr[2].ti_Data = TRUE;
   TagsforCrtNewPr[3].ti_Tag = NP_Output;
   TagsforCrtNewPr[3].ti_Data = MainTaskOutputHandle;
   TagsforCrtNewPr[4].ti_Tag = TAG_DONE;
   if (!(OtherProcess = CreateNewProc (TagsforCrtNewPr)))
      {printf(&quot;Couldn't find OtherProcess\n&quot;);
      exit(0);}
   printf(&quot;Other Process Loaded Successfully\n&quot;);
 }

 /*Locate MainTask*/
 MainTask = FindTask(NULL);
 Message_to_OtherProcess->MainTask = MainTask;
 
 
 /******************** WAIT FOR OTHER PROCESS TO SET UP IT'S PORT*************************/
 Delay (250);
 
 /*Send message to OtherProcess and wait for reply*/
 
 if (!(OtherProcessPort = FindPort (&quot;OtherProcessPort&quot;)))
   {printf(&quot;Couldn't find OtherProcessPort\n&quot;);
    exit (0);}
 PutMsg (OtherProcessPort, (struct Message *)Message_to_OtherProcess);
 WaitPort (ReplyPort);
 if (!(OtherProcessResponse = (struct Message_to_OtherProcess *)GetMsg (ReplyPort)))
   {printf (&quot;No message at port\n&quot;);
    exit (0);}
 
 printf (&quot;Message from OtherProcess received dude!!!\n&quot;);
 OtherProcess = OtherProcessResponse->OtherProcess;
 
 
/*void CleanUp (void)*/

/* Should have something like that ;) */


exit (0);
}




OtherProcess

Code: [Select]
#include
#include
#include
#include

#include
#include
#include
#include
#include
#include

struct Task *MainTask;
struct Task *OtherProcess; /*This one actually*/

struct MsgPort *OtherProcessPort;

struct Message_to_OtherProcess {
struct Message MesstoOP;
struct Task *MainTask;
struct Task *OtherProcess;
};
struct Message_to_OtherProcess *ReceivedMessage;

int main (int argc, char **argv)
{
 printf("OtherProcess Ver1.0, 2004\n");

 if (!(OtherProcessPort = CreatePort ("OtherProcessPort", 0)))
   {printf ("Couldn't create OtherProcessPort\n");
    exit (0);}
 
 WaitPort (OtherProcessPort);
 if (!(ReceivedMessage = (struct Message_to_OtherProcess *)GetMsg (OtherProcessPort)))
   {printf ("No message received");
    Delay (200UL);
    exit (0);}
 printf ("Message from MainTask received DUDE!!\n");
 MainTask  = ReceivedMessage->MainTask;
 
 
 OtherProcess = FindTask(NULL);
 ReceivedMessage->OtherProcess = OtherProcess;
 
 ReplyMsg ((struct Message *)ReceivedMessage);


 printf ("Message replyed...");

exit (0);
}



And by the way, could someone write a very short idiot's guide to linking with SAS C? I have it complaining about some undefined symbols and I'm pretty sure that the stub it prompts to link with as default could be the cause of some programs crashing (not in the exaple above) :oops:
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS linking
Post by: Sidewinder on September 24, 2004, 05:53:49 PM
What is the undefined symbol that the linker complains about?

Usually the easiest way to link is to add "link" to the sc command:

sc myprog.c myprog link
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Cymric on September 24, 2004, 09:10:47 PM
Okay, this *has* been a long time for me, so please pay very close attention to what I say---it could be horrendously wrong. I am quite sure Piru and Karlos will find the bugs with their eyes shut, but I can't resist trying my hand at it.

First of all: your code is subject to deadlocks. Yes, I know you incorporated Delay()s to introduce 'sufficient' pause for the ports to appear. But that is a Bad Idea in general. Way better is to start the main program, have it start the second while waiting for a signal from the second program to start the message exchange. That way deadlocks are avoided.

Second, an illegal copy of the RKRF: Libraries informs me you need to set the Message's type and length prior to sending, which you have not done. You need to add the following lines below

Message_to_OtherProcess->MesstoOP.mn_ReplyPort = ReplyPort;

Message_to_OtherProcess->MesstoOP.mn_Node.ln_Type = NT_MESSAGE;
Message_to_OtherProcess->MesstoOP.mn_Length=sizeof(struct Message_to_OtherProcess));


Third, starting a program from the Workbench involves waiting for the startup message from the Workbench itself. I think the startup code already handles that, and it is also exceedingly unlikely that your code interferes with this special message as the message ports are very different. Nevertheless, I've always paid extra good care when dealing with the Workbench :-).

Finally, I have to admit I found it extremely odd that the return type of CreateProc() is struct Message *. Are you *sure* that it isn't struct Process *?

I hope this helps, and if not, well, I'm sure a Guru will be along shortly :-D.
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 25, 2004, 12:09:32 AM
@Sidewinder

" What is the undefined symbol that the linker complains about?"
Some functions on jpeg.library. It's because I'm not giving the right instructions to compile and link I think.

"Usually the easiest way to link is to add "link" to the sc command:
sc myprog.c myprog link"

I'm using a SASC project thingie. I'll try that. After link comes the object to link with right?


Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 25, 2004, 12:21:25 AM
@Cymric

"First of all: your code is subject to deadlocks. Yes, I know you incorporated Delay()s to introduce 'sufficient' pause for the ports to appear. But that is a Bad Idea in general. Way better is to start the main program, have it start the second while waiting for a signal from the second program to start the message exchange. That way deadlocks are avoided."

Yes, but the stupid thing is, I'm trying to send a message with a pointer to the main task and it's allocated signal to the child process in the first place  :-) This is because the child process is loaded from disk as a separate compiled program. But I think 250 ticks (5 seconds) should be enouph as I don't have anything else running.
This is just to understand what's wrong with this and learn, I guess I'll have to get another method if I want to do this with 1.3.

"Second, an illegal copy of the RKRF: Libraries informs me you need to set the Message's type and length prior to sending, which you have not done. You need to add the following lines below
Message_to_OtherProcess->MesstoOP.mn_ReplyPort = ReplyPort;
Message_to_OtherProcess->MesstoOP.mn_Node.ln_Type = NT_MESSAGE;
Message_to_OtherProcess->MesstoOP.mn_Length=sizeof(struct Message_to_OtherProcess));"

Yes! I forgot that. Done now, but it still hangs. The child process doesn't even display the initial message on the Shell.


"Third, starting a program from the Workbench involves waiting for the startup message from the Workbench itself. I think the startup code already handles that, and it is also exceedingly unlikely that your code interferes with this special message as the message ports are very different. Nevertheless, I've always paid extra good care when dealing with the Workbench ."

Hmm, ok later:-)

"Finally, I have to admit I found it extremely odd that the return type of CreateProc() is struct Message *. Are you *sure* that it isn't struct Process *?"
It's struct MsgPort actually...

Funny that  the initialization of a program can be much bigger and troublesome than the program itself.
 
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Sidewinder on September 25, 2004, 12:22:49 AM
Just add your object files in the command:

sc myprog.c code1.o code2.o link

or you can use slink with the object files:

slink myprog.o code1.o code2.o to myprog

will link and create the executable "myprog".
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Cymric on September 25, 2004, 08:34:42 AM
Well, waiting for a signal or message at the beginning of your main program isn't hard. Just create a named message port, have the child task find it using FindPort() and send a message to it. But that is in principle the same thing what you are trying to do here, with the exception it doesn't work ;-). (ALternatively, you can allocate a signal in the user-space range by number, say 16, and use that exclusively as a means of signalling other programs to they are ready to run. Only problem is that you have to use Wait() instead of WaitPort(), and find out the MessagePort's own signal number.)

So after inserting my code it still doesn't work. Dang. There are a few other suggestions I can help you with:
a) printf() is buffered, meaning that what you printf() is not always immediately displayed on-screen. If your program hangs before the buffer is emptied, you can wait until doomsday before you see the pent-up messages appear. So my first suggestion would be to put a fflush(stdout); after each printf() and then see where the program ends up.
b) That CreateProc()/CreateNewProc()-business is needless extra complication at this moment. Can you rewrite the programs in such a way that you have to start them both by hand and see what happens during the message exchange? If *that* is working okay, you know the error is elsewhere.

Good luck!
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 25, 2004, 02:46:16 PM
@Cymric

I stripped down the CreateProc() and CreateNewProc() stuff (see below) and it works flawlessly  :-D So the problem is there.

That flush thing you mentioned I didn't do it because like it is now the OtherProcess simply outputs in it's own window.
Don't have time now, will try to output to the MainTask window later...

So, the hunt to the bug is declared  :-) What was wrong with the first versions ?


Here's the now working versions without CreateProc() and CreateNewProc():

MainTask

Code: [Select]

#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <exec/execbase.h>
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/exec.h>
#include <utility/utility.h>
#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <stdio.h>
#include <stdlib.h>


struct MsgPort *ReplyPort;

struct Message_to_OtherProcess
  { struct Message MesstoOP;
    struct Task *MainTask;
    struct Task *OtherProcess;
  };

struct Message_to_OtherProcess *Message_to_OtherProcess, *OtherProcessResponse;

struct Task *MainTask; /*This task*/
struct Task *OtherProcess;



int main (int argc, char **argv)
{                
 
 BPTR MainTaskOutputHandle;
 
 if (!(Message_to_OtherProcess  = (struct Message_to_OtherProcess *)AllocMem (sizeof(Message_to_OtherProcess),MEMF_PUBLIC|MEMF_CLEAR)))
   {printf (&quot;Couldn't allocate memory for Message to OtherProcess\n&quot;);
    exit (0);}
 
 if (!(ReplyPort =  CreatePort (0,0)))
   {printf (&quot;Couldn't create ReplyPort\n&quot;);
    exit (0);}
 Message_to_OtherProcess->MesstoOP.mn_ReplyPort = ReplyPort;
 Message_to_OtherProcess->MesstoOP.mn_Length = sizeof(struct Message_to_OtherProcess);
 Message_to_OtherProcess->MesstoOP.mn_Node.ln_Type = NT_MESSAGE;
   
 
 MainTaskOutputHandle = Output();

 /*Locate MainTask*/
 MainTask = FindTask(NULL);
 Message_to_OtherProcess->MainTask = MainTask;
 
 
 /******************** WAIT FOR OTHER PROCESS TO SET UP IT'S PORT*************************/
 Delay (250);
 
 /*Send message to OtherProcess and wait for reply*/
 
 if (!(OtherProcessPort = FindPort (&quot;OtherProcessPort&quot;)))
   {printf(&quot;Couldn't find OtherProcessPort\n&quot;);
    exit (0);}
 PutMsg (OtherProcessPort, (struct Message *)Message_to_OtherProcess);
 WaitPort (ReplyPort);
 if (!(OtherProcessResponse = (struct Message_to_OtherProcess *)GetMsg (ReplyPort)))
   {printf (&quot;No message at port\n&quot;);
    exit (0);}
 
 printf (&quot;Message from OtherProcess received dude!!!\n&quot;);
 OtherProcess = OtherProcessResponse->OtherProcess;
 
 
/*void CleanUp (void)*/

/* Should have something like that ;) */


exit (0);
}



OtherProcess

Code: [Select]
#include
#include
#include
#include

#include
#include
#include
#include
#include
#include

struct Task *MainTask;
struct Task *OtherProcess; /*This one actually*/

struct MsgPort *OtherProcessPort;

struct Message_to_OtherProcess {
struct Message MesstoOP;
struct Task *MainTask;
struct Task *OtherProcess;
};
struct Message_to_OtherProcess *ReceivedMessage;

int main (int argc, char **argv)
{
 printf("OtherProcess Ver1.0, 2004\n");

 if (!(OtherProcessPort = CreatePort ("OtherProcessPort", 0)))
   {printf ("Couldn't create OtherProcessPort\n");
    exit (0);}
 
 WaitPort (OtherProcessPort);
 if (!(ReceivedMessage = (struct Message_to_OtherProcess *)GetMsg (OtherProcessPort)))
   {printf ("No message received");
    Delay (200UL);
    exit (0);}
 printf ("Message from MainTask received DUDE!!\n");
 MainTask  = ReceivedMessage->MainTask;
 
 
 OtherProcess = FindTask(NULL);
 ReceivedMessage->OtherProcess = OtherProcess;
 
 ReplyMsg ((struct Message *)ReceivedMessage);


 printf ("Message replyed...");

exit (0);
}
 
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 25, 2004, 02:57:04 PM
@Sidewinder
Cool.
What about when you want to link with and external link library?
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Cymric on September 25, 2004, 07:58:49 PM
Glad to hear that at least the messaging was doing okay now. I've dug into some forgotten Google links, hoping to find a PDF of the Guru Book, but alas, I was stuck with the criminally bad AmigaDOS manual plus some header files. Fortunately, I managed to find something which is definitely suspicious. dos/dostags.h mentions that the tags NP_CloseInput, NP_CloseOutput and NP_CloseError are all TRUE by default, meaning that the system automagically closes any open file handles for these streams upon the exit of the process. You use default input and error file handles (i.e., Open("NIL:")-sorta things), but supply the output handle of the main task. So here's what happens: the child process ends, the system closes all streams, including the output stream of the main process, thus yanking the carpet out from under the feet of your main process. It still needed that stream. So what I'm thinking is that your code actually works, but that the programs never have a chance to tell you.

To remedy this bug, either have the child process use its own output stream, or supply an additional tag to the TagList setting NP_CloseOutput to FALSE. You understand I'm kinda curious what will happen now ;-).
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Sidewinder on September 25, 2004, 08:19:13 PM
To link with and external library just add the library name in with the object modules:

slink myprog.o module1.o module2.o mylibrary.lib to myprogram

or from sc:

sc myprog.c mylibrary.lib link
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 26, 2004, 12:44:04 AM
Ok, I'm back... Tried it with that NP_CloseOutput tag written as FALSE. It's better, now sometimes it crashes when I load it first time, others it works well and the OtherProcess now outputs well to the MainTask window :-)
One of the problems could be that I donn't have any cleanup deleting the MsgPorts so when I run it second time the port list is corrupt.
But still that doesn't justify the fact it randomly crashes even when run first time after a cold boot.
I remember in the RKM Lib they say that one should do a Forbid()before calling the FindPort(), but in this case I know the port is not going anywhere...
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Cymric on September 26, 2004, 01:43:23 PM
You're not making this easy for me, are you? I'm afraid I'm at the end of my Latin, since I don't have an Amiga myself to try out your code. If this were Linux, things would be a cinch: the program dumps core, and then gdb can analyse the core quicker than a politician can change his opinion. I can only write down the following hints:

a) Try to write a program which includes the CreateNewProc()-stuff, but not the message passing. The simplest way is to comment out relevant sections with #if 0 / #endif pairs.
b) Do try the printf()/fflush() method. In addition, have the program print various values of variables, like pointers. See if they make sense.
c) Run the program through a debugger. If you use SAS/C, you can compile the program with debugging information so you don't have to stare at pure assembly. It's difficult since you're creating a separate process, and I'm fairly sure SAS/C can't debug that from within the first program.
d) Can you determine the Guru-number?

Good luck!
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 26, 2004, 06:18:29 PM
Hey 8-) Went straight to c) and SAS/C does allow debbuging for subtasks and processes but I haven't read the manual in that part. Over 100 pages just for the debugger. Very complete package.

Anyway, done a step by step trace through each function and it crashes right when I call the CreateNewProc function so the problem is there, I just can't see what it is. Could it be the NP_Cli Tag ? I don't have the DOS manual... The includes say that tag is for creating a cli structure.

I'm gonna go the lazy way and wait for someone to give a hint, already tired, I'm sure I won't forget the error when I know what it is
 :-o
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 27, 2004, 12:15:54 PM
Bump...
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on September 29, 2004, 01:53:14 PM
By the way, it's not the NP_Cli tag either. I though that could be causig the NewProcess startup to treat it like a Cli process and not remove the WBStartup message before using the DOS library functions. But it still crashes when launched from WB :pissed:
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Jose on October 01, 2004, 10:13:29 PM
Found the {bleep}!! :-x

It's in this line:

Code: [Select]
if (!(Message_to_OtherProcess  = (struct Message_to_OtherProcess *)AllocMem (sizeof(Message_to_OtherProcess),MEMF_PUBLIC|MEMF_CLEAR)))
 
I guess it should be obvious. Do I look stupid now? 8-)
But I guess it's with errors like these that one learns.

I can now go on with the other stuff  :-D
Title: Re: Why doesn't this code starts automatically (full example with ports and message)...Also SAS link
Post by: Cymric on October 06, 2004, 04:11:39 PM
Quote
Jose wrote:
I guess it should be obvious. Do I look stupid now? 8-)

Ah, bloody hell. Jeez. Well, yeah. After staring at it for five minutes, it struck me: you forgot a struct in the sizeof-operator. I've been in the habit of typedef'ing my structures so I don't have to write down the keyword all the time, and as an added bonus can use a simple type to indicate a pointer to the structure. So the missing keyword never bothered me, as I have not written one myself in such a situation since 1998 :-D. (C++ does this automatically for you, by the way, and I think this is a Good Thing.)

In your case it would mean that you write:

typedef struct Message_to_OtherProcess  MtoOP;
typedef struct Message_to_OtherProcess *MtoOPptr;


and then use program lines like

if (!(Message_to_OtherProcess = (MtoOPptr)AllocMem(sizeof(MtoOP), MEMF_CLEAR|MEMF_PUBLIC)))

I can recommend such abbreviations, they help you guard against mistakes like this, plus it makes programs easier to read: there is never any confusion about whether something is a true variable, or a meta-variable.