If you have become used to stopping PHANTOM processes by killing them or editing a controlling record in a file, there is a much more elegant method available using the often under-recognised method of process locks.
For example, if we choose lock 1 to signify that a job is already running and lock 2 to instruct a job to terminate, then the code below can be used to ensure that one - and only one - instance of the PHANTOM is permitted to run at any one time:
* Sample code snippet:- PHANTOM job start
*
* Set the PHANTOM job name
*
PHANTOM_JOB='WEEKLY_INVOICES_EMEA'
*
* Designate the locks to be used for this handshake
* RUNNING_LOCK=1 ;
* the lock number used by the PHANTOM when it is running
*
* check if the PHANTOM is running by attempting to set the process lock
* that it would own if it were already running.
*
LOCK RUNNING_LOCK THEN
*
* The controlled job is not running - OK to start it
* first release the lock we just set to check if it was already running.
*
UNLOCK RUNNING_LOCK
*
* now launch the PHANTOM
*
EXECUTE 'PHANTOM ':PHANTOM_JOB
PRINT 'PHANTOM ':PHANTOM_JOB:' launched @ ':TIMEDATE()
END ELSE
*
* already running - do not allow a second copy to run
*
PRINT PHANTOM_JOB:' is already started @ ':TIMEDATE()
END
As can be seen, if Process Lock 1 is set then the PHANTOM is already running, and we only permit one instance of the PHANTOM to run. If however the PHANTOM is not already running then once launched it would set Process Lock 1 to indicate that it is now running.
Similar code using process lock 2 can now be used to request that the PHANTOM terminate in a controlled fashion. As the PHANTOM job being used as an example may be either busy performing its designated task or sleeping waiting for a set time to elapse, we have added a loop/retry with a brief pause between each attempt.
<------------------------------------------------------------------------------------------------------------->
* Sample code snippet:- PHANTOM job termination
*
* Set the PHANTOM job name
*
PHANTOM_JOB='WEEKLY_INVOICES_EMEA'
*
* Designate the locks to be used for this handshake
*
RUNNING_LOCK=1;
* the lock number used by the job being controlled when it is running
STOPPING_LOCK=2;
* the lock number used to signal the job to stop
*
ALL_DONE=0
*
* check if the job is running
*
LOCK RUNNING_LOCK THEN
*
* The running lock could be obtained, so the job is not running and
* there is nothing to do.
*
UNLOCK RUNNING_LOCK
PRINT PHANTOM_JOB:' not running @ ':TIMEDATE()
*
END ELSE
*
* The job is running, so signal it to stop
*
LOCK STOPPING_LOCK THEN
*
* iterate - looking for the RUNNING_LOCK to be released when
* the job terminates.
*
MONITOR_STOPPED=0 CHECK_INTERVAL=2;
* interval between checks of the process status
CHECK_ITERATIONS=30; * number of times to check at the selected interval
ITERATION_PASS=0
LOOP
*
* Is the job still running?
*
LOCK RUNNING_LOCK THEN
*
* No - it is not running
* unlock the two locks we are using and exit gracefully.
*
UNLOCK RUNNING_LOCK
UNLOCK STOPPING_LOCK
ALL_DONE=1
IF ITERATION_PASS #0 THEN
PRINT
PRINT PHANTOM_JOB:' stopped @ ':TIMEDATE()
END ELSE
*
* Still running - wait a little longer *
ITERATION_PASS +=1
*
* Prettification - give a ticking row of dots as we wait
*
IF ITERATION_PASS=1 THEN
PRINT 'Waiting .':
END ELSE
PRINT '.':
END
IF ITERATION_PASS > CHECK_ITERATIONS THEN
*
* Number of attempts to stop the job exceeded - abandon the attempt.
*
ALL_DONE=2
PRINT; PRINT 'Stop of ':PHANTOM_JOB:' failed @ ':TIMEDATE()
END
END
UNTIL ALL_DONE#0 DO
*
* sleep until the next iteration
*
SLEEP CHECK_INTERVAL
REPEAT
PRINT
END ELSE
*
* STOPPING_LOCK already set - can't be used by two processes at the same time
PRINT 'Unable to set lock ':STOPPING_LOCK:' already set/in use @ ':TIMEDATE()
END
END
<------------------------------------------------------------------------------------------------------------->
Following on from the two controlling programs illustrated above, it now follows that the job being controlled will use the same locks in its overall control structures. Lock 1 indicating that the job is already running, and lock 2 indicating that the job has been requested to stop:
<------------------------------------------------------------------------------------------------------------->
* Sample code snippet: job being controlled
*
PHANTOM_JOB='WEEKLY_INVOICES_EMEA'
*
* Designate the locks to be used for this handshake
*
RUNNING_LOCK=1; * the lock number used by the job when it is running
STOPPING_LOCK=2; * the lock number used to signal the job to stop
*
* Is the job already running?
*
LOCK RUNNING_LOCK THEN
*
* Not already running - so OK to run as long as it has not
* already been asked to stop. So is STOPPING_LOCK set?
*
LOCK STOPPING_LOCK THEN
*
* No - so still OK to run.
*
UNLOCK STOPPING_LOCK
END ELSE
UNLOCK RUNNING_LOCK
PRINT PHANTOM_JOB:' under orders to stop - exiting @ ':TIMEDATE()
STOP
END
END ELSE
*
* already running - exit
*
PRINT PHANTOM_JOB:' already running, stopping @ ':TIMEDATE()
STOP
END
*
*
* <<< MAIN PROCESSING LOGIC>>>
*
*
* Has the job been ordered to stop?
*
LOCK STOPPING_LOCK THEN
*
* no - unlock and carry on
*
UNLOCK STOPPING_LOCK
EOF=0
END ELSE
*
* ordered to stop - release the running lock after tidying up
*
*<<< Place any closing logic here - for example: >>>
** Closing sequential files
** Updating control records
** etc.
*
UNLOCK RUNNING_LOCK; * release the lock that showed the job was running
*
* exit gracefully *
<------------------------------------------------------------------------------------------------------------->
A fully-worked example has been attached as three BASIC programs in source code form:
- START_LICENSE_MONITOR - a program to launch the PHANTOM program LICENSE_MONITOR
- LICENSE_MONITOR - a program to monitor and record U2 database license usage to a CSV file suitable for import into a spreadsheet.
- STOP_LICENSE_MONITOR - a program to stop the PHANTOM program LICENSE_MONITOR
The programs are fully self-documenting and should be compiled and catalogued in an account of your choice. To launch the monitoring program the syntax is:
- START_LICENSE_MONITOR n
- Where 'n' is the number of seconds between sample intervals. The default value is 5 (seconds).
- STOP_LICENSE_MONITOR
If wished the program can also be run directly on a terminal:
- LICENSE_MONITOR n
- Where 'n' is the number of seconds between sample intervals. The default value is 5 (seconds).
Some will be well familiar with this technique, but hopefully others will find it useful as an overall control methodology. For more widespread Job Control usage it may be necessary to dynamically allocate and return pairs of available free locks from the Process Lock table, though I will leave this as an exercise for the Developer if needed.
------------------------------
John Jenkins
Principal Technical Support Engineer
Rocket Software Limited
U.K.
------------------------------