IBM frequently admonishes system programmers to transform their mainframe applicationsto being thread-safe to reap the performance benefits of parallelism and vertical integration. Why not use the CICS ENQ command?
At minimum, a thread-safe program must be reentrant and serially access important resources. CICS and database management systems protect data access, but other things, such as a location in memory, require applications to do their own locking. This is where CICS ENQ, or enqueue comes in handy.
Executing the CICS ENQ command
The ENQ interface requires a resource name or address on which to broker access. After issuing the command, control will return to the caller if no one else holds the enqueue. Otherwise, by default, the task will wait until the enqueue owner frees the resource by terminating or by issuing the dequeue (DEQ) command.
For additional flexibility, other parameters allow the enqueue to persist across units of work or for the lifetime of the task, and NOSUSPEND tells CICS to return immediately to the application even if another task holds the enqueue.
For example, if an application wants to serialize access to a counter in storage, the enqueue command might look like this:
EXEC CICS ENQ RESOURCE(CNTRADDR)
CNTRADDR contains the address of the storage. For many reasons, it may be wiser to serialize the storage by a name that requires commands like this:
EXEC CICS ENQ RESOURCE('LameResourceName') LENGTH(16)
The LENGTH parameter tells CICS how much of the resource name is significant.
For this to work properly, all programs must use exactly the same resource name -- down to the bit -- and length. Any program not linked to the enqueue will run roughshod over the whole process.
Initiating a global enqueue
The scope of the above commands is limited to a single CICS region. However, some applications may need to raise logical partition- or sysplex-level enqueues. These are called "global enqueues." A global enqueue causes CICS to issue a global resource serialization (GRS) ENQ macro that with an eight-byte queue name and a resource name that's a maximum of 44 bytes.
A global enqueue requires an enqueue model (ENQMODEL) resource definition. The ENQMODEL specifies an enqueue name (ENQNAME) and scope (ENQSCOPE). Placing an asterisk at the end of the ENQNAME makes it generic, which frees the systems programmer from creating individual models for every possible combination of names the application team may devise. ENQSCOPE specifies a four-byte scope.
For every ENQ command, CICS matches the resource name against the installed ENQMODELs. If it doesn't find a match, the enqueue processes locally. If CICS finds a matching model with a non-blank ENQSCOPE, CICS issues the global ENQ macro.
CICS formats the ENQ macro queue name as "DFHE" with the ENQSCOPE value on the end, and passes along the RESOURCE parameter from the original CICS command.
So, if a program issues an ENQ command with resource name "HelloDolly" that matches an ENQMODEL with an ENQSCOPE of "HOOB," CICS issues a GRS ENQ macro with a queue name of "DFHEHOOB" and resource "HelloDolly." GRS configuration parameters (GRSCNFxx) also apply.
The trouble with enqueue waits
It sounds pretty straightforward, but there is a twist that might cause confusing results.
According to researchers, when a global enqueue resource conflict occurs, CICS does not put the requesting task to sleep; it just imposes a 200 millisecond (msec) wait. After the wait expires, CICS will continue retrying the ENQ macro until it finally controls the enqueue. IBM confirms that CICS transactions can be delayed by these 200-msec waits.
Consider the implications. A transaction may issue one ENQ command, but the CICS monitoring facility (CMF) performance bucket for global enqueue waits will instead reflect the number of times CICS tried to get control of the enqueue.
In the diagram below, each block represents 50 msec. Teal rectangles show when a task owns an enqueue, while red indicates when it waits.
At intervals 1 and 2, TRNA owns the enqueue. TRNB tries but fails to get control in interval 2, so CICS puts the task to sleep for another 200 msec interval. By Interval 3, TRNA relinquishes the enqueue in time for TRNC to gain ownership, which it holds for 250 msec. Meanwhile, TRNB wakes up in Interval 6 only to conflict with TRNC and enter a new wait. Finally, by Interval 10, TRNC releases the enqueue so that TRNB can take ownership.
This scenario presents problems. A task with bad timing luck may wait for an enqueue much longer than necessary, and tasks may not get ownership of the enqueue in the order in which they are issued.
Local ENQ commands work just fine to lock resources on local CICS. The resource overhead involved to acquire and release global enqueues, especially in a large Sysplex, may make global enqueues prohibitive, but it depends on the needs of your business. If at all possible, leave global locking to database systems and record-level sharing.
About the author:
Robert Crawford spent 29 years as a systems programmer, covering CICS technical support, Virtual Storage Access Method, IBM DB2, IBM IMS and other mainframe products. He programmed in Assembler, Rexx, C, C++, PL/1 and COBOL. Crawford is currently an operations architect based in south Texas, establishing mainframe strategy for a large insurance company.