Spawn new copy of program

This forum is for general developer support questions.
Post Reply
User avatar
mritter0
Posts: 214
Joined: Mon Aug 25, 2014 9:41 pm
Location: Bettendorf, IA, USA

Spawn new copy of program

Post by mritter0 »

Is it possible to spawn a totally new copy of a program from itself? Totally independent of the first copy.

IDOS->CreateNewProcTags(
NP_Entry, main,
TAG_END);

And can you pass command line args to the new copy?
Workbench Explorer - A better way to browse drawers
User avatar
colinw
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 207
Joined: Mon Aug 15, 2011 9:20 am
Location: Brisbane, QLD. Australia.

Re: Spawn new copy of program

Post by colinw »

mritter0 wrote: Is it possible to spawn a totally new copy of a program from itself? Totally independent of the first copy.
And can you pass command line args to the new copy?
Sure, you just effectively run youself and prepare the arguments. You do have to make sure that you are
not already being "run" or are already async from Workbench startup, but it's quite easy.
I'll attach some example source code to play with. (I'll just inline it here.)

Code: Select all


;/* Execute this to compile...
;
gcc run_detached.c  -nostdlib -o  RunDetached
strip -g --strip-unneeded  RunDetached -o RunDetached
quit
*/

/******************************************************************************/
/* "Run_Detached_Test"  Example code by Colin Wenzel. QLD, Australia.         */
/******************************************************************************/
  
#define __NOGLOBALIFACE__
#define __NOLIBBASE__

/******************************************************************************/

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

#include <dos/startup.h>

/******************************************************************************/

STATIC CONST USED TEXT sversion[]="\0$VER: Run_Detached_Test 53.02 (26.08.2012)\n\r";

/******************************************************************************/

#define NO  !
#define NOT !

/******************************************************************************/

struct globaldata 
{ 
	struct Library      *SysBase;
	struct Library      *DOSBase;
	struct Library      *UtilityBase;

	struct ExecIFace    *IExec;
	struct DOSIFace     *IDos;
	struct UtilityIFace *IUtil;

	struct CommandLineInterface *Cli;
	struct WBStartup *WBMsg;
};



/***************************************************************************/
/* Handy macros to reference the globaldata members */

#define DOSBASE   (gd->DOSBase)
#define SYSBASE   (gd->SysBase)
#define UTILBASE  (gd->UtilityBase)

#define IEXEC     (gd->IExec)
#define IDOS      (gd->IDos)
#define IUTILITY  (gd->IUtil)

#define CLI       (gd->Cli)
#define WBM       (gd->WBMsg)

/***************************************************************************/


static int32 async_main( struct globaldata *gd )
{

	/*
	** This code is now guaranteed to be running ASYNC
	** from either workbench or CLI.
	*/ 


	if( CLI )
	{
		IDOS->PutStr( "Hello from async Cli process.\n\n" );
		IDOS->Printf( "Supplied commandline; %s\n\n", IDOS->GetArgStr() );

		IDOS->PutStr( "\nProgram will exit in 10 seconds.\n" );

		IDOS->Delay(10*TICKS_PER_SECOND);
	}
	else
	{
		IDOS->TimedDosRequesterTags(TDR_Timeout, 10,
							TDR_FormatString, "Hello from async Workbench process.\n",
							TDR_GadgetString, "OK",
							TDR_ImageType, TDRIMAGE_INFO,
							TAG_END );
	}

	return(RETURN_OK);
}




/*****************************************************************************/



/* DOS entry function */

