TOC PREV NEXT INDEX DOC LIST MASTER INDEX



Multiple User Programs

The Apex embedded target environment consists of three separately linked programs: TDM, kernel and user. The Apex embedded target also supports the execution of additional separately linked user programs. Throughout this section, these additional user programs will be referred to as user_n programs.

Like the normal user program, each user_n program has a main task, elaboration table, exception table and configuration table. It has its own heap area for doing an Ada new or Unchecked_Deallocation. It can create, abort and complete program specific tasks. However, the tasks created in a user_n program are added to a single run queue shared by all programs. When a user_n program does not have any active tasks or tasks waiting at a delay or interrupt, it exits the same way the normal user program does. To summarize, a user_n program behaves identically to the user program.

VADS Exec has services for starting and terminating user_n programs. It provides name services allowing objects to be shared between programs. It also supports calling procedures in other programs.

Here is a list of VADS Exec services made available for multiple user programs:

The following subsections show how to use the above services. We conclude with a detailed example of a server program providing services for multiple client programs. First, however, we outline the steps to link, download and run a user_n program.

Linking

The kernel program implicitly starts the user program at the conclusion of its initialization via the user program's LINK_BLOCK. The LINK_BLOCK contains information needed by the kernel for executing the program, such as, its execution start address, address of its configuration table, address of its Ada exception handler, ...

When the kernel program is linked, the address of the user program's LINK_BLOCK is obtained via the USER_LINK_BLOCK policy switch.

A user_n program is dynamically started by calling the VADS Exec service, V_Xtasking.Start_Program, with the address of the user_n program's LINK_BLOCK.

Here's an example showing the USER_LINK_BLOCK Policy switch and linker description for the user program:

Before invoking Compile > Link or apex link to link a user_n program, make the following changes to the Policy/Switches and linker description file for the user program:

1 . Set the USER_LINK_BLOCK2 policy switch to point to the start of the user_n program.

2 . Update the LINKER_DESCRIPTION_FILE policy switch to be the linker description file for the user_n program.

3 . Set the ORIGIN in the OPTIONS section of the linker description file to ${USER_LINK_BLOCK2}.

Here's an example where the policy switches and linker description file were modified to link a user_n program located at the memory address, 16#8018_0000#:

Note: Above we use the example of using USER_LINK_BLOCK2. However, USER_LINK_BLOCK3 is also available. If you have more than three user programs, you will need to hard-code the address in the Linker description file rather than use a Policy switch.

The above user_n program could be started by making the following call:

It's most desirable to link the user_n programs below the user program. The kernel's V_Krn_Conf.Configuration_Table has the parameter, Heap_Stack_Bottom. By default, it's set to V_Krn_Conf_I.End_Of_User_Program. This special value causes the kernel to dynamically calculate the Heap_Stack_Bottom by iterating through the user program's group table to find the top of the user program. If you link user_n programs above the user program, Heap_Stack_Bottom must be changed to an absolute address above all the user_n programs. Otherwise, the user_n program will be located in the kernel's Heap_Stack area and zeroed out during kernel initialization.

Note: You must manually verify that none of the user_n programs overlap. Tools > Check Config is a very good tool to use for this.

Loading

The user_n programs can be put in ROM or downloaded like the kernel or user programs by using apex_download or File > Download. The user_n programs must be loaded before being started by calling V_Xtasking.Start_Program.

Debugging

The Apex debugger has the capability of debugging multiple user programs. The support is implemented using the lp (list programs) and program (select program) commands. Arguments to the load command also allow you to specify exactly which program is to be loaded.

The output from the lt (list tasks) and lb (list breakpoints) also reflect the multiprogramming support available in the debugger. For the lt output, tasks are grouped by program and preceded with a program title line. Similarly, for the lb output, breakpoints are grouped by program and preceded with a program title line.

Before setting breakpoints, you should position yourself in the program of interest. That is, use the program command or other commands that change the current position.

All multiprogram debugger support is controlled by the APEX_MULTIPROGRAM session switch (shell environment variable). Support is only enabled when this session switch is set to True.


Warning: In this mode, the debugger will attempt to load Diana information from the views in which the successive programs are located.

The only restriction is that all programs being debugged are built in libraries with no conflicting units. That is, the set of units in each library is disjoint from the set of units in all the other libraries.

Starting, Exiting and Terminating

A user_n program can be started from the kernel program, the user program or another user_n program.

There are a limited number of services that can be called from packages and procedures linked into the kernel program. The interface to these services is provided in the V_Krn_Aug package found in krn_conf.ss/<target_view>/v_krn_aug.1.ada. A user_n program is started by calling V_Krn_Aug.Program_Start(). A good place to put a call to V_Krn_Aug.Program_Start() is in the V_Krn_Main procedure. There's already an example in V_Krn_Main for starting the EneT server program.

The VADS Exec service, V_Xtasking.Start_Program can be called to start a user_n program from the user program or another user_n program.

The V_Krn_Aug.Program_Start and V_Xtasking.Start_Program services have the following input parameters:

