Overview:
This post
will illustrate a way for an application process to launch other applications
and then wait until these other applications have terminated before terminating
itself.
In this
way a "master" application can launch/start the other applications of
a configuration and, using the Is_Running function of the previous post, then
determine when they finish before terminating.
(Note: For the launching application to continue running while the
launched applications are running seems to have importance in Linux as was
pointed out by a number of internet posts that I found while searching for how
to launch an application from another application.)
Upon
succeeding in launching my previous three test applications (test1, test2, and
test3) from a new fourth test application (test4) I found that the result was
different than under Windows. Under
Windows when I launched three Exploratory Project (EP) DOS (that is, console)
applications via the Windows Display application, a new console window opened
for each one. Therefore, their console
output was displayed to their separate console windows and any of them could be
terminated via a ctrl-C in its particular window.
However,
under Linux nothing of that sort happens.
Instead all the console output goes to the terminal window from which
the launching application (that is, test4) was launched. Therefore, the console output from test4 and
that of the launched application processes (test1, test2 and test3) become
intermixed in the one terminal window.
I tried
to find a way to either create a new Linux terminal window as the new process
was spawned or to have it create a window for itself as it started. But I failed in this. I did find that GNAT has a terminals.c file
that implements functions declared in the file g-tty.ads that contains the
GNAT.TTY package. This package has an
Allocate_TTY procedure to create a new TTY.
I had hopes for this but it doesn't seem to create a terminal but,
instead, a special file that can be written to via normal file writes. This doesn't cause a terminal window to be
displayed. Since I already have a log
file and a Console Ada package that displays both to the terminal window and
writes the text to a particular disk file, this is of no help at all. And doesn't allow a person to force the
termination of the application from its terminal window.
Implementation:
The
ability to spawn another application process is supplied via the
Non_Blocking_Spawn procedure of the GNAT.OS_Lib package. (Note: This package is just another name for
the System OS_Lib package which contains the actual code.) Per Linux, as I understand it, this spawn of
another process creates the process and then swaps its use to that for the
provided application.
There
are two versions of spawn, blocking and non-blocking where blocking prevents
the invoking process from continuing until the spawned process terminates. Non_Blocking_Spawn takes two parameters; the
file path of the executable application to be launched and a list of
arguments. In my use, as illustrated in
the code below, I provided one null argument for the list since
the test applications don't input any operator parameters.
Then,
since in my internet searches the spawning process was to continue until the
spawned processes terminated, I used a loop checking via the Is_Running
function to determine when all the spawned processes were no longer
running. For the Exploratory Project
this behavior would need to be changed since the other applications have no
natural termination and need to be terminated by the application that spawned
them although, under Windows, they can be terminated by the operator via ctrl-C
in their console window.
test4.adb:
procedure Test4 is
subtype Remote_App_Range_Type
--|
Range of remote application processes to be launched
is
Apps.Application_Number_Type range 1..3;
type
Running_App_Type
is
array (Remote_App_Range_Type) of Boolean;
Running_App
--|
True if launched app is running
:
Running_App_Type := ( others => False );
type
Launched_Process_Id_Type
is
array (Remote_App_Range_Type) of GNAT.OS_Lib.Process_Id;
Launched_Process_Id
--|
Identifiers of the Launched processes to be taken over by the app
:
Launched_Process_Id_Type := ( others => GNAT.OS_Lib.Invalid_PId );
type
Launched_App_Name_Type
is
array (Remote_App_Range_Type) of String(1..35);
Launched_App_Name
--|
Application executable names including path
:
constant Launched_App_Name_Type
:= (
1 => "/home/clayton/Source/Test/test1.exe",
2 => "/home/clayton/Source/Test/test2.exe",
3 => "/home/clayton/Source/Test/test3.exe" );
Status
:
Exec_mC.Status_Type;
use
type GNAT.OS_Lib.Process_Id;
begin
Console.Initialize; -- output to the terminal and the log file
Exec.Text_Log.Open( App => 4 );
Console.Write( "Test4 started" );
Exec_mC.Initialize;
--
Spawn / launch the other three application processes via this one.
declare
Arg1
--|
Argument 1 of list
:
GNAT.OS_Lib.String_Access := new String(1..10);
Args
--|
List of arguments
:
GNAT.OS_Lib.Argument_List(1..1);
begin
Arg1.all := ( others => ' ' );
Args(1) := Arg1;
Launched_Process_Id(1) :=
GNAT.OS_Lib.Non_Blocking_Spawn
(
Program_Name => Launched_App_Name(1),
Args => Args );
Launched_Process_Id(2) :=
GNAT.OS_Lib.Non_Blocking_Spawn
(
Program_Name => Launched_App_Name(2),
Args => Args );
Launched_Process_Id(3) :=
GNAT.OS_Lib.Non_Blocking_Spawn
(
Program_Name => Launched_App_Name(3),
Args => Args );
end;
--|
Logic_Step:
--| Count number of launched
applications.
declare
Count
:
Integer;
Launched_Count
:
Integer;
begin
Count := 0;
Launched_Count := 0;
for
I in Remote_App_Range_Type loop
if Launched_Process_Id(I) /= GNAT.OS_Lib.Invalid_PId then
Running_App(I) := True; -- assume running
Launched_Count := Launched_Count + 1;
end if;
end loop;
--| Logic_Step:
--|
Wait for all the launched applications to finish.
delay 0.5; -- Allow launched applications to start
Forever:
loop
-- Wait for each launched app to finish running.
for I in Remote_App_Range_Type loop
if Running_App(I) then
if not Exec_Itf.Is_Running( Path => Launched_App_Name(I) ) then
Running_App(I) := False;
Count := Count + 1;
exit Forever when Count = Launched_Count;
end if;
end if;
end loop;
end
loop Forever;
end;
--|
Logic_Step:
--| Terminate.
Exec.Text_Log.Close;
Exec_Itf.Exit_to_OS;
end Test4;
No comments:
Post a Comment