int32  _start(STRPTR args UNUSED, int32 arglen UNUSED, struct ExecBase *sysbase)  
{
	struct ExecIFace *iexec = (APTR)sysbase->MainInterface;
	struct globaldata *gd;
	struct Process *process;
	APTR   wbmsg = NULL;
	int32  rc = RETURN_FAIL;



	iexec->Obtain();
	process = (struct Process *) iexec->FindTask(NULL);
	
	if( NOT process->pr_CLI )  /* must get a workbench startup message first.*/ 
	{
		iexec->WaitPort(&process->pr_MsgPort);
		wbmsg = iexec->GetMsg(&process->pr_MsgPort);
	}

	gd = iexec->AllocVecTags( sizeof(*gd),
		                      AVT_Type,MEMF_SHARED,
		                      AVT_Lock,FALSE,
		                      AVT_ClearWithValue,0,
		                      TAG_END );
	do
	{
		if( NO gd )
		{
			break;
		}

		IEXEC   = iexec;
		SYSBASE = (APTR)sysbase;

		if( NOT (DOSBASE = IEXEC->OpenLibrary ("dos.library", 50) ))
		{
			break;
		}
		if( NOT (IDOS = (struct DOSIFace *)IEXEC->GetInterface(DOSBASE, "main", 1, NULL)))
		{
			break;
		}

		if( NOT (UTILBASE = IEXEC->OpenLibrary ("utility.library", 50) ))
		{
			break;
		}
		if( NOT (IUTILITY = (struct UtilityIFace *)IEXEC->GetInterface(UTILBASE, "main", 1, NULL) ))
		{
			break; 
		}

		CLI = IDOS->Cli();
		WBM = wbmsg;




		/*
		** If we are started from workbench, we are already async.
		** If we are started from the CLI "run" command, we're async too.
		** Only if we are started from CLI and not "run", we just run ourself.
		** The 'cli_Background' is TRUE if we were "run", which includes self running.
		** Check the CLI commandname BSTR as well, just for safety reasons.
		*/

		if(( CLI ) && ( NOT CLI->cli_Background ) && ( CLI->cli_CommandName )) 
		{
			TEXT   command[256+256];

			/*
			** Async output stream may simply be a "NIL:" spec (ZERO)
			** or something more exotic like a console window if you prefer.
			*/
			BPTR   stream = IDOS->Open("CON:20/30/600/180/StdOutput//",MODE_OLDFILE);
		//	BPTR   stream = ZERO;

			/*
			** We need to put our command name in double quotes
			** incase it contains a space, then append our supplied
			** arguments to that, after one space.
			*/
			IUTILITY->SNPrintf(command,sizeof(command),"\"%b\" %s", 
		                       CLI->cli_CommandName, IDOS->GetArgStr() );

			rc = IDOS->SystemTags(command, 
			                      SYS_Input,  stream,
			                      SYS_Output, ZERO,
			                      SYS_Error,  ZERO, 
			                      SYS_Asynch, TRUE,
			                      TAG_END );
			if( rc )
			{
				rc = RETURN_ERROR;   /* transform to a CLI error code. */
				IDOS->Printf("Unable to start async CLI process; %s\n",command);

				IDOS->Close(stream); /* ZERO is safe */
			}		
			else
			{
				IDOS->PutStr("Async CLI process started successfully. \n");
			}

			break;	/* we leave this command instance now */
		}

		rc =  async_main(gd);
	}
	while(0);

	if( gd )
	{
		IEXEC->DropInterface((APTR)IDOS);		/* NULL safe for these */
		IEXEC->DropInterface((APTR)IUTILITY);

		IEXEC->CloseLibrary(DOSBASE);
		IEXEC->CloseLibrary(UTILBASE);			/* NULL safe for these */

		IEXEC->FreeVec(gd);
	}

	if( wbmsg )               /* reply to any workbench startup message */ 
	{
		iexec->ReplyMsg( wbmsg );
	}

	iexec->Release();
	return(rc);
}


/*****************************************************************************/
/* EOF */
User avatar
nbache
Beta Tester
Beta Tester
Posts: 1714
Joined: Mon Dec 20, 2010 7:25 pm
Location: Copenhagen, Denmark
Contact:

Re: Spawn new copy of program

Post by nbache »

colinw wrote:You do have to make sure that you are not already being "run" or are already async from Workbench startup
Why?

Is there anything preventing one asynchronous process from spawning another asynchronous one?

I'm just curious, I don't know whether that is what mritter0 wanted in the first place.

Best regards,

Niels
xenic
Posts: 1185
Joined: Sun Jun 19, 2011 12:06 am

Re: Spawn new copy of program

Post by xenic »

