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:
- V_Mailboxes.Bind_Mailbox
- V_Mailboxes.Resolve_Mailbox
- V_Mutexes.Bind_Cond
- V_Mutexes.Bind_Mutex
- V_Mutexes.Resolve_Cond
- V_Mutexes.Resolve_Mutex
- V_Names.Bind_Object
- V_Names.Bind_Procedure
- V_Names.Resolve_Object
- V_Names.Resolve_Procedure
- V_Semaphores.Bind_Semaphore
- V_Semaphores.Resolve_Semaphore
- V_Xtasking.Get_Program_Key
- V_Xtasking.Inter_Program_Call
- V_Xtasking.Set_Is_Server_Program
- V_Xtasking.Start_Program
- V_Xtasking.Terminate_Program
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:
Policy/Switches ------- USER_LINK_BLOCK: 080200000 Link.des -------------- OPTIONS MAP; ORIGIN ${USER_LINK_BLOCK}; -- updated to point to start of user program END OPTIONS; OBJECTS -- Board specific user configuration initialization. v_usr_conf; v_usr_data; v_usr_local; END OBJECTS; GROUP program_text IS text; END GROUP; FOR program_text USE AT *; GROUP program_const IS const; END GROUP; FOR program_const USE AT *; GROUP program_data IS data; END GROUP; GROUP program_data_image IS IMAGE program_data; FOR program_data USE AT *; FOR program_data_image USE AT *; GROUP program_bss IS bss; END GROUP; FOR program_bss USE AT *;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#:
Policy/Switches -------------------------- USER_LINK_BLOCK2: 080180000 Link.des --------------- OPTIONS MAP; ORIGIN ${USER_LINK_BLOCK2}; <------- changed for user_n program --- END OPTIONS;END OPTIONS; OBJECTS -- Board specific user configuration initialization. v_usr_conf; v_usr_data; v_usr_local; END OBJECTS; GROUP program_text IS text; END GROUP; FOR program_text USE AT *; GROUP program_const IS const; END GROUP; FOR program_const USE AT *; GROUP program_data IS data; END GROUP; GROUP program_data_image IS IMAGE program_data; FOR program_data USE AT *; FOR program_data_image USE AT *; GROUP program_bss IS bss; END GROUP; FOR program_bss USE AT *;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:
V_Xtasking.Start_Program( Link_Block_Address => System.Address'Ref(16#80180000#), Key => Arg'Address, Terminate_Callout => System.No_Addr);
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:
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:
procedure Bind_Object (Name : in String; Object_Address : in System.Address);
V_Names has an overloaded interface to the following service for resolving the name of an object into its address:
procedure Resolve_Object (Name : in String; Object_Address : out System.Address; Wait_Time : in Duration := Wait_Forever); function Resolve_Object (Name : in String; Wait_Time : in Duration := Wait_Forever) Return System.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:
user_1 program -------------- with V_Semaphores; use V_Semaphores; package body User_1_Pkg is Shared_Semaphore : Binary_Semaphore_Id; ... begin Shared_Semaphore := Create_Semaphore; Bind_Semaphore("Shared_Semaphore", Shared_Semaphore); end; user_2 program -------------- with V_Semaphores; use V_Semaphores; package body User_2_Pkg is Shared_Semaphore : Binary_Semaphore_Id; ... begin -- Will wait until User_1 does a Bind_Semaphore(). Shared_Semaphore := Resolve_Semaphore("Shared_Semaphore", Wait_Forever); end;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:
- Obtaining the address of the procedure
- The package containing the called procedure must be elaborated before the procedure is called
- Stack limit checking: each program has its own copy of the STACK_LIMIT variable
- Any Ada "new" allocations in the called procedure should come out the called program's heap area, not the calling task's program area. Likewise, any Ada unchecked_deallocations should be returned to the called program's heap area.
- Any Ada tasks created in the called procedure should have the called program as their parent, not the calling program.
- While in the called procedure, the calling task can be aborted by another task in the calling program
- Numeric, predefined and user-defined Ada exceptions may be raised in the called procedure
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:
procedure Bind_Procedure (Name : in String; Procedure_Address : in System.Address); procedure Bind_Procedure (name : in String; Procedure_Program : in System.Program_Id; Procedure_Address : in System.Address);
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:
procedure Resolve_Procedure (Name : in String; Procedure_Program : out System.Program_Id; Procedure_Address : out System.Address; Wait_Time : in Duration := Wait_Forever);
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:
procedure Inter_Program_Call (Procedure_Program : in System.Program_Id; Procedure_Address : in System.Address; Argument : in System.Address);
The Argument parameter is passed as the only argument to the called procedure. The called procedure has the following profile:
procedure Called_Procedure(Argument : System.Address);
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
user_1 program -------------- with System; use System; with V_Names; use V_Names; package body User_1_Pkg is procedure Shared_Proc(Arg : Address) is begin ... end Shared_Proc; ... begin -- Shared_Proc() can be called after this package completes -- its elaboration Bind_Procedure("Shared_Proc", Shared_Proc'Address); end User_1_Pkg; user_2 program -------------- with System; use System; with V_Names; use V_Names; with V_Xtasking; use V_Xtasking; package body User_2_Pkg is Shared_Proc_Prg : Program_Id; Shared_Proc_Addr : Address; procedure Call_It is Arg : Address := No_Addr; begin ... Inter_Program_Call(Shared_Proc_Prg, Shared_Proc_Addr, Arg); ... end Call_It; ... begin -- Will wait until user_1 does a Bind_Procedure(). Resolve_Procedure("Shared_Proc", Shared_Proc_Prg, Shared_Proc_Addr, Wait_Forever); end User_2_Pkg;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:
- It does not exit when there are no active tasks
- It is inhibited from being terminated
- However, it will automatically terminate when all the other non-server tasks have exited
- When its main program returns (at end of server's elaboration), the main task's stack is freed and its microkernel thread is stopped/freed.
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.adaThe widget_client.ss view contains these Ada files:
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.adaThe 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:
USER_LINK_BLOCK2: 080180000 LINKER_DESCRIPTION_FILE: <view>/Link.des
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:
% cd .../krn_conf.ss/<target_view> % apex_download -v v_krn_main % cd .../widget_server.ss/<target_view> % apex_download -v widget_server % cd .../widget_test.ss/<target_view> % apex_execute -v widget_test
If you forgot to modify V_Krn_Main to start the Widget_Server, you would get the following message when running widget_test:
** MAIN PROGRAM ABANDONED -- EXCEPTION "NAME_RESOLVE_TIMED_OUT" RAISEDDuring 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.1.ada --- ----------------------- with Widget_Status; use Widget_Status; with Widget_Types; package Widget is -- -- Interface to the WIDGET services -- subtype Widget_Id is Widget_Types.Widget_Id; No_Widget_Id : constant Widget_Id := Widget_Types.No_Widget_Id; -- Each service has two variations: one that raises an exception and -- another that returns status. function Create return Widget_Id; procedure Create (W : out Widget_Id; Status : out Status_T); procedure Delete (W : in out Widget_Id); procedure Delete (W : in out Widget_Id; Status : out Status_T); procedure Do_It (W : Widget_Id; In_Arg : Integer; Out_Arg : out Integer); procedure Do_It (W : Widget_Id; In_Arg : Integer; Out_Arg : out Integer; Status : out Status_T); end Widget; ----------------------- --- widget.2.ada --- ----------------------- with System; with System.Runtime; with V_Names; with V_Xtasking; with Widget_Ipi; use Widget_Ipi; with Widget_Status; use Widget_Status; with Widget_Types; package body Widget is -- Program_Id and Address of the Widget_Server's Dispatch procedure. -- Updated during elaboration by the call to V_Names.Resolve_Procedure. Service_Program_Id : System.Runtime.Program_Id; Service_Address : System.Address; -- Calls the corresponding service in the Widget_Server procedure Service (Params_Address : System.Address) is begin V_Xtasking.Inter_Program_Call (Procedure_Program => Service_Program_Id, Procedure_Address => Service_Address, Argument => Params_Address); end Service; pragma Inline_Only (Service); -- Each service has two variations: one that raises an exception and -- another that returns status. function Create return Widget_Id is W : Widget_Id; Status : Status_T; begin Create (W, Status); Check_Status (Status); -- if status /= OK, then, raise an exception return W; end Create; procedure Create (W : out Widget_Id; Status : out Status_T) is Params : Widget_Ipi.Params_T (Svc_Create); begin Service (Params'Address); -- do interprogram call W := Params.W; Status := Params.Status; end Create; procedure Delete (W : in out Widget_Id) is Status : Status_T; begin Delete (W, Status); Check_Status (Status); -- if status /= OK, then, raise an exception end Delete; procedure Delete (W : in out Widget_Id; Status : out Status_T) is Params : Widget_Ipi.Params_T (Svc_Delete); begin Params.W := W; Service (Params'Address); -- do interprogram call Status := Params.Status; W := No_Widget_Id; end Delete; procedure Do_It (W : Widget_Id; In_Arg : Integer; Out_Arg : out Integer) is Status : Status_T; begin Do_It (W, In_Arg, Out_Arg, Status); Check_Status (Status); -- if status /= OK, then, raise an exception end Do_It; procedure Do_It (W : Widget_Id; In_Arg : Integer; Out_Arg : out Integer; Status : out Status_T) is Params : Widget_Ipi.Params_T (Svc_Do_It); begin Params.W := W; Params.In_Arg := In_Arg; Service (Params'Address); -- do interprogram call Out_Arg := Params.Out_Arg; Status := Params.Status; end Do_It; begin -- Get the Program_Id and Address of the Widget_Server's Dispatch -- procedure. Give the Widget_Server at lease 10.0 seconds to -- complete its elaboration before it does a V_Names.Bind_Procedure(). -- -- If the Widget_Server program wasn't started, the exception, -- Name_Resolve_Timed_Out, will be raised after 10.0 seconds. V_Names.Resolve_Procedure (Name => Widget_Ipi.Service_Name, Procedure_Program => Service_Program_Id, Procedure_Address => Service_Address, Wait_Time => 10.0); end Widget; ----------------------- --- widget_ipi.1.ada --- ----------------------- with System; with Unchecked_Conversion; with Widget_Status; with Widget_Types; package Widget_Ipi is -- -- Interprogram interface between Widget clients and Widget server -- program -- -- Name bound to the Widget_Server's Dispatch procedure Service_Name : constant String := "widget"; -- Widget service id type Svc_Id_T is (Svc_Create, Svc_Delete, Svc_Do_It); -- variant record with the Widget service Id as the discriminant -- for passing parameters to the Dispatch procedure in the Widget_Server type Params_T (Svc_Id : Svc_Id_T) is record W : Widget_Types.Widget_Id; -- in parameter Status : Widget_Status.Status_T; -- out parameter case Svc_Id is when Svc_Create => null; when Svc_Delete => null; when Svc_Do_It => In_Arg : Integer; -- in parameter Out_Arg : Integer; -- out parameter end case; end record; type A_Params_T is access Params_T; function To_A_Params_T is new Unchecked_Conversion (System.Address, A_Params_T); end Widget_Ipi; ----------------------- --- widget_status.1.ada --- ----------------------- package Widget_Status is -- -- Exceptions and status codes for the Widget services -- Widget_No_Memory, Widget_Invalid_Operation, Widget_Bad_Arg, Widget_System_Error : exception; type Status_T is (Ok, No_Memory, Invalid_Operation, Bad_Arg, System_Error); -- -- Raise the exception corresponding to the specified status code. -- procedure Check_Status (Status : Status_T); pragma Inline_Only (Check_Status); end Widget_Status; ----------------------- --- widget_status.2.ada --- ----------------------- package body Widget_Status is procedure Raise_Error (Status : Status_T) is begin case Status is when Ok => null; when Invalid_Operation => raise Widget_Invalid_Operation; when No_Memory => raise Widget_No_Memory; when Bad_Arg => raise Widget_Bad_Arg; when System_Error => raise Widget_System_Error; end case; end Raise_Error; procedure Check_Status (Status : Status_T) is begin if Status /= Ok then Raise_Error (Status); end if; end Check_Status; end Widget_Status;
----------------------- --- widget_types.1.ada --- ----------------------- with System; package Widget_Types is -- -- Type definitions for the Widget services -- type Widget_Id is private; No_Widget_Id : constant Widget_Id; private type Widget_Id is new System.Address; No_Widget_Id : constant Widget_Id := Widget_Id (System.No_Addr); end Widget_Types;Widget_Test Main Procedure Ada File
----------------------- --- widget_test.2.ada --- ----------------------- with Widget; use Widget; with Widget_Status; with Simple_Io; use Simple_Io; procedure Widget_Test is W : Widget_Id; Out_Arg : Integer; begin -- -- Test program for calling the Widget services. The Widget services -- are linked in a separate server program started by the kernel. -- W := Create; Do_It (W, 10, Out_Arg); Do_It (W, 20, Out_Arg); Put ("last arg::"); Put (Out_Arg); New_Line; if Out_Arg /= 10 then Put_Line ("Error! Do_It returned bad out_arg."); end if; -- pass Do_It a bad argument begin Do_It (W, -1, Out_Arg); Put_Line ("Error! Widget_Bads_Arg exception wasn't raised."); exception when Widget_Status.Widget_Bad_Arg => Put_Line ("Got expected Widget_Bad_Arg exception."); end; Delete (W); -- Any operation on a deleted Widget is invalid begin Do_It (W, 10, Out_Arg); Put_Line ("Error! Widget_Invalid_Operation exception wasn't raised."); exception when Widget_Status.Widget_Invalid_Operation => Put_Line ("Got expected Widget_Invalid_Operation exception."); end; end Widget_Test;Widget_Server Ada Files
------------------------- --- widget_main.2.ada --- ------------------------- with Widget_Server; -- include the package containing the Widget services with V_Xtasking; procedure Widget_Main is begin -- -- Main procedure for the Widget_Server program -- -- Mark the Widget_Server as being a server program. This inhibits -- us from exiting until all the client programs exit. V_Xtasking.Set_Is_Server_Program; end Widget_Main; ------------------------- --- widget_server.1.ada --- ------------------------- package Widget_Server is -- The Widget services are in the body. They are made known to the -- world via V_Names.Bind_Procedure(); -- Ada95 requires that for there to be a body to this package, that -- the spec must require it. So we put a dummy procedure spec here. procedure Dummy; end Widget_Server; --------------------------- --- widget_server.2.ada --- --------------------------- with Widget_Ipi; use Widget_Ipi; -- shared with Widget client with Widget_Types; use Widget_Types; -- shared with Widget client with Widget_Status; use Widget_Status; -- shared with Widget client with V_Names; with V_Xtasking; with System; with Unchecked_Conversion; with Unchecked_Deallocation; package body Widget_Server is -- -- Widget services located in the server program. -- Magic_Id : constant := 16#1234_5678#; type Widget_T is record Magic : Integer; Last_Arg : Integer; end record; type A_Widget_T is access Widget_T; function To_A_Widget_T is new Unchecked_Conversion (Widget_Id, A_Widget_T); function To_Widget_Id is new Unchecked_Conversion (A_Widget_T, Widget_Id); -- Forward declarations to the Widget services called by Dispatch(). -- Unlike the Widget's client package specification, only the exception -- raising version of the services are implemented here. function Create return A_Widget_T; procedure Delete (W : A_Widget_T); procedure Do_It (W : A_Widget_T; In_Arg : Integer; Out_Arg : out Integer); -- During elaboration, the Widget's service name is bound to Dispatch -- via V_Names.Bind_Procedure. Dispatch is called from the client -- programs via V_Xtasking.Inter_Program_Call. Dispatch is passed the -- address of a variant record containing the service parameters. This -- variant record uses the Widget service ID as the discriminant. Both -- in and out parameters are passed in the record. -- -- Exceptions raised by the Widget service routines are mapped to their -- corresponding error code and returned to the client program via the -- status field in the params record. procedure Dispatch (Params_Address : System.Address) is Params : A_Params_T := To_A_Params_T (Params_Address); begin Params.Status := Ok; case Params.Svc_Id is when Svc_Create => Params.W := To_Widget_Id (Create); when Svc_Delete => Delete (To_A_Widget_T (Params.W)); when Svc_Do_It => Do_It (To_A_Widget_T (Params.W), Params.In_Arg, Params.Out_Arg); end case; exception when Widget_Invalid_Operation => Params.Status := Widget_Status.Invalid_Operation; when Widget_No_Memory => Params.Status := Widget_Status.No_Memory; when Widget_Bad_Arg => Params.Status := Widget_Status.Bad_Arg; when others => Params.Status := Widget_Status.System_Error; end Dispatch; function Create return A_Widget_T is W : A_Widget_T; begin -- Memory is allocated from the Widget_Server program and not the -- client program. W := new Widget_T'(Magic_Id, 0); return W; exception when Storage_Error => raise Widget_No_Memory; end Create; procedure Delete (W : A_Widget_T) is procedure Free is new Unchecked_Deallocation (Widget_T, A_Widget_T); My_W : A_Widget_T := W; begin if W = null or else W.Magic /= Magic_Id then raise Widget_Invalid_Operation; end if; My_W.Magic := 0; -- The freed Widget data structure is returned to the Widget_Server -- program and not the client program. Free (My_W); end Delete; procedure Do_It (W : A_Widget_T; In_Arg : Integer; Out_Arg : out Integer) is begin if W = null or else W.Magic /= Magic_Id then raise Widget_Invalid_Operation; end if; -- Since we were called via V_Xtasking.Inter_Program_Call, the -- client task and therefore this procedure is inhibited from -- being aborted. However, any tasks we create can be aborted. declare task A is -- task a's parent is the Widget_Server program and not the -- client program. entry E (I : Integer; O : out Integer); end A; task body A is begin accept E (I : Integer; O : out Integer) do if In_Arg < 0 then raise Widget_Bad_Arg; end if; O := W.Last_Arg; W.Last_Arg := I; end E; end A; begin A.E (In_Arg, Out_Arg); end; end Do_It; procedure Dummy is begin null; end Dummy; begin --Enabled the Widget services to be called by the client programs. V_Names.Bind_Procedure (Name => Widget_Ipi.Service_Name, Procedure_Address => Dispatch'Address); end Widget_Server;Modified V_Krn_Main Ada File
---------------------- --- v_krn_main.2.ada --- ---------------------- ------------------------------------------------------------------------ -- TASKING or NO TASKING KERNEL program's main procedure ------------------------------------------------------------------------ with V_Krn_Conf; -- kernel configuration with V_Reset; -- reset vector with V_Start; -- kernel entry point with V_Krn_Srv; -- kernel service table -- If you start server programs, then, uncomment the following lines with System; with V_Krn_Aug; -- kernel service augmentation -- With any board specific interrupt handler packages to be linked -- into kernel program procedure V_Krn_Main is pragma Suppress(All_Checks); pragma Suppress(Exception_Tables); begin -- Start the Widget_Server user_n program. -- declare Prg: V_Krn_Aug.A_Krn_Pcb_T; begin Prg := V_Krn_Aug.Program_Start( Usr_Link_Block_A => System.Address'Ref(16#8018_0000#), -- Usr_Link_Block_A must be set to the address -- of the widget server program's link_block. Key => System.Address'Ref(2), Terminate_Callout => System.No_Addr); end; end V_Krn_Main;
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |