23
Sat, Nov
1 New Articles

RPG's Persistent User Spaces

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

In order to make proper use of user spaces, you need to change your traditional approach to writing programs. Traditional RPG programmers are accustomed to minimizing the overhead of a program; we've spent a lot of time ensuring that a program utilizes only the memory it actually needs (e.g., how often have you wondered how many elements to assign to an array?).

But the "modernized" RPG programmer takes a different view of minimizing the overhead. Memory is cheap, and the system and programs are now so fast that there is little or nothing to be gained from the minimalist approach. Dare I say it, but it is a PC style of programming: load the data into memory, manipulate it, and save it. Think of how your favorite spreadsheet or word processor program works.

This approach is also reflected in how RPG has changed to accommodate larger field sizes: 256 to 64K for character fields and 999 to 32,767 for the number of elements in an array. And wait until you see what happens with these limits in V6R1!

As with most RPG programmers, I first came across user spaces when using the list APIs. I soon realized that user spaces also had uses in applications and could prove invaluable in a Web environment that was dependent on CGI programs.

Basically, using user spaces taught me that not everything needs to be database based and at times it is better to keep a lot of information in memory.

In this article, I offer a quick review of user spaces and explain how to create and manage them. I will also show how you can use user spaces to handle the problem of persistence in a CGI environment.

User Spaces

A user space is an object (object type *USRSPC) that consists of bytes, the format and content of which are up to you. More precisely (even though it might sound extremely vague), a user space is whatever you want it to be. A user space is "loaded" into memory and is accessed in a program by using pointers and based variables; it is simply a stream of bytes that you can access directly from within a program.

The nearest that traditional programming can come to this concept is a data area, but you have to input and output data areas to and from programs. In contrast, you can immediately change the content of a user space by merely changing the value of a field in your program.

Also, a user space will only be "loaded" in memory once, which means that multiple programs (whether in the same job or multiple jobs) will share the same instance of the user space. This means that multiple programs in different programs are sharing the same portion of memory. So, when program PGMA in job JOBA changes the value of a field, it is actually changing the value of a field in PGMB running in JOBB, without PGMB having to do any I/O operations. The fact that a user space may be common to programs in multiple jobs at the same time (without the programs having to perform any I/O) is a feature that proves very useful in CGI programming.

A user space has a maximum size of 16 megabytes. To create a user space or manipulate its contents, you must use one of the system-supplied APIs, and the only system-supplied command for user spaces is Delete User Space (DLTUSRSPC).

Although user spaces have been on the system for a long, long time, it was only with the advent of pointers in RPG IV that they became a lot easier to use and a lot more beneficial.

The Persistence Problem

One of the issues with CGI is that the connection between the browser and the called program is not persistent, i.e., a request is sent from a browser to a program in a CGI job, and the program returns an HTML document to the browser; the connection is now "broken," and the next request to the CGI job could come from a different browser.

On the same basis, when a request is sent from a browser to a program in a CGI job, the program returns an HTML document, and when the browser makes a subsequent request, there is no guarantee that the request will go to the same CGI job.

If there is any data that must be maintained for a browser, a program must have some way of storing it for each requesting browser session and ensuring that the data is available to all CGI jobs. Although this problem could be solved using database files, there would be the requirement for CGI programs to constantly read, write, update, and delete records. User spaces provide a more efficient means of managing "persistent" information without the need for any I/O.

Let's look at an example.

At System i Developer, there is a small application we use to maintain information for planning our RPG & DB2 Summit conferences. This application is Web-based and is written using CGIDEV2 (details available at easy400.net).

All of the maintenance programs use externalized database processing subprocedures for database maintenance. The basic concept is that the client programs (CGIDEV2 programs in this example) have no idea of the format of the actual database. All access to the data is through getters and setters. If you are not familiar with the concept of externalizing a database, you can get your hands on some sample code here. (These examples do not have the user space processing applied, but the following information should make it easy to implement the required changes if you wish).

Here's the basic format for external processing (speaker data in this example):

NewSpeaker(Speaker:Id);
GetSpeakerData(Id:SpeakerData);
SetSpeakerData(Id:SpeakerData);
ReleaseSpeaker(Id);

