Newsletters   Subscriptions  Forums  Store   Career  Media Kit  About Us  Contact  Search   Home 
fhg
Volume 4, Number 38 -- November 10, 2004

Let Me Out of Here!

Hey, Ted:


Having read your article "Subprocedures: Better than Subroutines," I have been inspired to use subroutines less and subprocedures more in RPG programs. However, I have come up against a problem that I can't solve.

In many of my programs I include a clean-up subroutine that runs when the program ends, whether the program ends normally or abnormally. The subroutine includes various house-keeping tasks, sets on the LR indicator, and executes a RETURN operation. You probably know what happens when I implement such a routine in a subprocedure. Instead of returning to the calling program, control returns to the calling routine within the program.

How can I make my RPG program end from a subprocedure?

--Rick


As you've discovered, there is no RPG op code that ends a program. I suggest you replace the RETURN operation in the shutdown subprocedure to the C exit function. Here's a short example program to illustrate the use of the exit function.

H dftactgrp(*no) actgrp(*new)
H bnddir('QC2LE')

D SomeAmount      s              7p 2
D SomeTypeField   s              1a

D Quit            pr
D Exit            pr                  extproc('exit')
D                                3u 0 value

 /free
     if SomeAmount < *zero                                    ;
         Quit()                                               ;
     endif                                                    ;

           // ... some calcs omitted ...

     if SomeTypeField = 'X'                                   ;
         Quit()                                               ;
     endif                                                    ;

           // ... some more calcs omitted ...

     // Normal end of procedure
     Quit()                                                   ;
 /end-free
 * =========================================================  ;
P Quit            b
 /free
     // clean-up calcs go here
     *inlr = *on                                              ;
     exit(0)                                                  ;
 /end-free
P Quit            e

Here are a few things to notice.

  • You need to bind to the QC2LE binding directory in order for the compiler to find the exit routine.

  • Prototype the exit function, specifying one argument--a three-digit unsigned integer.

  • As far as I know, any value from zero to 255 is acceptable as the argument to exit because the system won't do anything with the value, anyway.

To make sure that I wasn't giving you bad advice, I asked Barbara Morris of IBM Toronto about your dilemma. She gave me quite a bit more useful information. Here it is, for the edification of us all.

--Ted


Calling exit( ) behaves the same as calling CEETREC. The behavior is complex, but basically what happens is that all contiguous call-stack invocations in the same activation group as the caller end. What it actually does depends on the activation groups of the entries in the call stack. Imagine the following call stack under two scenarios.


Procedure Scenario 1 Scenario 2
PGMmain AG1 AG1
proc1 AG1 AG1
proc2 AG1 AG2
proc3 AG1 AG1

Suppose proc3 issues the exit( ) or CEETREC( ). In scenario 1, in which the entire application is in same activation group, all the activations, including PGMmain, would end, as desired. In scenario 2, where proc2 is in a different activation group, only proc3 would end.

If you have a cancel handler enabled, one of the parameters to the cancel handler is the value specified on the exit( ) or CEETREC( ). The cancel handler would be enabled by the top-level program/procedure in the application. The cancel handler would do the cleanup. If it wanted to pass the value back to the caller of the application, it could have access to the parameters of the top-level procedure, and pass back the exit status in one of those parameters. Or it could send a message to its caller.

If you have the cancel handler do the cleanup, a good way to handle it is to have a cleanup procedure that is called both by normal end processing and from the cancel handler. The cleanup routine should keep track of what it has already done, in case it gets cancelled the first normal time and gets called again through cancel processing.

Sending an escape message to a particular invocation is more reliable. If proc3 sends an escape to PGMmain, then activation group AG1 would end in both scenarios. But having a cancel handler is still a good idea, because the application might get ended through, say, the subsystem being ended.

Below is some code for playing around with this. I used DSPLY with an exported variable to show whether the program really was ended, since non-exported variables are subject to RPG's LR processing. You could also use static variables in subprocedures to show whether things got cleaned up.

Create all the programs. Call EXITMAIN (uses exit), and answer some value to the DSPLYs. Call EXITMAIN again and notice the prompt values of the DSPLYs. Everything gets cleaned up.

Now change the source for EXIT1 to use AG2 instead of AG1. Repeat the double call and see how nothing really got cleaned up.

