23
Sat, Nov
1 New Articles

The CL Corner: Incrementing a Numeric Value Across Jobs

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

You probably won't need Compare and Swap (CMPSWP) often, but when you do, you'll appreciate its high level of performance.

 

There are some types of data, an invoice number for instance, that have characteristics such that the "next" value...

•     needs to be generated concurrently by several jobs,

•     needs to be unique within the application,

•     and is generally sequential in nature.

 

This type of data requirement historically has been satisfied with approaches such as using a data area (*DTAARA) to store and share the next available invoice number across the users of the application. While *DTAARAs work fine for this type of application, other options can provide this level of function at a much higher level of performance. One such option is the Compare and Swap (CMPSWP) Machine Interface (MI) instruction.

 

The CMPSWP instruction is shown below and documented here.

 

CMPSWP(

            Op1               :address of a scalar (1,2,4,8) value

            Op2               :address of a scalar (1,2,4,8) value

            Op3               :scalar (1,2,4,8) value

            Cntl              :signed binary(4) literal value (optional)

      ) : signed binary(4)

 

This instruction requires three parameters, has one optional parameter, and returns a return value. We will be looking at the three required parameters and the return value today. The first three parameters can be defined as being 1, 2, 4, or 8 bytes in length, and all three must be of the same size. We will be using 4-byte, unsigned integer values today as this would accommodate an invoice number ranging from 0 to 4294967295. The return value is a 4-byte, signed integer value and will be discussed shortly.

 

When the CMPSWP instruction runs, the value of the first parameter (Op1) is compared to the value of the second parameter (Op2). If they are equal, the value of the third parameter (Op3) is copied (swapped) to the second parameter and the instruction returns a return value of 1. If they are not equal, the value of the second parameter is copied to the first parameter and the instruction returns a return value of 0. Further, the instruction assures that no other CMPSWP instruction on the system can access the value of the second parameter when parameters one and two do compare as equal.

 

Whew!  Let's see what this means by way of an example.

 

Let's say we have a user space (*USRSPC) QGPL/HOLDNBR, and within the *USRSPC, HOLDNBR is stored as an unsigned integer value representing the last invoice number used. The *USRSPC could be created like this:

 

EDTF STMF('/qsys.lib/qgpl.lib/holdnbr.usrspc')

 

After using F3 to Save/Exit from the EDTF panel (and not typing anything into the panel), we will have a *USRSPC of minimum size initialized to x'00's. This is convenient as x'00's will be treated as if the last invoice number used was 0. With this *USRSPC created, here is the ILE CL program GETNBRCL that can be used to determine the next invoice number:

 

 

     Pgm                                                     

     Dcl        Var(&NbrPtr)    Type(*Ptr)                  

     Dcl        Var(&FndValue)  Type(*UInt)                 

     Dcl        Var(&NewValue)  Type(*UInt)                 

     Dcl        Var(&CurValue)  Type(*UInt) Stg(*Based) +   

                  BasPtr(&NbrPtr)                           

     Dcl        Var(&RtnCde)    Type(*Int)                  

     Dcl        Var(&NewValChr) Type(*Char) Len(10)         

     Dcl        Var(&SpcName)   Type(*Char) Len(20) +       

                  Value('HOLDNBR   QGPL      ')             

(A)  Call       Pgm(QUSPTRUS) Parm(&SpcName &NbrPtr)        

(B)  ChgVar     Var(&FndValue) Value(&CurValue)             

     DoUntil    Cond(&RtnCde = 1)                            

                ChgVar Var(&NewValue) Value(&FndValue + 1)  

(C)             CallPrc Prc('_CMPSWP') Parm((&FndValue) +   

                  (&CurValue) (&NewValue *ByVal)) +         

                  RtnVal(&RtnCde)                   

     EndDo                                          

     ChgVar     Var(&NewValChr) Value(&NewValue)    

(D)  SndPgmMsg  Msg(&NewValChr) ToPgmQ(*Ext)        

     EndPgm

 

 

The variable &NbrPtr is a space pointer that is used to access the *USRSPC directly. This pointer is set by calling the Retrieve Pointer to User Space (QUSPTRUS) API at (A). The variables &FndValue, &NewValue, and &CurValue are 4-byte, unsigned integers representing the invoice number found in the *USRSPC, the next invoice number that could be used, and the current value in the *USRSPC, respectively. Note that the variable &CurValue is Based on the pointer variable &NbrPtr.

 