The important parameter in all of these subprocedures is the Id. The module containing the database subprocedures contains two arrays: an array of database records it is currently "handling" and an array of single-character fields used to identify the next available record element. The NewSpeaker subprocedure returns the index (Id) where the requested database record (Speaker is the key) is being stored. The Id is then used as a parameter to any Get or Set subprocedure to identify the record to be processed. The client program calls the Release subprocedure when it is finished processing the data.

In order to incorporate this design into a CGI environment, the two arrays (handles and database records) must be common (or available) to any CGI jobs that might end up processing a request from a browser. If the two arrays are placed in a user space, they are both "common" to programs in all CGI jobs that need to access them; all the programs need is the Id to identify which element to process. Therefore, all the browser page has to return is the Id, which is easily achieved using a hidden field in a form.

Creating and Managing User Spaces

Since a user space will be required for each table (physical file) in my application, I decided to write a subprocedure that creates a user space (if it does not already exist), loads the user space into memory, and returns a pointer with the location of the user space. This pointer may then be used as the basing pointer for a based variable. Figure 1 shows the prototype for a subprocedure named GetStorageSpace.

D GetStorageSpace...
D                 PR                  ExtProc('GetStorageSpace')
D  SpaceObj                     10a   Const
D  SpacePtr                       *
D  SetSize                      10i 0 Const
D                                     Options(*NoPass)      

 

Figure 1: This is the prototype for the GetStorageSpace subprocedure.

The GetStorageSpace subprocedure has three parameters:

  • SpaceObj is the name of the user space. In this example, I am using a predefined library to contain the user spaces.
  • SpacePtr is the pointer that indicates the location of the user space in memory. This pointer will be used as a basing pointer for one of the two arrays.
  • SetSize is an optional parameter that indicates the initial size of the user space (all user spaces are set so they will automatically extend).

The information that will be placed in the user space must be based on pointers. Figure 2 shows the definition of the two arrays (mentioned earlier) that will be mapped to the user space used for the Speaker database (Speakers is the record format on the Speaker table).

     D StoreRecord     DS                  LikeRec(Speakers)
     D                                     Dim(32767)
     D                                     Based(PtrStoreRecord)

     D Handles         S              1A   Dim(32767)
     D                                     Based(PtrHandles)

     D PtrStoreRecord  S               *
     D PtrHandles      S               *
     D HaveStorage     S               n

 

Figure 2: These based arrays will be mapped to the user space.

Figure 3 shows the definition of the CheckStorage subprocedure. This subprocedure calls the GetStorageSpace subprocedure, providing the name of the user space (STSPEAKERS) and the initial size (8M). The returned pointer (PtrHandles) is the basing pointer for the Handles array. The length of the Handles array is added to PtrHandles to calculate the value of the basing pointer (PtrStoreRecord) for the record array. This means that the first 32,767 bytes of the user space will contain the Handles array followed by the record array.

CheckStorage is executed as the first line in the NewSpeaker subprocedure.

     P CheckStorage    B
     D CheckStorage    PI
      /Free
        If Not HaveStorage;
           GetStorageSpace('STSPEAKERS' : PtrHandles : 8000000);
           PtrStoreRecord = PtrHandles + %Elem(Handles);
           HaveStorage = *On;
        EndIf;
      /End-Free
     P                 E

Figure 3: Set the basing pointers.

The User Space APIs

The GetStorageSpace subprocedure is going to make use of the Create User Space (QUSCRTUS), Change User Space Attributes (QUSCUSAT), and Retrieve Pointer to User Space (QUSPTRUS) APIs. Figure 4 shows the corresponding prototypes, along with the definition of the SpaceAttribute data structure, which is used as a parameter to the QUSCUSAT API:

D CreateUserSpace...
D                 PR                  ExtPgm('QUSCRTUS')
D  UserSpaceName                20A   Const
D  Attribute                    10A   Const
D  Size                         10I 0 Const
D  Initial                       1A   Const
D  Authority                    10A   Const
D  Text                         50A   Const
  // Optional Parameter Group 1
D  Replace                      10A   Const Options(*NOPASS)
D  ErrorCode                          Options(*NOPASS)
D                                     LikeDS(APIError)
  // Optional Parameter Group 2
D  Domain                       10A   Const Options(*NOPASS)
  // Optional Parameter Group 3