nbache wrote: I'm just curious, I don't know whether that is what mritter0 wanted in the first place.
Yea. My impression was that he wants to run a program like an AmigaDOS 'Resident' command. The code Colin provided looks like an OS4 safe way to detach a program that was initially run synchronously. Maybe mritter0 should clarify exactly what he meant.
AmigaOne X1000 with 2GB memory - OS4.1 FE
User avatar
mritter0
Posts: 214
Joined: Mon Aug 25, 2014 9:41 pm
Location: Bettendorf, IA, USA

Re: Spawn new copy of program

Post by mritter0 »

@colinw

That is what I have been doing, using SystemTags() to launch a new copy, with optional command line args.

I was just curious if there was an "internal" way to do it. It is for Workbench Explorer, to open more windows for people who want multiple listers.

I was thinking maybe you can't enter at main(), or I am not giving it enough/correct info. I was thinking of splitting main() into 2 functions. main() do very little and pass the command line args to main2() which does the rest of the initial setup. Then the new process would start there.

CreateNewProc(
NP_Entry, main2,
NP_Arguments, "blah=doh QUIET",
TAG_DONE);

I want each process to be totally independent so the original copy can be closed at any time and not affect the others.
Workbench Explorer - A better way to browse drawers
User avatar
salass00
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 530
Joined: Sat Jun 18, 2011 3:12 pm
Location: Finland
Contact:

Re: Spawn new copy of program

Post by salass00 »

mritter0 wrote: I want each process to be totally independent so the original copy can be closed at any time and not affect the others.
The simplest way to achieve this is with SystemTags().

If you use CreateNewProc() with NP_Entry then the created process will be dependent on the seglist of the parent process and if the parent process exits before the child things will go badly.
User avatar
colinw
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 207
Joined: Mon Aug 15, 2011 9:20 am
Location: Brisbane, QLD. Australia.

Re: Spawn new copy of program

Post by colinw »

nbache wrote:
colinw wrote:You do have to make sure that you are not already being "run" or are already async from Workbench startup
Why?
Is there anything preventing one asynchronous process from spawning another asynchronous one?
I'm just curious, I don't know whether that is what mritter0 wanted in the first place.
Best regards,
Niels
The answer is outlined in the IDOS->RunCommand() autodoc, but i'll mention it here anyway.

The reason is because when you start anything from WB, it creates a distinct new process with CreateNewProc().
When you start a program from the shell with the "RUN" command, it creates a distinct new process with System().
When you start a program from the shell without "RUN", it does NOT create a new process but uses the existing
shell process to launched it via RunCommand(), the shell remains "busy" while the program runs on its process.

So, this code effectively checks to see if it's a WB or RUN launched program first, before detaching itself,
simply because it would be entirely pointless doing it twice, plus you would have to use some other
custom method, other than the standard system method to determine if you had already run yourself before.
User avatar
colinw
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 207
Joined: Mon Aug 15, 2011 9:20 am
Location: Brisbane, QLD. Australia.

Re: Spawn new copy of program

Post by colinw »

mritter0 wrote:@colinw
I want each process to be totally independent so the original copy can be closed at any time and not affect the others.
Then that code is what you need, all that's required is to substitute main() for _start() and change the arguments to suit.
Basically, just use the bit between "do{ ... }while(0); and remove the library opening and interfaces stuff at the top
above; CLI=IDOS->Cli(). Also, remove the macros / globaldata references and just use a local variable for "CLI".

Each copy will have it's own i/o streams and locks and not be attached in any way to other running copies.
User avatar
broadblues
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 600
Joined: Sat Jun 18, 2011 2:40 am
Location: Portsmouth, UK
Contact:

Re: Spawn new copy of program

Post by broadblues »

@mritter

If you go down that route, bear in mind that you can't have any globals, or you do they will need semapahore protection.

If you didn't design it that way from the get go, you might be making a rod to beat your own back.
User avatar
mritter0
Posts: 214
Joined: Mon Aug 25, 2014 9:41 pm
Location: Bettendorf, IA, USA

Re: Spawn new copy of program

Post by mritter0 »

I will just stick with what I have. Just checking out my options.

Thanks, guys.
Workbench Explorer - A better way to browse drawers
Post Reply