Using C Runtime Functions in COBOL: sleep()-ing and qsort()-ing

By | August 10, 2013

Hard to believe, but once I had an Operator complain to me about a looping batch job.  When I looked into it, I found that the programmer had coded a loop around a COBOL ‘ACCEPT TIME’ comparing the seconds value with the previous seconds value waiting for it to increase.  A classic busy wait.  In this case, there was also a logic error in that the previous value was already “59” and no matter how long the program checked the time the seconds value was never going to be larger than that. (Until, I suppose, a leap second occurred.)

I have no recollection why the programmer felt he needed to do this; I don’t remember what problem he was trying to solve. I figured he came from a Windows programming environment where it probably doesn’t matter if you use all of the CPU. 😉

In any case, I provided him some sample COBOL code to call BPX1SLP instead of the busy wait and he was satisfied:

77  seconds pic s9(9) comp-5 value 30.
77  retval  pic s9(9) comp-5.
77  bpx1slp pic x(8)  value 'BPX1SLP'.

[...]

call bpx1slp   using  seconds
                      retval.

Nowadays, there are a couple of Language Environment callable service routines that will do this, too, such as CEE3DLY and CEEDLYM. I wonder when these were introduced.  Did I just overlook them at the time?

sleep()-ing

Simple and effective but not as clear as I would have liked. I really wanted to call the C runtime function sleep() from COBOL. (Even though sleep() probably just calls BPX1SPL anyway.) This can be done, as I will demonstrate, but does require some additional changes that did not conform to our shop standards, arbitrary though they might have been.

Code to use the sleep() function is just as simple:

77  seconds pic s9(9) comp-5 value 30.
77  retval  pic s9(9) comp-5.

[---]

call 'sleep'   using  by value  seconds
                      returning retval.

But the call must be a static call which means compiling with the NODYNAM option. (Non-standard in our case.) The manual also states you must use the PGMNAME(LONGMIXED) option, but I didn’t run into a problem without it with sleep(). Other C runtime functions probably won’t work without it. This is covered in Calling UNIX/POSIX APIs.  Wait.  What?  This is Unix?  This is POSIX?  It turns out, this means we must also run with POSIX(ON) which probably means either a PARM change at run-time or a CEEUOPT set up or something: See Specifying Run-Time Options.  If this were C we could use a #pragma, but there’s nothing like that for COBOL yet.  Anyway, this sort of complication was more than I wanted to have to explain to this programmer.  Also, the load library needs to be a PDSE (or hfs).  I wasn’t going to affect that change.

But, for my benefit, I tested this using a CEEUOPT…

// JCLLIB ORDER=(IGY.SIGYPROC)
//CEEUOPT  EXEC ASMAC
//C.SYSLIB DD  DSN=CEE.SCEEMAC
//C.SYSIN  DD  *
CEEUOPT CSECT
CEEUOPT AMODE ANY
CEEUOPT RMODE ANY
CEEXOPT POSIX=ON
END   CEEUOPT
//C.SYSLIN DD  DSN=&&LOADSET
//COBSLEEP EXEC IGYWCLG,LNGPRFX='IGY'
//COBOL.SYSIN  DD DISP=SHR,DSN=XXXXXXX.SRC.COB(COBSLEEP)
//LKED.SYSLMOD DD DSNTYPE=LIBRARY
//LKED.SYSIN   DD *
  ENTRY COBSLEEP
//GO.SYSOUT    DD SYSOUT=*

That was fun, but a bit obscure. Once I got this working, I could move on to what I really wanted to try: qsort().

qsort()-ing

qsort() is a standard C library routine that sorts an in-storage array using the quicksort method.  The complexities here are that the calling program must provide a routine to compare array items that the qsort function will call.  In my case, I chose to do this as a separate entry point in the same program calling qsort().  This makes the program “recursive” in COBOL terms.  This is noted in the source as

identification division.
program-id.    "QSORTTST" is recursive.

We need to pass the address of the compare routine to qsort() which means we have to know what it is. This is how we do that:

77  qsort-compare-proc-ptr          procedure-pointer.
[...]
set  qsort-compare-proc-ptr  to entry 'QSORTCMP'
call 'QSORT' using by reference data-table
                   by value     table-entry-count
                   by value     length of table-entry (1)
                   by value     qsort-compare-proc-ptr
[...]
entry 'QSORTCMP' using compare-entry-a
                       compare-entry-b

The compare routine (QSORTCMP in the example) sets the return-code to zero, -1 or +1 depending on the result of the comparison of the two table entries passed to it.

if  compare-entry-a-key > compare-entry-b-key
    move 1    to return-code
else
    if  compare-entry-a-key < compare-entry-b-key
        move -1   to return-code
    else
        move zero to return-code
    end-if
end-if

goback
.

The rest of the demo program, QSORTTST.cob, is pretty standard COBOL stuff and is compiled the same was as the COBSLEEP program, above.

qsort() is useful and gets us away from homebuilt sort routines.  I also like tsearch().

One issue we will have to address over and over again if we are going to use POSIX, C runtime services is that while there are C header files describing the functions and structures, there is nothing supporting COBOL. This will introduce some amount of complexity and risk to any project. If only there were an automatic way of generating the proper COBOL artifacts that will reduce the possibility of error and keep things up to date.

Can we generate COBOL copybooks from the appropriate C header files?  There may be a way with RDz or some other Rational Developer bundle.  (I can’t find the reference for this at the moment.)  There is also the h2cpy utility from Micro Focus, if you have that somewhere in your shop, you could ftp the z/OS /usr/include directories down to a development server and use it.  It will generate COBOL that won’t work will the z/OS Enterprise compiler without some massaging so it can’t easily be made automatic, but at least it gets you started.

To pass a structure to a C routine, you would normally use BY REFERENCE, however if it is the last parameter on the CALL, COBOL will set the high order bit.  To avoid this, use BY VALUE ADDRESS OF.

See also, using regexec().

Leave a Reply

Your email address will not be published. Required fields are marked *