If you've been avoiding this technique, maybe you shouldn't be.
With the introduction of the ILE programming model, high-level languages (HLLs), including RPG, are enabled to make bound calls (procedure calls). This makes it possible for programs written in ILE RPG to cooperate with all the other HLLs available on the i5/OS, including Java, and to reuse all existing algorithms and functionalities that have been implemented in other HLLs. This fact is often regarded as proof of the RPG language's openness and modernization.
At the same time, the i5/OS machine interface (MI) layer exposed invocation interfaces to ILE programs, which are referred to as "bound program access interfaces" in IBM's MI documentation and as "system built-ins" in IBM's ILE RPG documentation. Now, programs written in RPG as well as in other i5/OS HLLs can interact with the operating system at the lowest level, the MI layer. This enables RPG programs to reuse the most efficient algorithms provided by the MI layer and to access system objects (MI objects) directly. This makes the RPG programming language more powerful.
Overview of MI Pointers
We know that the i5/OS is an object-based operating system. As the well-known UNIX idiom says, everything in a UNIX system is a file; on i5/OS, everything is an object. And the only way we can refer to an i5/OS object (MI object) or data in the object is through the MI pointer. For example:
- A system pointer (SYSPTR) addresses to an MI object. Each MI instruction that accesses a specific type of MI object expects a system pointer as a necessary operand to identify which MI object to deal with.
- A space pointer (SPCPTR) is used to address bytes within a space object. SPCPTR is the pointer type used in both MI and HLLs to address stack, static, and heap storage.
- A data pointer (DTAPTR) is a special type of space pointer that also stores the attributes, date type, and length of the data addressed by the pointer.
Also, MI pointers represent other objects in a program or in a process in run time, such as procedure pointers, instruction pointers, invocation pointers, and so on.
Then, how does one represent an MI pointer in an RPG program? Actually, the bound program access interfaces exposed to ILE HLLs by the MI layer do not distinguish different MI pointer types, and all 16-byte pointer types in ILE RPG and other ILE HLLs can be used in MI instruction invocations. Thus, all MI pointers can be represented by an RPG variable of pointer type (with data type field set to character '*' in position
Let's go through some of the most often used pointer operations.
Pointer Operations Supported by ILE RPG
As an HLL supporting pointers, ILE RPG provides the following support for pointer operations:
- Testing for NULL pointers. To test whether a pointer is a NULL pointer, compare it to the reserved word *NULL of the ILE RPG language. For example:
d p s * d pp s * procptr
/free
if p = *null and pp = *null; dsply 'null' ''; endif;
*inlr = *on; /end-free |
- Retrieving a space pointer that addresses the leftmost byte of an RPG variable by built-in %addr().
- Changing the offset of a space pointer. To change the offset of a space pointer, use operator +, -, +=, or -=. For example:
d buf ds qualified d fa d fb d fc
d ptr s * d var ds 4 based(ptr)
/free
ptr = %addr(buf.fb); // point ptr to buf.fb dsply 'var' '' var; // 'FLDB'
ptr = ptr + 4; // offset ptr forward 4 bytes dsply 'var' '' var; // 'FLDC'
ptr -= 8; // offset ptr backward 8 bytes dsply 'var' '' var; // 'FLDA'
*inlr = *on; /end-free |
- Copying a pointer. By using either operation code EVAL or operation code MOVE, an ILE RPG program can copy a pointer from one storage location to another, whether copying a pointer directly or copying a data structure containing a pointer. Note that it is not always the same when copying pointers at the MI layer. As we know, an i5/OS pointer is tagged protected, and not all storage copy MI instructions reserve the usability of a duplicated pointer.
- Retrieving a procedure pointer that addresses a procedure. To retrieve a procedure pointer that addresses a procedure, use ILE RPG built-in %paddr().
- Calling a procedure by a procedure pointer with operation code CALLB. Here is an example.
h dftactgrp(*no)
d increase pr d number 10i 0
d pptr s * procptr d n s 10i 0 inz(95)
* retrieve a procedure pointer c eval pptr = %paddr(increase)
* call procedure increase by PROCPTR c callb pptr c parm n
c 'result' dsply n c seton lr
p increase b
d increase pi d number 10i 0
/free number += 1; /end-free
p increase e |
Operating Pointers with MI Instructions
The prerequisite to invoke an MI instruction is to declare the right prototype for it. It is quite time-consuming to declare and validate prototypes for all the more than 200 MI instructions that have bound program access interfaces. System-builtin Headers for ILE RPG, a sub-project of the open-source project i5/OS Programmer's Toolkit, is working on this task. Prototypes of MI instructions mentioned in the following examples are extracted from one of the header files of the project, SourceForge's mih52.rpgleinc.
Determining the Type of a Pointer
To make sure that a pointer is of an expected type, use MI instruction Compare Pointer Type (CMPPTRT). To find out the type of a pointer, use MI instruction Materialize Pointer (MATPTR).
The prototype of MI instruction CMPPTRT provided by mih52.rpgleinc is shown as the following:
/* returns 1 if ptr is of specified type, otherwise 0. */ d cmpptrt pr 10i 0 extproc('_CMPPTRT') * expected pointer type d ptr_type * pointer to check d ptr * value |
In the following example ILE RPG program, CMPPTRT is used to test whether a given pointer is a system pointer.
/copy mih52
d ptr s * d r s 10i 0
/free
// locate a program object rslvsp_tmpl.obj_type = x'0201'; rslvsp_tmpl.obj_name = 'P03';
rslvsp2(ptr : rslvsp_tmpl);
if cmpptrt(x'01' : ptr) = 1; // ptr is a SYSPTR endif;
*inlr = *on; /end-free |
This is the prototype of MI instruction MATPTR.
/* MATPTR, materialize pointer attributes */ d matptr pr extproc('_MATPTR') d receiver * value d ptr * /* MATPTR template header */ d matptr_tmpl_t ds qualified d based(dummy_ptr) * bytes provided d bytes_in 10i 0 * bytes available d bytes_out 10i 0 * pointer type returned d ptr_type |
This example ILE RPG program uses MI instruction MATPTR to get the type of a pointer:
/copy mih52
d type s d ptr s * inz(%addr(type))
d tmpl_ptr s * d tmpl ds likeds(matptr_tmpl_t) d based(tmpl_ptr)
/free
// allocate storage for MATPTR template header tmpl_ptr = modasa(9); tmpl.bytes_in = 9;
matptr(tmpl_ptr : ptr);
// check ptr_type select; when tmpl.ptr_type = x'01'; type = 'System pointer'; when tmpl.ptr_type = x'02'; type = 'Space pointer'; // more pointer types when tmpl.ptr_type = x'FF'; type = 'Unsupported pointer'; other; // ... endsl;
dsply 'pointer type' '' type;
*inlr = *on; /end-free |
Retrieving Attributes of a Pointer
To retrieve attributes of a pointer, use MI instruction MATPTR. Here is an example of retrieving attributes of a procedure pointer (PROCPTR):
Attributes of a Procedure Pointer |
||
Offset |
Data Type |
Meaning |
0 |
UBin(4) |
Number of bytes provided for materialization |
4 |
UBin(4) |
Number of bytes available for materialization |
8 |
Char(1) |
Pointer type Hex 01 = System pointer Hex 02 = Space pointer Hex 03 = Data pointer Hex 04 = Instruction pointer Hex 05 = Invocation pointer Hex 06 = Procedure pointer Hex 07 = Label pointer Hex 08 = Suspend pointer Hex 09 = Synchronization pointer Hex FF = Unsupported pointer Note: For a PROCPTR, this field is always x'06'. |
9 |
Char(1) |
Pointer status Bit 0 = 1 if process object no longer exists. Bit 1 = 1 if pointer is from another process. Bit 2 = 1 if referenced program cannot be accessed. Bit 3 = 1 if containing process owns a shared activation group Bit 4-7, reserved. |
10 |
Char(6) |
Reserved |
16 |
UBin(4) |
Module number. Index in the module list of the bound program (*PGM or *SRVPGM) for the module whose activation the pointer addresses. |
20 |
UBin(4) |
Procedure number. Index in the procedure list of the module for the procedure addressed by the pointer. |
24 |
UBin(4) |
Activation mark. The activation mark of the activation that contains the activated procedure. Zero if the program activation no longer exists. |
28 |
UBin(4) |
An activation group mark of the activation group that contains the activated procedure. Zero if the program activation no longer exists. |
32 |
SYSPTR |
Containing program. A system pointer to the program object (*PGM or *SRVPGM) that contains the procedure. Null if the program activation no longer exists. |
48 |
SYSPTR |
Containing process. A system pointer to the process control space object (*PCS) that contains the procedure's activation group. A null pointer value is returned if the process control space object no longer exists or if it is no longer possible to determine the containing process for a destroyed activation group. |
64 |
Char(8) |
8-byte activation mark. |
72 |
Char(8) |
8-byte activation group mark. |
80 |
-- End -- |
|
The following shows the procedure pointer information structure declared in mih52.rpglinc.
/* length of PROCPTR information */ d matptr_procptr_info_length... d c 80
/* PROCPTR info structure */ d matptr_procptr_info_t... d ds qualified d based(dummy_ptr) d bytes_in 10i 0 d bytes_out 10i 0 d ptr_type d ptr_status d d mod_num 10u 0 d proc_num 10u 0 d act_mark 10u 0 d ag_mark 10u 0 d pgm * d process * d act_mark2 d ag_mark2 |
The following ILE RPG program T030 accepts a procedure pointer as its only input parameter and retrieves attributes of the given procedure pointer.
/** * @file t030.rpgle * * Materialize a PROCPTR */
/copy mih52
d i_main pr extpgm('T030') d pptr *
d info_ptr s * d info ds likeds(matptr_procptr_info_t) d based(info_ptr)
d sysptr_info_ptr s * d sysptr_info ds likeds(matptr_procptr_info_t) d based(info_ptr)
d i_main pi d pptr *
/free
// allocate storage for MATPTR template info_ptr = modasa(matptr_procptr_info_length); info.bytes_in = matptr_procptr_info_length;
// materialize input PROCPTR matptr(info_ptr : pptr);
// check returned PROCPTR attributes dsply 'module number' '' info.mod_num; dsply 'procedure number' '' info.proc_num;
sysptr_info_ptr = modasa(matptr_sysptr_info_length); sysptr_info.bytes_in = matptr_sysptr_info_length;
// retrieve containing program's name and library matptr(sysptr_info_ptr : info.pgm); dsply 'program name' '' sysptr_info.obj_name; dsply 'library name' '' sysptr_info.ctx_name;
// AG mark: info.ag_mark // ... ...
*inlr = *on; /end-free |
Another ILE RPG program, T029, calls program T030 twice, passing respectively a procedure pointer addressing procedure increase() in T029's program entry point (PEP) module and a procedure pointer addressing the C library routine printf().
/** * @file t029.rpgle * * test of matptr() * * call program T030. */
h dftactgrp(*no) h bnddir('QC2LE')
d t030 pr extpgm('T030') d ppp * procptr
d increase pr d num 10i 0
d ptr s * procptr
/free
ptr = %paddr(increase); t030(ptr);
// call T030 with procptr addressing // libc procedure printf() ptr = %paddr('printf'); t030(ptr);
*inlr = *on; /end-free
p increase b d increase pi d num 10i 0
c eval num = num + 1 c return
p increase e |
You can see that procedure increase() is the first procedure of the first module of program T029, and the C library routine printf() is the fifth procedure of service program QC2IO's thirteenth module.
DSPLY module number 1 *N DSPLY procedure number 1 *N DSPLY program name T029 *N DSPLY library name LSBIN *N DSPLY module number 13 *N DSPLY procedure number 5 *N DSPLY program name QC2IO *N DSPLY library name QSYS |
Managing i5/OS Objects by Using System Pointers
One can access an i5/OS object only by using a system pointer address to the object. A system pointer to an i5/OS object is somewhat like a ticket by which you are permitted to get on a train and find your seat. To invoke an object-related MI instruction on an i5/OS object, you must show such a ticket. Here are a couple of practical examples of accessing i5/OS objects by system pointers.
Exchange Data via a User Queue Object
A queue object (with object type code hex
DMPSYSOBJ OBJ(*PCS) /* the process control object of your current job */ TYPE( |
Here is an example of exchanging data via a user queue object. First, we create a user queue object by calling API QUSCRTUQ. Next, we put a queue entry onto the created user queue object by using MI instruction ENQ. Last, we dequeue a queue entry from the user queue object by using MI instruction DEQ.
This CL command creates a user queue with name QPROC:
CALL PGM(QUSCRTUQ) PARM( 'QPROC LSBIN' /* user queue name */ 'PROCPTR' /* extended attribute */ 'F' /* queue type: FIFO, first in first out */ X'00000000' /* key length = 0 */ X'00000010' /* message size = 16 */ X'00000020' /* initial number of messages = 32 */ X'00000020' /* number of messages of each extension = 32 */ '*CHANGE' /* public authority */ 'PROCPTR holder' /* text description */ '*YES' /* replace */ X'00000010000000000000000000000000' /* API error code */ '*DEFAULT' /* object domain */ '*YES' /* permit queue entries to contain pointers */ ) |
Here are the prototypes of MI instructions ENQ and DEQ:
/* message prefix used by instruction ENQ */ d enq_prefix_t ds qualified d msg_len 10i 0 * for keyed queue objects d msg_key
/* enqueue to a queue object */ d enq pr extproc('_ENQ') d queue * d msg_prefix * value d msg * value
/* message prefix used by instruction DEQ */ d deq_prefix_t ds qualified d deq_time d time_out d msg_len 10i 0 d state_flag * for keyed queue objects; input key, output key d msg_keys
/* dequeue from a queue object without waiting */ d deqi pr 10i 0 extproc('_DEQI') d msg_prefix * value d msg * value d queue *
/* dequeue from or wait on a queue object */ d deqwait pr extproc('_DEQWAIT') d msg_prefix * value d msg * value d queue * |
ILE RPG program T025 enqueues an entry into user queue QPROC:
/copy mih52
d q s * d prefix ds likeds(enq_prefix_t) /* * make sure the message text operand is aligned to * 16 bytes boundary when the target queue object * can contain pointers in queue entries. */ d text s
/free
// resolve target *USRQ QPROC rslvsp_tmpl.obj_type = x' rslvsp_tmpl.obj_name = 'QPROC'; rslvsp2(q : rslvsp_tmpl);
// enqueue *USRQ QPROC prefix.msg_len = 8; enq( q : %addr(prefix) : %addr(text) );
// use CL command DSPQMSG to check *USRQ QPROC // e.g. DSPQMSG QPROC *USRQ
*inlr = *on; /end-free |
ILE RPG program T026 dequeues an entry from user queue QPROC:
/copy mih52
d q s * d prefix ds likeds(deq_prefix_t) /* * make sure the message text operand is aligned to * 16 bytes boundary when the target queue object * can contain pointers in queue entries. */ d text s d rtn s 10i 0
/free
// resolve target *USRQ QPROC rslvsp_tmpl.obj_type = x' rslvsp_tmpl.obj_name = 'QPROC'; rslvsp2(q : rslvsp_tmpl);
// dequeue *USRQ QPROC prefix.msg_len = 16; rtn = deqi( %addr(prefix) : %addr(text) : q );
if rtn = 1; dsply 'Q entry' '' text; else; dsply 'nothing DEQed' ''; endif;
*inlr = *on; /end-free |
Get All User Profiles of Your i5/OS Installation
Everything on i5/OS is an object. A user profile object (*USRPRF) is of object type hex 08 and sub-type code 01. Theoretically, we can retrieve all objects residing in a library (a context object in the MI layer) with the MI instruction Materialize Context (MATCTX). Here is an example of retrieving *USRPRF objects by MI instruction MATCTX.
This is the prototype of one of MATCTX's bound access interfaces, _MATCTX1:
/* MATCTX option structure */ d matctx_option_t... d ds 46 qualified d sel_flag d sel_criteria... d d name_len 5i 0 selection name length d obj_type d obj_subtype... d d name d timestamp... d d asp_num
/* length of MATCTX option structure */ d matctx_option_length... d c 46
/* MATCTX template */ d matctx_receiver1_t... d ds qualified d bytes_in 10i 0 bytes provided d bytes_out 10i 0 bytes available d ctx_type d ctx_name d ctx_opt d rcvy_opt d spc_size 10i 0 space size d spc_init_val... d d perf_cls d d acc_grp * access group
d matctx_offset1... d c 96
/* MATCTX, materialize context */ d matctx1 pr extproc('_MATCTX1') d receiver * value d option |
ILE RPG program T031 lists all user profiles of an i5/OS installation:
/** * @file t031.rpgle * * test of MATCTX. * retrieve *USRPRFs from the machine context (of system ASP) */
/copy mih52
* SPCPTR to receiver d rcv_ptr s * d rcv_info ds likeds(matctx_receiver1_t) d based(rcv_ptr)
d obj_info ds qualified d based(rcv_ptr) d type d name
d option ds likeds(matctx_option_t) d BUF_LEN c x'010000' d num s 10i 0 d
/free
propb(%addr(option) : x'00' : matctx_option_length);
// receive object's symbol identifications option.sel_flag = x'05'; // select by object's type code option.sel_criteria = x'01'; // type code of a *USRPRF object is x'08' option.obj_type = x'08';
// allocate storage for receiver rcv_ptr = modasa(BUF_LEN); // 1Mb rcv_info.bytes_in = BUF_LEN;
// materialize the machine context for *USRPRFs matctx1 (rcv_ptr : option);
// display returned *USRPRFs num = (rcv_info.bytes_out - matctx_offset1) / %size(obj_info); rcv_ptr += matctx_offset1;
for dsply 'user profile' '' obj_info.name; rcv_ptr += %size(obj_info); endfor;
*inlr = *on; /end-free |
Conclusions
When talking about using MI instructions in RPG programs, some of us might argue that it will affect code consistency and require extra training and maintenance. In my opinion, it is just a matter of viewpoint. For example, if you like to use routines in other i5/OS HLLs, such as C in memory operations or mathematical calculations, you should be aware that memory operation MI instructions and mathematical MI instructions provide the same functionality as their C library counterparts but without dependency on the C library's service programs, such as QC2UTIL2, and will then shorten the time required by an ILE RPG program's activation progress. Or if you consider RPG as the only native language on the platform, then you should also know that, starting with S/38, the platform has been speaking in the MI language to all of us.
LATEST COMMENTS
MC Press Online