D  TransferSize                 10I 0 Const Options(*NOPASS)
D  OptimumAlign                  1A   Const Options(*NOPASS)


D ChangeUserSpaceAttributes...
D                 PR                  ExtPgm('QUSCUSAT')
D  ReturnLibrary                10A
D  UserSpaceName                20A   Const
D  Attribute                          Const
D                                     LikeDS(SpaceAttribute)
D  ErrorCode                          LikeDS(APIError)


D GetUserSpace    PR                  ExtPgm('QUSPTRUS')
D  UserSpaceName                20A   Const
D  pSpacePtr                      *
D  ErrorCode                          Options(*NOPASS)
D                                     LikeDS(APIError)

D SpaceAttribute  DS                  Qualified
D  NumberOfRecs                 10I 0
D  ExtendRecord                 12A
D   Key                         10I 0 Overlay(ExtendRecord)
D   Length                      10I 0 OverLay(ExtendRecord:*Next)
D   Extend                       1A   OverLay(ExtendRecord:*Next)

 

Figure 4: Here's the prototype for the QUSCRTUS, QUSCUSAT, and QUSPTRUS APIs.

The QUSCRTUS API consists of one mandatory parameter group and two optional parameter groups (remember, if one parameter in an optional parameter group is defined, then all parameters in the optional parameter group must be defined). These are the parameters for the QUSPTRUS API:

  • UserSpaceName is the name of the user space; the first 10 characters are the object name, and the last 10 are the library name. Remember that object names and library names should be uppercase.
  • Attribute can be any valid name (it is the object attribute).
  • Size is the size of the user space in bytes. This can range from 1 to 16,776,704.
  • Initial is the initialization value for all bytes in the user space. The API documentation recommends that this is set to X'00'.
  • Authority is the public authority for the object (*ALL, *CHANGE, *EXCLUDE, *LIBCRTAUT, *USE, or the name of an authorization list).
  • Text is the text description for the object.
  • Replace indicates whether or not (*YES or *NO, with *NO being the default) you should replace the user space, if it exists.
  • ErrorCode is the standard error data structure used for APIs.
  • Domain indicates whether the user space should be placed in the system or the user domain (*SYSTEM or *USER). The default value of *DEFAULT means that the system decides on the domain based on the value of the QALWUSRDMN system value.
  • TransferSize is the number of pages to be transferred between main storage and auxiliary storage; it may range from 0 to 32. The default value of 0 indicates that the system determines the transfer size.
  • OptimumAlign indicates whether or not (1 or 0, the default) optimum space alignment is performed for the user space, i.e., the user space is aligned in memory based on the size of a disk page.

These are the parameters for the QUSCUSAT API:

  • ReturnLibrary is the name of the library that contains the changed user space object. If the space attributes are successfully changed, the name of the library in which the user space was found is returned.
  • UserSpaceName is the name of the user space; the first 10 characters are the object name, and the last 10 are the library name.
  • Attribute is a special data structure (described next) containing details of which attributes are to be changed.
  • ErrorCode is the standard error data structure used for APIs.

There are four user space attributes that may be changed: the size of the user space, the initial value, automatic extendibility (the one we are interested in), and the transfer size request. The attribute data structure (SpaceAttribute in Figure 4) contains the following information:

  • NumberOfRecs indicates the number of attribute change request records contained in the data structure (1 to 4).
  • ExtendRecord contains variable-length records defining each attribute change request. In this example, we are only interested in making a user space automatically extendible, so ExtendRecord is re-mapped as Key (which will be initialized to 3 for automatically extendible), Length (which will be initialized to 1), and Extend (which will be initialized to '1').

These are the parameters for the QUSPTRUS API:

  • UserSpaceName is the name of the user space; the first 10 characters are the object name, and the last 10 are the library.
  • pSpacePtr will contain the address of the user space in memory.
  • ErrorCode is the standard error data structure used for APIs.

The GetStorageSpace Subprocedure

Figure 5 shows the GetStorageSpace subprocedure.

     P GetStorageSpace...
     P                 B                   Export
     D GetStorageSpace...
     D                 PI
     D  SpaceObj                     10a   Const
     D  SpacePtr                       *
     D  SetSize                      10i 0 Const
     D                                     Options(*NoPass)

     D FullSpaceName   S             20a
     D Library         S             10a
     D Size            S             10i 0 Inz(10000000)

      /Free