After setting &FndValue to the value of &CurValue at (B), GETNBRCL falls into a DoUntil loop where &NewValue is set by adding 1 to &FndValue. We then run the CMPSWP instruction. On the initial entry into the DoUntil loop, &FndValue will be 0 (from the newly created *USRSPC) and &NewValue set to 1. The CMPSWP instruction will compare &FndValue to &CurValue and, if they are equal (both are 0), then set &CurValue to 1 (the value of &NewValue) and return a return value of 1. This return value indicates that no other job on the system needed an invoice number between steps (B) and (C). The DoUntil loop is then exited with &NewValue holding the invoice number that should be used. This value is displayed at (D).

 

If the comparison between &FndValue and &CurValue is not equal (for example, two users needed invoice numbers between the running of (B) and (C), leaving &CurValue set to 2), then CMPSWP will set &FndValue to 2, leave &CurValue alone, and return a return value of 0. The DoUntil loop will then continue, setting &NewValue to 3 (a &FndValue of 2 plus 1) and using CMPSWP to determine if any other job has again modified the value found in the *USRSPC. This will continue until CMPSWP returns a return value of 1 (indicating success) or we attempt to generate a &NewValue value of 4294967296, which would exceed the capacity of a 4-byte unsigned integer, resulting in a MCH1210 error ('Receiver value too small to hold result') being sent by the system.

 

You might be thinking, "This looping doesn't occur when using data areas, so why would I take the CMPSWP approach? Won't it hurt my system's performance?" Those are reasonable questions, and I'll make two observations.

 

First, the looping occurs only when two or more users are trying to get an invoice number at the same time. With *DTAARAs, this same type of contention occurs, but you don't see it as your job is waiting, rather than looping, for access to the *DTAARA. This "waiting" takes more time than the DoUntil loop shown, so when contention occurs, CMPSWP will resolve that contention faster.

 

Second, in controlled testing comparing RPG *DTAARA *IN and *OUT operations with a CMPSWP solution generating 10000 invoice numbers, CMPSWP was consistently in the neighborhood of 100 times faster (5 ms vs. 565 ms on my 515). And please note that that is 100 times faster, not 100 percent faster!

 

This is the equivalent program in COBOL:

 

PROCESS NOMONOPRC NOSTDTRUNC.

                                

IDENTIFICATION DIVISION.                                  

PROGRAM-ID. GETNBRCBL.

                                       

ENVIRONMENT DIVISION.                                     

CONFIGURATION SECTION.                                    

SPECIAL-NAMES.                                            

LINKAGE TYPE SYS FOR "_CMPSWP".

                              

DATA DIVISION.                                            

WORKING-STORAGE SECTION.                                  

01  FndValue                    PIC  9(9) BINARY.         

01  NewValue                    PIC  9(9) BINARY.         

01  RtnCde                      PIC S9(9) BINARY.         

01  SpcName                     PIC X(20)                     

                                VALUE "HOLDNBR   QGPL      ".

01  SpcPtr                      POINTER.

                     

LINKAGE SECTION.                                          

01  CurValue                    PIC 9(9)  BINARY.

              

PROCEDURE DIVISION.                                        

MAIN-LINE.                                                 

(A) CALL "QUSPTRUS" USING BY REFERENCE SpcName,               

                          BY REFERENCE SpcPtr.             

    SET ADDRESS OF CurValue TO SpcPtr.

(B) MOVE CurValue TO FndValue.                     

    PERFORM UNTIL RtnCde = 1                                  

            ADD 1 TO FndValue GIVING NewValue                 

(C)         CALL "_CMPSWP" USING BY REFERENCE FndValue,       

                                 BY REFERENCE CurValue,       

                                 BY VALUE     NewValue,       

                                 RETURNING    RtnCde          

    END-PERFORM.                                           

(D) DISPLAY NewValue.                                      

    STOP RUN.

 

And here's a slightly modified approach using RPG:

 

h dftactgrp(*no)

                                               

dGetNbr           pr            10i 0 extproc('_CMPSWP')       

d FndValue                      10u 0                          

d CurValue                      10u 0                          

d NewValue                      10u 0 value

                    

dRtvSpcPtr        pr                  extpgm('QUSPTRUS')       

d SpcName                       20    const                    

d UsrSpcPtr                       *                            

                                                               

dFndValue         s             10u 0                           

dCurValue         s             10u 0 based(NbrPtr)            

dSpcName          c                   'HOLDNBR   QGPL      '   

                                                               

 /free                                                          

 (A) RtvSpcPtr(SpcName :NbrPtr);

 (B) FndValue = CurValue;                            

 (C) dow GetNbr(FndValue :CurValue :(FndValue + 1)) = 0;    

     enddo;                                                 

 (D) dsply (FndValue + 1);                                  

     *inlr = *on;                                           

     return;                                                

 /end-free 

                                             