Do the same for EXITMMAIN, which uses an escape message from the subprogram and exit() at the top level to do the final cleanup. With everything being AGM1, everything gets cleaned up. With EXITM1 being AGM2, almost everything gets cleaned up, except the one program in AGM2.

1. Using exit:

 * pgm EXITMAIN
H actgrp('AG1')                                                
D msg             s              5a   export                  
C     'exitmain'    dsply                   msg                
C                   call      'EXIT1'                          
C     'exitmain aft'dsply    
C                   return                                    

 * pgm EXIT1
H actgrp('AG1')                                    
H*actgrp('AG2')
D msg             s              5a   export        
C     'exit1'       dsply                   msg    
C                   call      'EXIT2'              
C     'exit1 after' dsply    
C                   return                          

 * pgm EXIT2
H actgrp('AG1') bnddir('QC2LE')                          
D exit            pr                  extproc('exit')    
D   rc                          10i 0 value              
D msg             s              5a   export              
C     'exit2'       dsply                   msg          
C                   callp     exit(1)                    
C     'exit2 after' dsply    
C                   return                                

2. Using an escape message:

 * pgm EXITMMAIN
H actgrp('AGM1') bnddir('QC2LE')                        
D msg             s              5a   export            
D say             s             52a                    
D exit            pr                  extproc('exit')  
D   rc                          10i 0 value            
C     'exitmmain'   dsply                   msg        
C                   call(e)   'EXITM1'                  
C     'exitmmain af'dsply                              
C                   if        %error                    
C     'exitmmain er'dsply                              
C                   callp     exit(1)                  
C                   endif                              
C                   return                        

 * pgm EXITM1      
H actgrp('AGM1')                                  
H*actgrp('AGM2')
D msg             s              5a   export      
C     'exitm1'      dsply                   msg  
C                   call      'EXITM2'            
C     'exitm1 after'dsply                        
C                   return                        

 * pgm EXITM2
H actgrp('AGM1')
D msg             s              5a   export            
C     'exitm2'      dsply                   msg        
C                   call      'EXITM3'                  
C     'exitm2 after'dsply                              
C                   return                              

 * CLLE pgm EXITM3 (could be CLP, doesn't matter what AG)
      SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('escape +
                   message') TOPGMQ(*SAME ('EXITMMAIN')) +    
                   MSGTYPE(*ESCAPE)                            

--Barbara Morris

Sponsored By
PRODATA COMPUTER SVCS

iDBU with mySeries, do you?

We brought you DBU database utility.
And now...

DBUnifier for on-demand applications
RSP RPG Server Pages for web-enablement
STE Stored Procedure Test Environment
SQL/Pro query & reporting tools

All NEW ServerProven products which are
virtually FREE with IBM's rebate offering!
Hurry to WIN a safari (drawing Dec 31st)
www.prodatacomputer.com
Email sales@prodatacomputer.com
Call 800.228.6318


Technical Editors: Howard Arner, Joe Hertvik, Ted Holt,
Shannon O'Donnell, Kevin Vandever
Managing Editor: Shannon Pastore
Contributing Technical Editors: Joel Cochran, Wayne O. Evans, Raymond Everhart,
Bruce Guetzkow, Marc Logemann, David Morris
Publisher and Advertising Director: Jenny Thomas
Advertising Sales Representative: Kim Reed
Contact the Editors: To contact anyone on the IT Jungle Team
Go to our contacts page and send us a message.


THIS ISSUE
SPONSORED BY:

Linoma Software
ProData Computer Svcs
WorksRight Software


BACK ISSUES

TABLE OF
CONTENTS
Executing Dynamic Calculations with Embedded SQL

Let Me Out of Here!

More Conditional Sorting with SQL


The Four Hundred
i5 Model 595: Big Bang for Big Bucks

IBM's New Customer Design Center Focuses on High Availability

Gartner Releases IT and Business Trends Through 2010

Four Hundred Stuff
Unleash the Borg: OS/400 Gets Autonomic Tooling

TeamQuest Brings Capacity Planning Tool to OS/400 Server

No More Coding for EAI? DAM Right, Says Magic

Four Hundred Monitor


Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved.
Guild Companies, Inc. (formerly Midrange Server), 50 Park Terrace East, Suite 8F, New York, NY 10034
Privacy Statement