project BLF > class BBaseDaemonProcessor > method LoopForWork

Description

Loop, and in each iteration check whether there is work to do.


Parameters


oiReturnStatusoutputintegerReturn status of the method.


Internal usage


BLF
method BBaseDaemonProcessor.StartDaemonProcessorInstance


program code (program1/bbasedaemonprocessor.p)

/* =================================================================================================== */
/* Find the Daemon it's debug specifications                                                           */
/* Hold them in class data-items and pass the as parameters to the methods called from PerformWorkItem */
/* =================================================================================================== */
assign viExternalDebugLevel     = 0
       vlAutoRecoverDaemons     = false
       vcConfigFile             = search(vcFcDaemonName + ".config":U)
       no-error.
if vcConfigFile       <> ? and 
   error-status:error  = false
then do :
    input from value(vcConfigFile).
    repeat on error undo, leave:  /* cannot use throw */
        import unformatted vcLine.
        if vcLine begins "DebugLevel=":U
        then assign viExternalDebugLevel = integer(substring(vcLine,LENGTH ("DebugLevel=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) no-error.
        /* backward compatibility */
        else if vcLine begins "BLDebugLevel=":U
             then assign viExternalDebugLevel = integer(substring(vcLine,LENGTH ("BLDebugLevel=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) no-error.
             else if vcLine begins "AutoRecoverDaemons=":U
                  then assign vlAutoRecoverDaemons = (trim(substring(vcLine,LENGTH ("AutoRecoverDaemons=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) = "TRUE":U or 
                                                      trim(substring(vcLine,LENGTH ("AutoRecoverDaemons=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) = "YES":U) no-error.
                  else if vcLine begins "AutoRecoverDaemonsIntervalSec=":U
                       then assign viAutoRecoverDaemonsIntervalSec = integer(trim(substring(vcLine,LENGTH ("AutoRecoverDaemonsIntervalSec=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U))) no-error.
    end. /* repeat */
    input close.
end. /* if vcConfigFile <> ? */

/* ========================================================================== */
/* By default all the Daemon processes will be stopped at the end.            */
/* The large-interval for calling the submethod will be 20 minutes by default */
/* ========================================================================== */
assign vlStopAllDaemonProcesses        = true
       viAutoRecoverDaemonsIntervalSec = (if viAutoRecoverDaemonsIntervalSec = 0 or 
                                             viAutoRecoverDaemonsIntervalSec = ? or 
                                             viAutoRecoverDaemonsIntervalSec < 60
                                          then 1200
                                          else viAutoRecoverDaemonsIntervalSec).

/* ================= */
/* Start the Looping */
/* ================= */
daemon-loop-block:
repeat on error undo, throw:

    assign viLoop = 1.
    
    /* ================================================================= */
    /*  Check whether the daemon needs to be stopped.                    */
    /* ================================================================= */
    <Q-18 run DaemonInfoLimited (all) (Read) (NoCache)
       (input 0, (DaemonId)
        input vcFcDaemonName, (DaemonName)
        input '':U, (DaemonStatus)
        input '':U, (DaemonStatusDiffersFrom)
        output dataset tqDaemonInfoLimited) in BBaseDaemon >

    find first tqDaemonInfoLimited where
               tqDaemonInfoLimited.tcDaemonName = vcFcDaemonName no-error.
    if not available tqDaemonInfoLimited
    or tqDaemonInfoLimited.tcDaemonStatus = {&DAEMONSTATUS-STOPPING}
    or tqDaemonInfoLimited.tcDaemonStatus = {&DAEMONSTATUS-INACTIVE}
    then leave daemon-loop-block.

    /* In case the PID would not be in the list held in fcDaemon, we stop this process (but only this process) */
    if lookup (string (viFcDaemonProcessId), tqDaemonInfoLimited.tcDaemonProcessIDs) = 0
    and lookup (vcFcDaemonProcessHostName + ":" + string (viFcDaemonProcessId), tqDaemonInfoLimited.tcDaemonProcessIDs) = 0
    then do:

        <M-70 run SetMessageInDaemonLog
           (input  'DaemonProcessor.LoopForWork; ':U + #T-78'The daemon process was stopped as it did not match with the daemon info in the database:':255(322033527)T-78# + string (viFcDaemonProcessId) + ' * ' + tqDaemonInfoLimited.tcDaemonProcessIDs (icMessage), 
            output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>

        assign vlStopAllDaemonProcesses = no.
        leave daemon-loop-block.
    end.

    assign viFcDaemonInterval = tqDaemonInfoLimited.tiDaemonInterval
           viRequestsNR       = tqDaemonInfoLimited.tiDaemonNrOfRequestsInLoop.
    
    /* ================================================================= */
    /* Update the SessionTimeStamps each 30 min (1800 sec) - use UTC     */
    /* ================================================================= */
    assign session:timezone = 0.

    if ((today - vtLastUpdateDateStamp) * 86400 +  (time - viLastUpdateTimeStampSec) > 1800)
        or viLastUpdateTimeStampSec = 0
    then do:
        <M-77 run UpdateSessionTimeStamps
           (input  viSessionID (iiSessionID), 
            output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>

        assign session:timezone = 0.
        assign vtLastUpdateDateStamp    = today
               viLastUpdateTimeStampSec = time.

    end.
    assign session:timezone = viTimeOffset.

    /* ================================================================= */
    /*  Load external work to be done                                    */ 
    /* ================================================================= */
    <M-7 run LoadExternalWork (output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>    
    if viFcReturnSuper <> 0
    then do :
        <M-9 run SetMessageInDaemonLog
           (input  'DaemonProcessor.LoopForWork; ':U + substitute('***LoadExternalWork failed with (&1)':U,string(vifcreturnSuper)) (icMessage), 
            output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
        /* ================================================================= */
        /*  We do not leave the loop, producing the effort should give       */
        /*  enough feedback.                                                 */
        /* ================================================================= */
    end.

    /* ================================================================= */
    /*  Read the not-locked work queue records.                          */
    /* ================================================================= */
    assign vcDaemonQueueStatus  = {&DAEMONQUEUESTATUS-WAITING}.
    <Q-3 run GetNextDaemonQueue (all) (Read) (NoCache)
          (input viFcDaemonId, (DaemonId)
           input vcDaemonQueueStatus, (DaemonQueueStatus)
           output dataset tqGetNextDaemonQueue) in BBaseDaemonQueue >

    queue-loop-block:
    for each tqGetNextDaemonQueue where
             tqGetNextDaemonQueue.tiDaemonQueueId <> 0 
             by tqGetNextDaemonQueue.tiDaemonQueuePriority
             on error undo, throw:

        /* Use date and time in UTC */
        session:timezone = 0.
        vlNext = (tqGetNextDaemonQueue.ttDaemonQueueReqStartDate > today
             or (tqGetNextDaemonQueue.ttDaemonQueueReqStartDate = today and
                 tqGetNextDaemonQueue.tiDaemonQueueReqStartTime > TIME)) and
             tqGetNextDaemonQueue.ttDaemonQueueReqStartDate <> ?.
        session:timezone = viTimeOffset.

        if vlNext then next.

        /* ================================================================= */
        /*  Try to lock the daemonqueue record.                              */
        /* ================================================================= */
        <M-20 run LockWorkItem
           (input  tqGetNextDaemonQueue.tiDaemonQueueId (iiDaemonQueueID), 
            input  viFcDaemonProcessId (iiProcessID), 
            output vcMessage (ocMessage), 
            output vlLocked (olLocked), 
            output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>

        if viFcReturnSuper <> 0 and viFcReturnSuper ne -111 /* because we don't want to see in the log file that another instance is processing this item */
        then do :
            <M-5 run SetMessageInDaemonLog
               (input  'DaemonProcessor.LoopForWork; ':U + substitute(#T-95'Unable to lock work item (&1) (&2).':255(613638236)T-95#,string(tqGetNextDaemonQueue.tiDaemonQueueId),vcMessage) (icMessage), 
                output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
        end.

        if vlLocked
        then do :
            /* ================================================================= */
            /*  The daemonqueue record is locked, now the desired action can be  */
            /*  performed.                                                       */
            /* ================================================================= */
            <M-6 run PerformWorkItem
               (input  tqGetNextDaemonQueue.tiDaemonQueueId (iiDaemonQueueId), 
                output vlSuccess (olSuccess), 
                output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>

            /* Delete dynamic datasets created during processing */
            run DeleteDSInPool.
            
            /* Move error and warning messages */
            empty temp-table tPassMessages.

            for each tFcMessages on error undo, throw:
                create tPassMessages.
                raw-transfer tFcMessages to tPassMessages.
                delete tFcMessages.
            end.

            if viFcReturnSuper <> 0
            then do:
                assign vcMessage = "DaemonProcessor.LoopForWork: " +
                                   if viFcReturnSuper > 0
                                   then trim(subst(#T-8'Warning processing work item &1, return status &2.':255(757252153)T-8#, string(tqGetNextDaemonQueue.tiDaemonQueueId), string(viFcReturnSuper)))
                                   else trim(subst(#T-89'***Error processing work item &1, return status &2.':255(68296975)T-89#, string(tqGetNextDaemonQueue.tiDaemonQueueId), string(viFcReturnSuper))).

                if viFcReturnSuper < 0
                then assign vlSuccess = false.

                <M-11 run SetMessageInDaemonLog
                   (input  vcMessage (icMessage), 
                    output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>

                create tPassMessages.
                assign tPassMessages.tcFcMessage = trim(subst(#T-13'The work item processing has returned return status &1.':255(443782020)T-13#, string(viFcReturnSuper))).
            end.
            
            <M-21 run SetWorkResult
               (input  vlSuccess (ilSucces), 
                input  tqGetNextDaemonQueue.tiDaemonQueueId (iiDaemonQueueID), 
                input  tqGetNextDaemonQueue.tiDaemonQueuePriority (iiDaemonQueuePriority), 
                output vcMessage (ocErrorMessage), 
                output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>

            /* JLA - BTS 10365 : Catch possible errors which are returned from the SetWorkResult method. */
            if viFcReturnSuper <> 0
            then do :
                <M-16 run SetMessageInDaemonLog
                   (input  'DaemonProcessor.LoopForWork; ':U + substitute('***SetWorkResult failed with (&1)':U,string(vifcreturnSuper)) + chr(10) + vcMessage (icMessage), 
                    output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>            
            end.            
            /* JLA - BTS 10365 : Catch possible errors which are returned from the SetWorkResult method. */                
        end.

        /* ================================================================= */
        /* Did we process enough requests ?                                  */
        /* ================================================================= */
        assign viLoop = viLoop + 1.
        if viLoop > viRequestsNR
        then leave queue-loop-block.

    end. /* queue-loop-block */

    if vlDaemonNeedsToEnd
    then leave daemon-loop-block.

    /* ============================================================================== */
    /* Clean up the non-persistent widget-pool to avoid build-up of dynamic objects.  */
    /* ============================================================================== */
    delete widget-pool "non-persistent".
    create widget-pool "non-persistent" persistent no-error.
    
    /* ================================================================= */
    /*  Sleep for a moment                                               */
    /* ================================================================= */   
    if viFcDaemonInterval > 0 and 
      (viLoop < 100 and viLoop <= viRequestsNR)
    then pause viFcDaemonInterval no-message.
    else pause 2 no-message. /* Go sleeping for 2 seconds so the CPU can be given to another process */
    
    /* ================================================================= */
    /*  Call the hook to enable customized code required at the end of   */
    /*  each iteration of the loop.                                      */
    /* ================================================================= */
    assign vlPerformedWork = viLoop > 1.
    <M-22 run IterationEnd
       (input  vlPerformedWork (ilIterationPerformedWorkItems), 
        output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>   
    if viFcReturnSuper < 0
    then do:
        assign oiReturnStatus = viFcReturnSuper.
        leave daemon-loop-block.
    end.
    
    /* ============================================================================================================ */
    /* Call a submethod that will only be called after a longer time (viAutoRecoverDaemonsIntervalSec). This method */
    /* can start some other daemons that should be running but that aren't or some other actions that can take up   */
    /* quite some time and that shouldn't be called very frequently (only after viAutoRecoverDaemonsIntervalSec)    */
    /* When we switch to another date (pass midnight), then we call the submethod straight away                     */
    /* ============================================================================================================ */
    if vlAutoRecoverDaemons = true
    then do :
        /* Switch to timezone UTC */
        assign session:timezone = 0.
        /* Initialise the date-time-reference when not yet done once - make sure the first time we pass here it gets executed in 30 seconds or simply the next time we pass here  */
        if vtLastStartDateForStartOtherDmn = ? or
           viLastStartTimeForStartOtherDmn = 0 or 
           viLastStartTimeForStartOtherDmn = ? 
        then if time > viAutoRecoverDaemonsIntervalSec 
             then assign vtLastStartDateForStartOtherDmn = today 
                         viLastStartTimeForStartOtherDmn = time - viAutoRecoverDaemonsIntervalSec + 30.
             else assign vtLastStartDateForStartOtherDmn = today - 1
                         viLastStartTimeForStartOtherDmn = 1.
        /* Check if the interval is passed compared against the date-time-reference */
        if today > vtLastStartDateForStartOtherDmn or 
           time  > viLastStartTimeForStartOtherDmn + viAutoRecoverDaemonsIntervalSec
        then do :  
            /* Call the sub-method that will do the actual processing - do not check for errors as we want to keep this daemon running even if starting other daemons failed */         
            <M-31 run LoopForWorkAutoRecoverDaemons  (output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
            /* Reset the date-time-reference */
            assign vtLastStartDateForStartOtherDmn = today 
                   viLastStartTimeForStartOtherDmn = time.
        end. /* if today > vtLastStartDateForStartOtherDmn or  */
        /* Switch from timezone UTC to the current timezone*/
        assign session:timezone = viTimeOffset.
    end. /* if vlAutoRecoverDaemons = true */
    
    /* ======================================== */
    /* Check if the daemon has to stop its loop */
    /* ======================================== */
    if vlDaemonNeedsToEnd
    then leave daemon-loop-block.
    
end. /* daemon-loop-block */

finally:

    if vlStopAllDaemonProcesses
    then do:
        <M-14 run StopDaemon  (output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
        if viFcReturnSuper <> 0
        then do :
            <M-15 run SetMessageInDaemonLog
               (input  'DaemonProcessor.LoopForWork; ':U + #T-55'The daemon processor instance was unable to update the daemon status when stopping.':255(132822179)T-55# (icMessage), 
                output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
        end.
    end. /* vlStopDaemon */

end finally.