The RPG approach is modified when compared to the CL and COBOL examples because of RPG's ability to use an expression as a parameter when calling CMPSWP. Rather than having a DoUntil loop where the program has a separate statement adding 1 to &FndValue and giving &NewValue, the RPG sample uses a DoWhile, which directly runs the CMPSWP instruction, exiting when the return value is 1. As part of running the instruction, the program uses an expression for the third parameter and eliminates the need for variable &NewValue.

 

Now, the question in your mind might be reversed: "Why wouldn't I use CMPSWP?" Again, a very reasonable question, and I'll point out two major reasons.

 

First, when CMPSWP is used in conjunction with a *USRSPC as we did here, it does not provide for any of the auditing and recovery features, such as journaling, that can be used with *DTAARAs. In fact, once an application has a pointer to the *USRSPC, the application can do almost anything to the contents of the *USRSPC and there will be no log of the changes, no journal entries to roll back inadvertent changes, and so on. To reset the invoice number to a prior value will mean restoring the *USRSPC from a backup or having an application program set the appropriate value. The relative importance of this aspect will depend on what type of data your application is working with. If we're talking invoice numbers, then this consideration may not be significant.

 

A second consideration is that this approach works only if all manipulation of the shared variable (&CurValue in our case) is done via CMPSWP. In most areas of the system, i5/OS manages data sharing on your behalf (for instance, record locking when updating and *DTAARA locking when updating), and updates are synchronized across jobs transparently. With CMPSWP, there is no i5/OS protection unless all applications use CMPSWP when modifying the value of the shared variable. A developer for instance could inadvertently code this:

 

ChgVar     Var(&CurValue) Value(&NewValue)

 

This would cause an immediate change to &CurValue within the *USRSPC, potentially regressing &CurValue to an earlier value and causing duplication of invoice numbers. You will notice that the only change to &CurValue in the example program is done with CMPSWP. This is critical. Also, when displaying the value of the current invoice number, the program always uses a local variable such as &NewValue. This is because &CurValue can be changing dynamically because of work being done in other jobs. Keeping in mind the dynamic nature of &CurValue will take some getting used to for developers. *DTAARA values can also be corrupted due to errors in the application, but it's not done quite as easily!

 

I should point out a few items concerning the examples:

 

•     If, when calling the CL program, you receive a run-time error, please make sure you have PTF SI28620 installed on your system. This PTF includes necessary CL compiler corrections related to pointer usage and will require that you recompile the sample CL program.

•    Some of you might, upon reflection of the CMPSWP instruction, conclude that the statement shown at (B) in the previous examples is not needed. This is true! On the initial call to the CMPSWP instruction, you can pass in any value for &FndValue and, as long as &NewValue is set correctly, the samples will work properly. One time out of 4294967296, you will get "lucky" in that your arbitrary value for &FndValue happens to match &CurValue, and you will immediately exit the DoUntil loop. And one time out of 4294967296, you will receive a MCH1210 because you used a &FndValue of 4294967295 when trying to calculate &NewValue. But in the other 4294967294 cases, you will get a return value of 0 with the returned value of the parameter &FndValue set correctly for the next iteration of the Do loop. Personally, I recommend leaving the statement at (B) in as it provides better documentation of your intent, but it can be left out as long as you are willing to (most likely) loop through the Do logic one extra time.

•     As COBOL doesn't support unsigned binary values, COBOL developers may want to use 8-byte binary values in order to support values greater than 2147483647. I used 4-byte values in the samples because CL does not support 8-byte binary variables.

 

The CMPSWP instruction is not one that you will use every day. But the power and speed of the instruction will make for an excellent tool in your development toolkit when the occasion does arise.

More CL Questions?             

Wondering how to accomplish a function in CL? Send your CL-related questions to Bruce at This email address is being protected from spambots. You need JavaScript enabled to view it..  He'll see what he can do about answering your burning questions in future columns.

Bruce Vining

Bruce Vining is president and co-founder of Bruce Vining Services, LLC, a firm providing contract programming and consulting services to the System i community. He began his career in 1979 as an IBM Systems Engineer in St. Louis, Missouri, and then transferred to Rochester, Minnesota, in 1985, where he continues to reside. From 1992 until leaving IBM in 2007, Bruce was a member of the System Design Control Group responsible for OS/400 and i5/OS areas such as System APIs, Globalization, and Software Serviceability. He is also the designer of Control Language for Files (CLF).A frequent speaker and writer, Bruce can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it.. 


MC Press books written by Bruce Vining available now on the MC Press Bookstore.

IBM System i APIs at Work IBM System i APIs at Work
Leverage the power of APIs with this definitive resource.
List Price $89.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: