[comp.sys.amiga.tech] Problem with CreateProc & LoadSeg

d87-khd@sm.luth.se (Karl-Gunnar Hultland) (05/04/90)

I have some sort of problem with an application I'm working on.
I want to have a main program which should take care of menues etc and
then upon command Load and Create some new processes who will open a
window on the Custom screen and execute in that window.

The problem is :
If I comment away the LoadSeg and CreateProc and start the program
Proc.c via a different CLI it works Like a Dream. BUT if I use the
code in Main.c I never get the window to open.
I have verifyed with Xoper that the process exists but doesn't open 
the window and thus it can't exit.

The question is: Is there ANYTHING I should do that I don't??
(code extracts follow .sig)

					Karl

---

Karl Hultland,(d87-khd@sm.luth.se)
University of Lulea,Sweden

Egoist: a person of low taste, more interested in himself than in me.
						- A. Bierce


=========================================================================
Main.c
=========================================================================

handleMSG(n)
int n;
{
	if (segm1) UnLoadSeg(segm1);
	MenuOn(0);
	MenuOn(3);
	return 0;
}

Load(n,msg,str)
int n;
struct OLFMessage *msg;
STRPTR str;
{
	MenuOff(0);
	MenuOff(3);

	segm1=LoadSeg(str);
	proc1=CreateProc(str,0,segm1,4000); 

	mesg1->Msg.mn_Node.ln_Type = NT_MESSAGE;
	mesg1->Msg.mn_Length=sizeof(struct OLFMessage);
	mesg1->Msg.mn_ReplyPort=OLFr;
	mesg1->Code=HELLO;
	mesg1->From=MAIN;
	mesg1->To=TEST;
	mesg1->scr=myscr;
	SafePutToPort( (struct Message *) mesg1,"OLF");

	return 0;
}




=========================================================================
Proc.c
=========================================================================
	
	do {
	msg=WaitForMsg("OLF",HELLO,TEST); /* Wait for WELCOME message */
	} while (msg==NULL);
	
	scr=msg->scr;			    /* Get address of screen */
	TestWin.Screen=scr;	
	win=OpenWindow(&TestWin);    /* Open window on customscreen */
	
	signalmask=1L << win->UserPort->mp_SigBit;
	
	while (!done){    /* Wait until closegadget is selected */
		signals=Wait(signalmask);
		if (signals & signalmask)
			done=handleIDCMP(win);
	
		};
	
	if (win) CloseWindow(win);
	if (GfxBase) CloseLibrary( (struct Library *) GfxBase);
	if (IntuitionBase) CloseLibrary((struct Library *) IntuitionBase);
	
	ReplyMsg( (struct Message *)msg );		
}

a464@mindlink.UUCP (Bruce Dawson) (05/05/90)

> root@dialog.sub.org writes:
> 
> Msg-ID: <1856@dialog.sub.org>
> Posted: 5 May 90 08:23:45 GMT
> 
> Org.  : Dialog Software Development
> Person: Christian Motz
> 
> Now for the "dirty" way, and  my  question  how  dirty  this  is,  and
> whether or not there is any cleaner way to do it. My  problem  was  to
> start a process much like Execute(), only asynchronously. As far as  I
> know, AmigaDOS knows no call for something like this. I know there are
> things like ArpAsyncRun(), but I would like to  do  it  on  a " stock"
> Amiga (besides: I wonder if ArpAsyncRun() is so clean itself).
> 
> Now hold on to your seats, NetPeople, just in case this is  the  worst
> programming practice you have ever seen -- feel free to flame me,  but
> only if you know of a better, "legitimate" solution.
> 
> --
> Christian Motz                                     root@dialog.sub.org


     Two of your debatable assumptions can easily be made solid.  These are the
assumption that CreateProc() will continue to return the address of the message
port, and that A0 and D0 can continue to be set by poking into the stack.  Both
of these problems can be solved by doing a CreateProc on a short little
assembler routine which will communicate with the main program.  The assembler
routine tells the main program where it's task structure is (easily found with
FindTask(0L)) then it loads A0 and D0 (far far easier to load A0 and D0 of your
own task than somebody elses) and then it does a JSR to the code that you
actually want turned into a process.  That still leaves the messiness of
filling out pr_CIS and pr_COS, but at least two assumptions disappear.

.Bruce.
--
Bruce Dawson    Day job: Distinctive Software Inc.
                Company name: CygnusSoft Software
Work hard, rock hard, eat hard, sleep hard, grow big, wear glasses if
you need 'em.    Webb Wilder credo.

root@dialog.sub.org (Christian Motz) (05/05/90)

In article <897@tau.sm.luth.se> d87-khd@sm.luth.se (Karl-Gunnar Hultland) writes:
>
>I have some sort of problem with an application I'm working on.
>I want to have a main program which should take care of menues etc and
>then upon command Load and Create some new processes who will open a
>window on the Custom screen and execute in that window.
>
>The problem is :
>If I comment away the LoadSeg and CreateProc and start the program
>Proc.c via a different CLI it works Like a Dream. BUT if I use the
>code in Main.c I never get the window to open.
>I have verifyed with Xoper that the process exists but doesn't open 
>the window and thus it can't exit.
>
>The question is: Is there ANYTHING I should do that I don't??

If I judge this right from the description of  the  problem,  you  are
basically trying to run an asynchronous Process  using  LoadSeg()  and
CreateProc(). I tried this, and  after  some  time  got  it  to  work,
although I am quite sure that my method is somewhat illegal. But  more
about that later on.
   When a program is started, the C startup code checks to see whether
it is started from the CLI (or via Execute() for that matter) or  from
Workbench. This is done by checking the pr_CLI member in  the  process
structure. If it is NULL, the code assumes it has  been  started  from
Workbench and therefore is waiting for a  Workbench- startup- message.
I suppose  that  this  is   the   problem   you'  re   having --   the
main()-function never gets called although a process has been created,
right? Obviously the C startup  code  is  waiting  for  the  Workbench
message it never gets.
   The "clean" way to circumvent this, I think, is to not  use  the  C
startup code, i.e. _main(). This of course can be painful if you  like
to use stdio stuff, but it works.

Now for the "dirty" way, and  my  question  how  dirty  this  is,  and
whether or not there is any cleaner way to do it. My  problem  was  to
start a process much like Execute(), only asynchronously. As far as  I
know, AmigaDOS knows no call for something like this. I know there are
things like ArpAsyncRun(), but I would like to  do  it  on  a " stock"
Amiga (besides: I wonder if ArpAsyncRun() is so clean itself).

Now hold on to your seats, NetPeople, just in case this is  the  worst
programming practice you have ever seen -- feel free to flame me,  but
only if you know of a better, "legitimate" solution.

Code fragment follows ...



/*
** We LoadSeg() the compress code at this point if it is not already
** loaded. With this we can achieve an additional performance increase
** by LoadSeg()'ing it just once at the beginning, then reusing it over
** and over again and UnLoadSeg()'ing it when we're done. This saves
** valuable time loading the code in memory.
*/
        if (seglist == 0L) {
            seglist = LoadSeg(comp);
        }
