× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



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

Follow-Ups:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

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.