link_block_address
is the address of the to be started program's LINK_BLOCK. Normally, this is the origin of the program.
key
is a user defined value stored in the new program's control block. This key is passed to the Program_Switch_Event callouts. The key may be obtained by routines in the new program via the V_Xtasking.Get_Program_Key service. One use for the key is to have it point to a list of program arguments. The value for the main program's key is System.Address'Ref(0). The kernel program has a predefined key with a value of System.Address'Ref(1).
terminate_callout
is the address of the procedure to be called when the program exits or is terminated. A value of NO_ADDR indicates no callout. The callout procedure is called as follows:

If the user_n program was successfully started, V_Krn_Aug.Program_Start returns a pointer to its kernel program control block, V_Krn_Aug.A_Krn_Pcb_T (the VADS Exec V_Xtasking.Os_Program_Id is another type for this pointer). Otherwise, it returns Null.

For success, V_Xtasking.Start_Program returns its Ada program_id. Otherwise, it returns No_Program_Id.

A user_n program exits under the same conditions as the normal user program: it does not have any active tasks, tasks waiting at a delay or tasks waiting for an interrupt. When the user_n program exits, if a Terminate_Callout was specified when the program was started, then it's called.

A user_n program can be terminated at any time by calling the V_Xtasking.Terminate_Program service. A program can terminate itself or another program.

Sharing Objects Between Programs

Name services have been added to VADS Exec to support the sharing of objects between programs. A name string can be bound to the address of any object. Another program can obtain the object's address by simply giving the object's name. These name services are provided by the V_Names package in VADS Exec.

V_Names has the following service to bind a name to the address of an object:

V_Names has an overloaded interface to the following service for resolving the name of an object into its address:

The Resolve_Object service has a wait_time parameter. If the name has not already been bound to the object, the task will wait until it is. By forcing the task to wait allows the program containing the object to complete any elaboration or initialization associated with the object before it can be accessed by another program. The wait_time parameter can be set to any duration or the predefined values of Wait_Forever or Do_Not_Wait.

Name services are also available for the V_Semaphores, V_Mutexes and V_Mailboxes packages in VADS Exec. After an object is created, a name can be bound to it. Another program is able to use the object by doing a name resolve operation instead of a create operation.

Two services are available in V_Semaphores, V_Mutexes and V_Mailboxes: Bind_object and Resolve_object, where object is Semaphore, Mutex, Cond or Mailbox. They have the same parameters as their V_Names counterparts, except the object address is replaced with the object ID.

Here's an example showing how a semaphore can be shared between the user_1 and user_2 programs:

Calling Procedures In Other Programs

It may be desirable to have procedures that can be called from tasks in other programs. However, there are a number of complicating factors in doing an interprogram call:

All of the above issues have been addressed by the VADS Exec services which support interprogram calls.

In addition to object sharing, the V_Names services support the sharing of procedures between programs. A name string can be bound to the Program_Id and address of a procedure. Another program can obtain the procedure's Program_Id and address by simply giving the procedure's name.

V_Names has an overloaded interface to the following service to bind a name to the Program_Id and address of a procedure:

If the procedure_program parameter is omitted, it defaults to the current program.

V_Names has the following service for resolving the name of a procedure into its Program_Id and address:

The Resolve_Procedure service has a Wait_Time parameter. If the name has not already been bound to the procedure, the task will wait until it is. By forcing the task to wait allows the program containing the procedure to complete any elaboration or initialization associated with the procedure before it can be called from another program. The Wait_Time parameter can be set to any duration or the predefined values of Wait_Forever or Do_Not_Wait.

Once you have the Program_Id and address of the procedure it can be called using the VADS Exec service, V_Xtasking.Inter_Program_Call. The Inter_Program_Call service has the following interface:

The Argument parameter is passed as the only argument to the called procedure. The called procedure has the following profile:

During the call, the current, calling task is disabled from being completed and terminated by an Ada abort.

Before doing the call, the current program is switched. Also, the stack limit in the program containing the called procedure is switched. Upon return, everything is restored.

Note: The Program_Switch_Event callouts are not called. The task's parent program is not switched. The Program_Switch_Event callouts are only called when the parent program switches (i.e. when we switch to another task that is in another parent program).

Any Ada new allocations or task creates will be done in the context of the program containing the called procedure and not in the calling task's parent program.

Ada exceptions can be raised and handled in the called procedure. However, Inter_Program_Call does not handle the propagation of Ada exceptions across inter-program calls. Therefore, the called procedure must have a handler for all possible Ada exceptions. An Ada exception raised in the called procedure can have an outer handler that maps the exception to error status returned to the calling program. The calling program can then decode the error status and reraise the Ada exception.

Here's an example showing how a procedure in the user_1 program can be called from the user_2 program:

Multiple User Programs - Procedure Calling Example

Server Program

You might want to start a program containing shared objects or having procedures called from other programs. If the program does not have many tasks, it exits upon completing elaboration. To prevent this, you can add a dummy task that iteratively delays or calls the VADS Exec service V_Xtasking.Set_Exit_Disabled.

Instead of having to make the program appear active, we added the VADS Exec service V_Xtasking.Set_Is_Server_Program. This service marks the current program as being a server. A server program has the following attributes:

Server Program Example

Here is a detailed example showing how services provided in a server program can be called from client programs.

In this example, the server will provide the following Widget services: Create, Delete, and Do_It. The interface to these services in provided in the Widget package specification Each service has two variations: one raising an Ada exception for an error, the other returning error status. The Widget package depends on the following packages:

Widget_Types
Type definition for Widget_Id
Widget_Status
List of Widget exceptions and return status codes

Figure 9 contains a diagram depicting the interprogram calls for the Widget services.

Figure 9 Interprogram Calls for the Widget Services

The Widget package body calls V_Names.Resolve_Procedure to get the address of the Widget's server procedure. One server procedure handles all the Widget service requests. It is passed the address of a variant record containing the service parameters. The variant record is discriminated by the service ID. This variant record is defined in the Widget_Ipi package specification (where, IPI is the InterProgram Interface). Each service updates its variant version of the params record and does a V_Xtasking.Inter_Program_Call to the Widget's server by passing the address of the params record. (In the example this is done by the inlined Service() procedure.) Upon return, it extracts "out" parameters from the params record. The exception version of the service checks the return status field in the params record and raises the corresponding exception if the status is not OK.

All of the above packages are located in the widget_common.ss and widget_client.ss views. The widget_common.ss view contains the following Ada files:

widget_ipi.1.ada
widget_status.1.ada
widget_status.2.ada
widget_types.1.ada

The widget_client.ss view contains these Ada files:

widget.1.ada
widget.2.ada

The source to these files is located in Widget_Client Ada Files.

An application accesses the Widget services by importing the widget_client.ss view and with'ing the Widget and Widget_Status packages. The application is compiled and linked like any normal user or user_n program. See Widget_Test Main Procedure Ada File for the source of a Widget_Test procedure calling the Widget services.

The Widget services are implemented in the Widget_Server program. The above widget_client.ss view provides the interface used by the client programs. The Widget_Server has a single Dispatch procedure called via V_Xtasking.Inter_Program_Call(). The Widget_Server's name is bound to the Dispatch procedure by V_Names.Bind_Procedure() during elaboration.

The Dispatch procedure is called with one argument, the address of a variant record discriminated by the service ID and containing the service specific parameters. The Dispatch procedure cases according to the service ID and calls the appropriate service procedure. The Dispatch procedure has exception handlers for all possible exceptions that can be raised. Since Ada exceptions are not propagated across interprogram calls, each exception is mapped to its corresponding status code and updated in the params record passed back to the client where it can be re-raised.

The Widget Dispatch procedure and its services are located in the Widget_Server package body. The Widget_Server package specification is empty. It is with'ed by the Widget_Server's main procedure, Widget_Main. Widget_Maincalls V_Xtasking.Set_Is Server_Program. Since the Widget_Server has no active tasks, Set_Is_Server_Program is called to inhibit the Widget_Server from exiting. Also, Set_Is_Server_Program enables Widget_Server to be implicitly terminated when all the client programs exit.

The Widget_Server program is compiled and linked in the widget_server.ss view. This view contains the following Ada files:

widget_main.1.ada
widget_server.1.ada
widget_server.2.ada

The source to these files is provided in Widget_Server Ada Files.

Since the Widget_Server shares the Widget_IPI, Widget_Status and Widget_Types packages with the Widget clients, the widget_common.ss view needs to be imported.

The Widget_Server is linked like any user_n program. The policy switches need to be set as follows:

Where 080180000 is the memory address to locate the program. Normally, the Widget_Server would be located below the user program.

The Link.des file is created from usr_conf.ss/<target_view>/Link.des by changing the ORIGIN to ${USER_LINK_BLOCK2}.

The Widget_Server program is started by modifying krn_conf.ss/<target_view>/v_krn_main.2.ada to call V_Krn_Aug.Start_Program.

The following commands would need to be entered to run the above Widget example:

If you forgot to modify V_Krn_Main to start the Widget_Server, you would get the following message when running widget_test:

During early development it may be desirable to link, run and debug the server and clients as a single program. Additionally, you might want to prototype the services using a Apex native compiler and runtime system. This could be done for the above Widget example by changing the Widget_Test procedure to also with the Widget_Server package and importing widget_server view. Note, if you with'ed Widget_Main, then, since it calls V_Xtasking.Set_Is_Server_Program, the program would exit immediately.

Widget_Client Ada Files

This section contains the source code to the widget* files used in this example.

Widget_Test Main Procedure Ada File

Widget_Server Ada Files

Modified V_Krn_Main Ada File


Rational Software Corporation 
http://www.rational.com
support@rational.com
techpubs@rational.com
Copyright © 1993-2002, Rational Software Corporation. All rights reserved.
TOC PREV NEXT INDEX DOC LIST MASTER INDEX TECHNOTES APEX TIPS