/*
** Now it is time to open the files to be used as compress's standard
** in- and output. We have to seek to position 12 of the input file,
** since it begins with "#! cunbatch\n" before the compressed batch
** file. Standard output is, of course, the pipe. We open them here
** because it would mess with the Forbid()-call if we would do it
** later on, since I/O re-enables Multitasking, thus effectively
** voiding any setup we make for the new process.
*/
        if (seglist != 0L) {
            if ((cis = Open(in, MODE_OLDFILE)) != NULL) {
                Seek(cis, 12L, -1L);
                if ((cos = Open(pn, MODE_NEWFILE)) != NULL) {
/*
** Here comes the tricky part. We have to create the process from the
** loaded code and hand the command line arguments to it. For this we
** disable multitasking while we set up the process environment of the
** new process, to keep the operating system from messing with the data.
*/
                    Forbid();
                    pid = CreateProc("compress", 0L, seglist, 4000L);
/*
** Guess how long it took me 'til I found out that CreateProc() returns
** the address of the process's MessagePort, not its process structure?
** Anyway, the MessagePort is the second member of the structure, the
** first being the Task structure. Thus the process structure address
** is pid-sizeof(struct Task). Voila, you have the process address,
** allowing you to manipulate it any way you want to. This of course is
** an assumption, and thus bad programming practice. But what else are
** you supposed to do under this operating system?
*/
                    pr = (struct Process *)(pid - (long)sizeof(struct Task));
                    pr->pr_CIS = (BPTR)cis;
                    pr->pr_COS = (BPTR)cos;
                    cli->cli_CommandName = (long)cmd >> 2L;
                    pr->pr_CLI = (long)cli >> 2L;
/*
** Have you ever thought about how command line arguments are passed
** under AmigaDOS? No? Well, the read this and learn something :-).
** A newly created process gets the address of the command line
** parameters in the processor register A0, and its length in the
** register D0. This is all very fine and dandy, and makes for nice
** and easy startup code. But if you try to create a process and hand
** it some parameters, it is virtually impossible to set these registers
** in an operating system-friendly fashion. The only way I have found is
** by determining the addresses of these registers in the initial
** register set on the process stack, and writing to them the desired
** values. This of course is really messy and ugly, so you may not want
** to look at it.
*/
                    t = (long)(pr->pr_Task.tc_SPUpper);
                    d = t - 68L;
                    a = d + 32L;
                    a0 = (char **)a;
                    d0 = (long *)d;
                    *a0 = opt;
                    *d0 = (long)strlen(opt);
/*
** Everything is set up now, so we can allow the operating system to
** resume multitasking, thus allowing the new process to be scheduled
** for execution. We don't care what happens to it (actually we have
** no other choice), so we continue by calling unbatch, feeding it the
** pipe as input file.
*/
                    Permit();

End of code fragment ...



How long can you get thrown in jail for doing something like that? But
seriously, how about a better way? Any hint would be appreciated.  The
above code works, no doubt about it, but after writing it I felt  like
I had just emerged from a pile of  sh**,  because  of  all  the  messy
things I did and assumptions I made ...

--
Christian Motz                                     root@dialog.sub.org

peter@sugar.hackercorp.com (Peter da Silva) (05/06/90)

In article <1856@dialog.sub.org> root@dialog.sub.org (Christian Motz) writes
about C startup code waiting for a workbench message.
>    The "clean" way to circumvent this, I think, is to not  use  the  C
> startup code, i.e. _main(). This of course can be painful if you  like
> to use stdio stuff, but it works.

No, the clean way to do this is to send a workbench message. I published a
complete package for starting programs in this environment, under the name
"launch". Look for it at your nearest archive site.

If you don't do something like this the process will never get cleaned up
after, and you'll eat memory every time you run it.

Then you can add extra stuff you want to feed the program (like file handles)
after the message. This is pretty much how RogueStart works (which is something
else you should look into).

> How long can you get thrown in jail for doing something like that?

How long can you tread water?
-- 
 _--_|\  Peter da Silva <peter@sugar.hackercorp.com>.
/      \
\_.--._/ I haven't lost my mind, it's backed up on tape somewhere!
      v  "Have you hugged your wolf today?" `-_-'

peter@sugar.hackercorp.com (Peter da Silva) (05/06/90)

In article <1677@mindlink.UUCP> a464@mindlink.UUCP (Bruce Dawson) writes:
> These are the
> assumption that CreateProc() will continue to return the address of the
> message port

Since it's more or less documented as doing this, and since changing this
would break so much working code, this is pretty safe.
-- 
 _--_|\  Peter da Silva <peter@sugar.hackercorp.com>.
/      \
\_.--._/ I haven't lost my mind, it's backed up on tape somewhere!
      v  "Have you hugged your wolf today?" `-_-'

root@dialog.sub.org (Christian Motz) (05/07/90)

In article <5662@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
>
> [My own ramblings about CreatProc()'ing a process deleted]
>
>No, the clean way to do this is to send a workbench message. I published a
>complete package for starting programs in this environment, under the name
>"launch". Look for it at your nearest archive site.

Yes, but can it start *UNMODIFIED* CLI programs? They won't be running
properly when not started from CLI, and I  can't  imagine I  would  be
able to set pr_CIS and pr_COS in anyway. As I said, The problem in  my
case is to create sort of an  asynchronous  Execute().  This  includes
passing command line arguments in the standard  fashion  and  allowing
for pr_CIS and pr_COS to be set.


>If you don't do something like this the process will never get cleaned up
>after, and you'll eat memory every time you run it.

Well, actually the code I posted works just fine, even  in  the  sense
that it doesn't even lose one byte of memory.  Obviously  the  process
gets cleaned up perfectly, since I seem  to  have  fooled  the  OS  in
believing that it is a standard CLI process. Strange but true.


>Then you can add extra stuff you want to feed the program (like file handles)
>after the message. This is pretty much how RogueStart works (which is something
>else you should look into).

As I said, this is something I cannot do, since  the  programs  to  be
started are usually standard CLI programs, often to  the  effect  that
they can't or shouldn't be modified. Arrgh! I want fork()!

--
Christian Motz                                     root@dialog.sub.org

jmeissen@oregon.oacis.org (John Meissen) (05/08/90)

In article <1677@mindlink.UUCP> a464@mindlink.UUCP (Bruce Dawson) writes:
>> root@dialog.sub.org writes:
>> whether or not there is any cleaner way to do it. My  problem  was  to
>> start a process much like Execute(), only asynchronously. As far as  I
 [...]
>of these problems can be solved by doing a CreateProc on a short little
>assembler routine which will communicate with the main program.  The assembler

This is basically what I did when I wrote the fork() implementation for 
the Lattice compiler. Given this approach you have a lot of flexibility.
I attached an assembler stub to the LoadSeg'd code that waited for a
startup message from the parent process. This message contained the 
argument string and miscellaneous stuff I don't remember any more. In
addition, the reply port was used for returning the exit status of the
child process, making it possible to wait on it and unload it. 
That's one thing you need to remember, that LoadSeg'd code won't automatically
get UnLoaded, you have to either do it manually or attach the SegList to the
task structure so the system will do it for you.

-- 
 John Meissen ............................... Oregon Advanced Computing Institute
 jmeissen@oacis.org        (Internet) | "That's the remarkable thing about life;
 ..!sequent!oacis!jmeissen (UUCP)     |  things are never so bad that they can't
 jmeissen                  (BIX)      |  get worse." - Calvin & Hobbes

jmeissen@oregon.oacis.org (John Meissen) (05/08/90)

In article <5662@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
>No, the clean way to do this is to send a workbench message. I published a

This only works for programs that are written to support being launched from
Workbench. If the program expects a CLI type command line with arguments and
switches, then this won't work.

-- 
 John Meissen ............................... Oregon Advanced Computing Institute
 jmeissen@oacis.org        (Internet) | "That's the remarkable thing about life;
 ..!sequent!oacis!jmeissen (UUCP)     |  things are never so bad that they can't
 jmeissen                  (BIX)      |  get worse." - Calvin & Hobbes

peter@sugar.hackercorp.com (Peter da Silva) (05/09/90)

In article <482@oregon.oacis.org> jmeissen@oregon.oacis.org (John Meissen) writes:
> In article <5662@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
> >No, the clean way to do this is to send a workbench message. I published a

> This only works for programs that are written to support being launched from
> Workbench.

ALL programs should be written to support being launched from either the
Workbench or the CLI, so this translates to "this only works for non-buggy
programs". Which is fine by me. The CLI startup environment is a crock
anyway.

And, of course, if you're writing the launchee as well as the launcher you can
go wild...
-- 
 _--_|\  Peter da Silva <peter@sugar.hackercorp.com>.
/      \
\_.--._/ I haven't lost my mind, it's backed up on tape somewhere!
      v  "Have you hugged your wolf today?" `-_-'

panon@cheddar.cc.ubc.ca (Paul-Andre Panon) (05/09/90)

In article <1860@dialog.sub.org> root@dialog.sub.org (Christian Motz) writes:
>>Then you can add extra stuff you want to feed the program (like file handles)
>>after the message. This is pretty much how RogueStart works (which is something
>>else you should look into).
>
>As I said, this is something I cannot do, since  the  programs  to  be
>started are usually standard CLI programs, often to  the  effect  that
>they can't or shouldn't be modified. Arrgh! I want fork()!
                                                   ^^^^^^^
>
>--
>Christian Motz                                     root@dialog.sub.org

I was going to say "couldn't you fake a fork()-Execute()?". What I had in
mind (based on someone else's (device driver?) code from long ago) was:

   - Have a procedure in your program which is just specialized code
     that waits for a(n extended) WorkbenchStartup message, takes the
     required info from the message, and calls Execute() with it.
   - then, find your SegList and follow down it until you find the
     address of said procedure.
   - use that as the SegList for a call to CreateProcess().
   - the child process will then Execute() the program with the
     parameters you gave it.
   - make sure your parent doesn't exit before the child does so that
     you can cleanup the child's resources and so that the pseudoforked
     section of the code doesn't get deallocated.

   You can separate the child code by detaching it from your
SegList if you linked it so it would be last on the main SegList
when loaded, and thus the parent program can exit without cleaning
up for the child or the child getting the rug pulled out from
under it, but you will lose the associated memory (nobody will
be around to clean it up) so you should only do it if you want
the code to stay around forever (for background demons and so
forth - in which case you should probably have been able to use
CreateProcess() directly instead of using Execute()).

If you don't mind having to wait for the child to finish eventually but
just want to do something else NOW, then this should do what you want.
And it's likely to be more robust than your current method. Your ugly hack :-)
:-) seems like the only way to spawn CLI processes and not have to worry
about them later though and would probably be what you'd have to do if you
were writing a shell for instance (actually I was wondering how to do that
so thanks for the hint - I hope I never need it).

--
    Paul-Andre_Panon@staff.ucs.ubc.ca        or    USERPAP1@UBCMTSG 
or  Paul-Andre_Panon@undergrad.cs.ubc.ca     or    USERPAP1@mtsg.ubc.ca
Looking for a .signature? "We've already got one. It is ver-ry ni-sce!"

jmeissen@oregon.oacis.org (John Meissen) (05/09/90)

In article <1860@dialog.sub.org> root@dialog.sub.org (Christian Motz) writes:
>As I said, this is something I cannot do, since  the  programs  to  be
>started are usually standard CLI programs, often to  the  effect  that
>they can't or shouldn't be modified. Arrgh! I want fork()!

Have you checked the manual? Both C compilers have something similar to
fork(). Manx, I believe, has forkexec(), and Lattice has forkl() and forkv().
The Lattice version wouldn't work for BCPL code (C= wouldn't give us any
help on how to setup a BCPL process, and Metacomco wouldn't talk to us), but
if you are fork'ing something you wrote then it's no problem. Of course,
John Toebes may have re-worked the Lattice version to use Execute(), so it
may work generically now.
-- 
 John Meissen ............................... Oregon Advanced Computing Institute
 jmeissen@oacis.org        (Internet) | "That's the remarkable thing about life;
 ..!sequent!oacis!jmeissen (UUCP)     |  things are never so bad that they can't
 jmeissen                  (BIX)      |  get worse." - Calvin & Hobbes

d87-khd@sm.luth.se (Karl-Gunnar Hultland) (05/09/90)

In article <5674@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
>
>ALL programs should be written to support being launched from either the
>Workbench or the CLI, so this translates to "this only works for non-buggy
>programs". Which is fine by me. The CLI startup environment is a crock
>anyway.

In my case the MAIN program supports being launched either from WB or CLI
which isn't very difficult 'cause I dont use stdio or cli-arguments, but
the sub-programs being "launched" from the MAIN (and only from the main)
shouldn't have to bother about WB or CLI. All I want to is to start them,
send them a message containing the address of the costomscreen I use,
and when they exit let the MAIN program do the unloading.


>
>And, of course, if you're writing the launchee as well as the launcher you can
>go wild...

In my case i could go wild, but since your launch package does,almost,
all the work I'll use it. THe irritating part is that I'll have to allocate
and send an extra message to the child, with the address to the screen.
But this is a minor nuisance with which I'll have to live.
Thanks anyway for you launch program.

					Karl
ps. That pic. in your .sig is that to be Australia?

---

Karl Hultland,(d87-khd@sm.luth.se)
University of Lulea,Sweden

Egoist: a person of low taste, more interested in himself than in me.
						- A. Bierce

peter@sugar.hackercorp.com (Peter da Silva) (05/10/90)

In article <908@tau.sm.luth.se> Karl-Gunnar Hultland <d87-khd@tau.luth.se> writes:
> In my case the MAIN program supports being launched either from WB or CLI
> which isn't very difficult 'cause I dont use stdio or cli-arguments, but
> the sub-programs being "launched" from the MAIN (and only from the main)
> shouldn't have to bother about WB or CLI. All I want to is to start them,
> send them a message containing the address of the costomscreen I use,
> and when they exit let the MAIN program do the unloading.

Sounds good. I'd recommend keeping all the WBstartup stuff anyway, and in
main() look for a magic word *beyond* the normal WBStartup message. That
way if the child programs get started up under another environment by
accident they won't kill the system.

> In my case i could go wild, but since your launch package does,almost,
> all the work I'll use it. THe irritating part is that I'll have to allocate
> and send an extra message to the child, with the address to the screen.

Just modify my launch program to send an extra two LONGs, one being a magic
word to indicate it's a "special environment" launch (like, if the first
word after WBStartup is "0xFEEDFACE"), and the other a pointer to your magic
stuff. One message, no problems.

> Thanks anyway for you launch program.

Welcome.

> ps. That pic. in your .sig is that to be Australia?

Yep, it be Oz.
-- 
 _--_|\  Peter da Silva <peter@sugar.hackercorp.com>.
/      \
\_.--._/ I haven't lost my mind, it's backed up on tape somewhere!
      v  "Have you hugged your wolf today?" `-_-'