|
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-2025 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.