(1)     If %Parms() > 2;
           Size = SetSize;
        EndIf;
(2)     FullSpaceName = SpaceObj + 'SPACELIB';

(3)     GetUserSpace(FullSpaceName : SpacePtr: APIError);
(4)     If (APIError.BytesAvail > 0) And
           (APIError.MsgId = 'CPF9801');

(5)       CreateUserSpace( FullSpacename
                         : 'STORERECS'
                         : Size
                         : x'00'
                         : '*ALL'
                         : 'User Space for Record Storage'
                         : '*YES'
                         : APIError);
(6)       SpaceAttribute.NumberOfRecs = 1;
          SpaceAttribute.Key = 3;
          SpaceAttribute.Length = 1;
          SpaceAttribute.Extend = '1';
          ChangeUserSpaceAttributes( Library
                                   : FullSpaceName
                                   : SpaceAttribute
                                   : APIError);
(7)       GetUserSpace( FullSpacename
                      : SpacePtr
                      : APIError);
        EndIf;

        Return;
      /End-Free
     P                 E  

Figure 5: This is the GetStorageSpace subprocedure.

The main points to note are these (refer to the corresponding numbers in Figure 5):

  1. If a value is passed for the third parameter, it is used to set the initial size. Otherwise, the initial size defaults to 10M.
  2. The name of the library is appended to the name of the user space.
  3. The QUSPTRUS API is called to retrieve a pointer to the user space.
  4. The API error data structure is checked to see if the call to QUSPTRUS failed because the user space does not exist.

    The following points apply only if the user space does not exist.
     

  5. The QUSCRTUS API is called to create the user space.
  6. Values are set in the SpaceAttribute data structure to indicate that the user space is automatically extendible, and the QUSCUSAT is called to change the attribute of the user space.
  7. The QUSPTRUS is called again to retrieve the pointer to the newly created user space.

Some Other Thoughts

This example showed a user space being used to make database data persistent across multiple CGI jobs. But it could be any data. A user space could be used to store information from multiple pages as a user enters information (think of the process of booking a flight online). The information is stored in a data structure array, and each requesting browser is assigned a sequence number that corresponds to an element of the data structure array. The sequence number is written to the browser and is returned as a parameter, as with a hidden field on a display file format. When a program in a CGI job receives a request from a browser, it uses the returned sequence number to identify the element containing the required persistent data.

Be very careful about assigning sequence numbers. What happens if a user closes the browser and reopens it or returns to a starting page? You may want to consider separate subprocedures for assigning a sequence number and checking the validity of a sequence number when a request is received; assignment and validity would be based on how long it has been (and that length of time is up to you) since a request was processed for a specific sequence number, which would allow for re-use of sequence numbers.

Having the data structure array mapped to a user space can enhance the process in a number of ways. Firstly, you can exceed the 32,767 element maximum; assuming that the user space can physically store more than 32,767 elements, you can simply change the basing pointer of the data structure array to have it start in a different portion of the user space (or even use a single data structure and use the starting pointer, the size of the data structure, and the sequence number to calculate where in the user space the data structure should be mapped). Secondly, during development and testing, having the data in a user space provides a means of viewing the "persistent" data in the program without having to debug it. All you have to do is write a program to view the data in the user space.

Paul Tuohy

Paul Tuohy has worked in the development of IBM midrange applications since the 1970s. He has been IT manager for Kodak Ireland, Ltd., and technical director of Precision Software, Ltd., and is currently CEO of ComCon, a midrange consultancy company based in Dublin, Ireland. He has been teaching and lecturing since the mid-1980s. 

Paul is the author of Re-engineering RPG Legacy Applications, The Programmer's Guide to iSeries Navigator, and the self-teach course "iSeries Navigator for Programmers." He is one of the partners of System i Developer and, in addition to speaking at the renowned RPG & DB2 Summit, he is an award-winning speaker at COMMON and other conferences throughout the world.


MC Press books written by Paul Tuohy available now on the MC Press Bookstore.

The Programmer’s Guide to iSeries Navigator The Programmer’s Guide to iSeries Navigator
Get to know iSeries Navigator and all the powerful tools and interfaces that will expand your programming horizons.
List Price $74.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: