|
Despite the fact that few (no?) converted programs will be intentionally using "shared" storage, to show how storage is affected by different AGs, let's change AG04 to add a simple counter that keeps track of how many times the getMbrList function has been called. A real-world use might be for allowing the application to track it's own performance and add or remove servers based on those statistics. Along with the counter, let's add a function to read the count and one to reset it. The intent is specifically to allow a different count in different AGs. The changes to the /copy member AG04: * Retrieve current count/last time used d getStats pr d outCount 10i 0 d outLastTime z * Reset current count/last time used d resetStats pr The new functions for AG04 (the service program): d* Global variables d invokeCount s 10i 0 d invokeTimeStp s z ... bottom of getMbrList procedure c EndofPgm Tag c add 1 invokeCount c time invokeTimeStp c return p e * Retrieve current count/last time used p getStats b export d getStats pi d outCount 10i 0 d outLastTime z c eval outCount = invokeCount c eval outLastTime = invokeTimeStp p e * Reset current count/last time used p resetStats b export d resetStats pi c eval invokeCount = 0 c eval invokeTimeStp = *loval p e These procedures 'expose' the global variables to client programs in a controlled manner. Rather than EXPORT the variables so that clients can directly manipulate them, we have the client go through functions. Here is the complete binder language source AG04: STRPGMEXP PGMLVL(*CURRENT) SIGNATURE(' 1.01 15 Mar 02') EXPORT SYMBOL(getMbrList) EXPORT SYMBOL(getStats) EXPORT SYMBOL(resetStats) ENDPGMEXP STRPGMEXP PGMLVL(*PRV) SIGNATURE(' 1.00 08 Mar 02') EXPORT SYMBOL(getMbrList) ENDPGMEXP Let's add the variables to the display format AG05: A 3 28' Count' A DSPATR(UL) A 3 40'Last invoked ' A DSPATR(UL) A WCOUNT 10Y 0O 4 28TEXT('Use count') A EDTCDE(Z) A WTIME Z O 4 40TEXT('Use time stamp') And here are the changes to AG05 where the new functions are used. ... local copies d count s 10i 0 d timeStamp s z There is a bug in the way the EXFMT worked. There's now an EXFMT at the top of the loop with the subfile load at the bottom, with an 03 leave after the EXFMT. This way if the user specifies a new source file AND types in an option, the option will be processed before loading up the new page. ... c setoff 30 alarm * continue to display c dow *in03 = *off c exsr refreshWSStats c exfmt mbrctl c 03 leave ... c exsr loadSfl c enddo ... * refresh workstation copy of statistics c refreshWSStatsbegsr c callp getStats(count: timeStamp) c eval wcount = count c eval wtime = timeStamp c endsr So there's a new subroutine called refreshWSStats that refreshes the count/time stamp that are displayed on the workstation. Compile AG04 and AG05 as ACTGRP('QILE'). Running this version will increment the count and time stamp every time Enter is presses (that is, every time the load subfile routine is run.) If you want to see how activation group persistence works, call this in the morning, load a page and exit. Call it later in the day and see how it has kept the last time. Then do a RCLACTGRP QILE and call it again. See how reclaiming (destroying) the AG causes the system to initialise the static memory used by the program and service program when they're called again. One of the things I find annoying about the AG concept is that the AG is a property of the program/service program rather than a job property. That is, I can't set AG names on the fly - I need to compile my program. I can't even do a CHGPGM to set the AG - it must be a re-compile. I guess that's my hint that I should plan my AG strategy carefully, eh? Anyway, in order to be able to demonstrate that this static storage is segregated by AG, we need to run that service program in several different AGs so we can increment one but not the others. Sticking with the idea that I'll show named AGs first, let's write a couple of CL programs that use different names activation groups: AG05CLA - dftactgrp(*no) actgrp(AGA) AG05CLB - dftactgrp(*no) actgrp(AGB) pgm call ag05 endpgm Then re-compile AG05 and AG04 to use *CALLER. Call AG05CLA and increment the count a few times. AG05CLA sets up AG(AGA) and since AG05 runs in *CALLER, it too runs in AGA. When it starts up in AGA, it binds to service program AG04, which as *CALLER also initialises memory in AGA. Running AG05CLB does the same thing, but everybody runs in AGB - separate memory from AGA, so the counts are separate. If we want the external environment (the command line, a menu option, a CL program etc.) to reset that storage, we can do a RCLACTGRP AGA or AGB and the chosen AG is destroyed. What happens if we call AG05 directly from the command line now? Well, it works, and it uses storage culled from the default activation group. The downside is that we can't segregate the counts; all the calls from the command line use *DAG. We also can't reset those counts with RCLACTGRP because you can't destroy *DAG. Well, that's enough for the moment. Some things to think about: If the design didn't specifically call for segregation of the counts by AG, would this application require multiple AGs? How would you name a 'single' AG? How would you accomplish this segregation in an OPM application? Do ILE AGs make this easier? How would making AG05 *NEW change things? After you've chewed on this a bit, please send some feedback regarding where I can clarify things. The next step is to demonstrate *NEW/*CALLER. I'll have to create some new code for that, so it may be more sensible to actually take one of YOUR situations and work with that, as a real-world example rather than fabricate some test jig. Let me know! --buck
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].
Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.