?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Statistics Facility: Job Limits Manager' ??
MODULE sfm$job_limits_manager;

{ PURPOSE:
{   This module contains the procedures that are used to create and manage job
{   limits.
{
{ DESIGN:
{   Limits contain accumulator, job warning limit, and job maximum limit values
{   that are used to control the number of resources a job is allowed to use.
{
{   If the accumulator reaches the job warning limit, a job warning condition
{   is initiated.  When a job warning condition occurs, a condition handler
{   (either user supplied or system default) will be called to deal with the
{   condition.  The job warning limit can be changed to any value between the
{   current value of the accumulator and the job maximum limit.
{
{   The job maximum limit is supposed to specify the point at which a job is
{   terminated without allowing the user to gain control (epilog processing would
{   still occur).  This implementation does not enforce job maximum limits because
{   the system does not (currently) provide the capability to "kill" a job and the
{   methods that are available for causing a job to terminate have the potential
{   for destroying files (and even hanging all of the jobs in the system).
{
{   Until solutions to the problems associated with a "kill" job function have
{   been found, the value of the maximum limit will be raised to unlimited if it is
{   hit.  That means that NOS/VE does not provide a mechanism to prevent a user
{   from exceeding a validation or job class imposed limit.
{
{   The limits for a job are kept in a linked list (called the job limit chain).
{   It will link all of the limit information together on a "first in - first out"
{   chain.  Updates to the job limits chain are interlocked by using signature
{   locks and the same lock variable that is used to interlock the job routing
{   control table.
{
{   Procedures are provided to:
{      - create a new limit for a job
{      - update the accumulator value for a limit
{      - update the job warning limit value for a limit
{      - update the job maximum limit value for a limit
{      - initiate a job resource condition
{
{   Once a limit has been created, it cannot be deleted and user code must not
{   be allowed to change the value of the job maximum limit.
{
{   Not allowing a limit to be deleted, has the nice side effect of allowing
{   code to read information from the job limit chain without having to set a
{   signature lock.

*copyc sfc$compiling_for_test_harness
?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc avc$system_defined_limit_names
*copyc jmd$job_resource_condition
*copyc jmt$job_resource_signal
*copyc ost$name
*copyc ost$status
*copyc sfc$unlimited
*copyc sfc$warning_grace_period
*copyc sfe$limit_condition_codes
*copyc sfe$statistics_not_available
*copyc sft$accumulator_update_kind
*copyc sft$binary_logset
*copyc sft$counters
*copyc sft$enforcement
*copyc sft$limit_chain_entry
*copyc sft$statistic_code
*copyc tmc$signal_identifiers
?? POP ??
*copyc pmf$job_mode
*copyc avp$monitor_statistics_handler
*copyc clp$find_current_job_synch_task
*copyc clp$get_processing_phase
*copyc jmp$logout
*copyc jmp$system_job
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$clear_job_signature_lock
*copyc osp$generate_message
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$test_signature_lock
*copyc oss$job_pageable
*copyc pmp$continue_to_cause
*copyc pmp$get_global_task_id
*copyc pmp$send_signal
*copyc sfp$add_job_routing_control
*copyc sfp$change_file_space_limit
*copyc sfp$job_limit_chain_entry
*copyc sfp$last_job_limit_chain_entry
*copyc tmp$fetch_job_statistics
*copyc avv$monitor_statistics_lock
*copyc jmv$jcb
*copyc osv$job_pageable_heap
*copyc pmv$job_maximum_limit_exceeded
*copyc sfv$job_routing_control_table
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$create_job_limit', EJECT ??
*copyc sfh$create_job_limit

{ DESIGN:
{   The initial value, maximum limit and warning limit are checked to insure
{   that INITIAL_VALUE < RESOURCE_LIMIT <= MAXIMUM_LIMIT.  If this is not true
{   an abnormal status of SFE$INVALID_INITIAL_LIMIT_VALUE is returned.
{
{   The job routing control table signature lock is set.
{
{   The job limit chain is searched to verify that no other limit has a name
{   that matches the specified name.  If another limit is found with the same
{   name, an abnormal status of SFE$DUPLICATE_LIMIT_NAME is set.
{
{   Otherwise, a limit chain entry is allocated and initialized using the values
{   specified on the call.  The limit count is incremented and used as the warning
{   condition identifier.
{
{   Clear the signature lock on the job routing control table.
{
{   If a pointer to an array of statistic codes was supplied,
{   SFP$ADD_JOB_ROUTING_CONTROL is called for each statistic code to set the limit
{   name in the routing control table entry.

  PROCEDURE [XDCL, #GATE] sfp$create_job_limit
    (    limit_name: ost$name;
         statistic_codes: ^array [1 .. * ] of sft$statistic_code;
         initial_value: sft$counter;
         warning_limit: sft$counter;
         maximum_limit: sft$counter;
         enforcement: sft$enforcement;
     VAR status: ost$status);

    VAR
      index: integer,
      limit_chain_entry: ^sft$limit_chain_entry;

?? NEWTITLE := 'create_limit_chain_entry', EJECT ??

    PROCEDURE create_limit_chain_entry
      (    limit_name: ost$name;
           initial_value: sft$counter;
           warning_limit: sft$counter;
           maximum_limit: sft$counter;
           enforcement: sft$enforcement;
       VAR limit_chain_entry: ^sft$limit_chain_entry;
       VAR status: ost$status);

      VAR
        last_limit_chain_entry: ^sft$limit_chain_entry;

      status.normal := TRUE;

      ALLOCATE limit_chain_entry IN osv$job_pageable_heap^;

      limit_chain_entry^.limit.name := limit_name;
      IF limit_name = avc$cpu_time_limit_name THEN
        limit_chain_entry^.limit.condition_identifier := jmc$time_limit_condition;
        ?IF sfc$compiling_for_test_harness THEN
          sfv$job_limit_count := sfv$job_limit_count + 1;
        ?IFEND
      ELSE
        sfv$job_limit_count := sfv$job_limit_count + 1;
        limit_chain_entry^.limit.condition_identifier := sfv$job_limit_count;
      IFEND;
      limit_chain_entry^.limit.accumulator := initial_value;
      limit_chain_entry^.limit.job_resource_limit := warning_limit;
      limit_chain_entry^.limit.job_abort_limit := maximum_limit;
      limit_chain_entry^.limit.enforcement := enforcement;
      limit_chain_entry^.cpu_time_of_job_resource_signal := 0;
      limit_chain_entry^.forward := NIL;

      last_limit_chain_entry := sfp$last_job_limit_chain_entry ();
      IF last_limit_chain_entry = NIL THEN
        sfv$first_job_limit_chain_entry := limit_chain_entry;
      ELSE
        last_limit_chain_entry^.forward := limit_chain_entry;
      IFEND;

    PROCEND create_limit_chain_entry;
?? OLDTITLE, EJECT ??
    status.normal := TRUE;

    IF (warning_limit > maximum_limit) THEN
      osp$set_status_abnormal ('SF', sfe$invalid_initial_limit_value, limit_name, status);
      IF warning_limit = sfc$unlimited THEN
        osp$append_status_parameter (osc$status_parameter_delimiter, 'UNLIMITED', status);
      ELSE
        osp$append_status_integer (osc$status_parameter_delimiter, warning_limit, 10, FALSE, status);
      IFEND;
      IF initial_value = sfc$unlimited THEN
        osp$append_status_parameter (osc$status_parameter_delimiter, 'UNLIMITED', status);
      ELSE
        osp$append_status_integer (osc$status_parameter_delimiter, initial_value, 10, FALSE, status);
      IFEND;
      IF maximum_limit = sfc$unlimited THEN
        osp$append_status_parameter (osc$status_parameter_delimiter, 'UNLIMITED', status);
      ELSE
        osp$append_status_integer (osc$status_parameter_delimiter, maximum_limit, 10, FALSE, status);
      IFEND;
      RETURN;
    IFEND;

    osp$set_job_signature_lock (sfv$job_routing_control_lock);

{   Check for a limit with the specified name

    limit_chain_entry := sfp$job_limit_chain_entry (limit_name);

    IF limit_chain_entry = NIL THEN
      create_limit_chain_entry (limit_name, initial_value, warning_limit, maximum_limit, enforcement,
            limit_chain_entry, status);
    ELSE
      osp$set_status_abnormal ('SF', sfe$duplicate_limit_name, limit_chain_entry^.limit.name, status);
    IFEND;

    osp$clear_job_signature_lock (sfv$job_routing_control_lock);

    IF NOT status.normal THEN
      RETURN;
    IFEND;

{   If an array of statistic codes has been supplied, the routing control
{   entries for the specified statistics are updated to include the limit name
{   so SFP$EMIT_STATISTIC can update the limit accumulator.

    IF statistic_codes <> NIL THEN
      FOR index := 1 TO UPPERBOUND (statistic_codes^) DO
        sfp$add_job_routing_control (statistic_codes^ [index], $sft$binary_logset [],
              limit_chain_entry^.limit.name, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      FOREND;
    IFEND

  PROCEND sfp$create_job_limit;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$initiate_resource_condition', EJECT ??
*copyc sfh$initiate_resource_condition

{ NOTES:
{   It is assumed that the job routing control table is already locked so this
{   routine can update fields in the limit chain entry.
{
{   The number of job mode CP seconds specified by the constant
{   SFC$RESOURCE_GRACE_PERIOD as the minimum amount of time between job warning
{   signals is arbitrary.  This interval protects against the end case of back to
{   back warning conditions when the job is at its CP time or SRU job warning
{   limit.
{
{ DESIGN:
{   The current job mode CP time is compared to the job mode CP time recorded
{   the last time a signal was sent for this limit.  If less than
{   SFC$RESOURCE_GRACE_PERIOD CP seconds have elapsed, normal status is returned
{   and no signal is sent.
{
{   The current job mode CP time is recorded in the limit chain entry.
{
{   A job warning signal is sent to the current job synchronous task.  The
{   contents of the signal will be the condition identifier associated with the
{   limit.

  PROCEDURE [XDCL, #GATE] sfp$initiate_resource_condition
    (    limit_chain_entry: ^sft$limit_chain_entry;
     VAR status: ost$status);

    VAR
      global_task_id: ost$global_task_id,
      job_resource_signal: jmt$job_resource_signal,
      job_statistics: jmt$job_statistics,
      local_task_id: pmt$task_id,
      time_for_next_signal: ost$cp_time_value;

    status.normal := TRUE;

{   Check if enough time has passed since the last job warning signal for this
{   limit.

    IF (limit_chain_entry^.limit.name <> avc$pfs_limit_name) AND
          (limit_chain_entry^.limit.name <> avc$tfs_limit_name) THEN
      tmp$fetch_job_statistics (job_statistics, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      IF limit_chain_entry^.cpu_time_of_job_resource_signal <> 0 THEN
        time_for_next_signal := limit_chain_entry^.cpu_time_of_job_resource_signal +
              (sfc$warning_grace_period * 1000000);
        IF job_statistics.cp_time.time_spent_in_job_mode < time_for_next_signal THEN
          RETURN;
        IFEND;
      IFEND;

      limit_chain_entry^.cpu_time_of_job_resource_signal := job_statistics.cp_time.time_spent_in_job_mode;
    IFEND;

    clp$find_current_job_synch_task (local_task_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pmp$get_global_task_id (local_task_id, global_task_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Build a job warning signal with the content of the signal being the condition identifier for the limit that
{ has been hit.

    job_resource_signal.signal_id := jmc$job_resource_signal_id;
    job_resource_signal.signal_contents := limit_chain_entry^.limit.condition_identifier;

    pmp$send_signal (global_task_id, job_resource_signal.signal, status);

  PROCEND sfp$initiate_resource_condition;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$update_job_maximum_limit', EJECT ??
*copyc sfh$update_job_maximum_limit

{ NOTES:
{   User code should not be allowed to call this routine.  Abort limits are set
{   based on information from the user's validation and/or job class.  The user
{   must not be allowed to change the maximum limit.
{
{   Job maximum limits will not be enforced until NOS/VE supports a "kill" job
{   function.  Once a "kill" job function is provided, this routine will be used
{   to raise the maximum limits to insure that epilogs get a chance to execute (even
{   if the job hit an maximum limit).
{
{ DESIGN:
{   If the pointer to the job routing control table is NIL, this routine returns
{   with an abnormal status of SFE$STATISTICS_NOT_AVAILABLE.  Otherwise a
{   signature lock is set for the job routing control table.
{
{   The limit chain is searched for an entry with the specified name.  If the
{   entry cannot be found, an abnormal status of SFE$LIMIT_NOT_ACTIVATED is
{   returned.
{
{   If the entry is found, the value specified as the new maximum limit is
{   compared with the current warning limit.  If the value is greater than or
{   equal to the warning limit, the maximum limit field is updated.  If the value
{   is less than the warning limit, an abnnormal status of
{   SFE$INVALID_MAXIMUM_LIMIT is returned.
{
{   Clear the signature lock on the job routing control table.

  PROCEDURE [XDCL, #GATE] sfp$update_job_maximum_limit
    (    limit_name: ost$name;
         maximum_limit: sft$counter;
     VAR status: ost$status);

    VAR
      limit_chain_entry: ^sft$limit_chain_entry;

    status.normal := TRUE;

    IF sfv$job_routing_control_table = NIL THEN
      osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'Job', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'sfp$update_job_maximum_limit', status);
      RETURN;
    IFEND;

    osp$set_job_signature_lock (sfv$job_routing_control_lock);

    limit_chain_entry := sfp$job_limit_chain_entry (limit_name);

    IF limit_chain_entry <> NIL THEN
      IF maximum_limit >= limit_chain_entry^.limit.job_resource_limit THEN
        limit_chain_entry^.limit.job_abort_limit := maximum_limit;
      ELSE
        osp$set_status_abnormal ('SF', sfe$invalid_maximum_limit, limit_chain_entry^.limit.name, status);
        osp$append_status_integer (osc$status_parameter_delimiter, maximum_limit, 10, FALSE, status);
        osp$append_status_integer (osc$status_parameter_delimiter,
              limit_chain_entry^.limit.job_resource_limit, 10, FALSE, status);
      IFEND;
    ELSE
      osp$set_status_abnormal ('SF', sfe$limit_not_activated, limit_name, status);
    IFEND;

    osp$clear_job_signature_lock (sfv$job_routing_control_lock);

  PROCEND sfp$update_job_maximum_limit;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$update_job_limit_accum', EJECT ??
*copyc sfh$update_job_limit_accum

{ NOTES:
{   This routine tests the signature lock on the job routing control table in
{   order to prevent a deadlock situation.  If the job is interrupted to update
{   the CP time and SRU accumulators while the job routing control table is being
{   accessed for another purpose, a deadlock situation could occur.
{
{   For now, when a job encounters a job maximum limit, the maximum limit will be
{   raised to unlimited.  When NOS/VE supports a "kill" job function, it will be
{   called instead of raisng the job maximum limit.
{
{  DESIGN:
{   If the pointer to the job routing control table is NIL, this routine returns
{   with an abnormal status of SFE$STATISTICS_NOT_AVAILABLE.
{
{   The signature lock for the job routing control table is tested to determine
{   if the table is already locked.  If the table is not locked or if the table is
{   locked by another task, set the signature lock.  Otherwise, this task already
{   has the table locked so the update can proceed.
{
{   The limit chain is searched for an entry with the specified name.  If the
{   entry cannot be found, an abnormal status of SFE$LIMIT_NOT_ACTIVATED is
{   returned.
{
{   For an incremental update, if the increment would cause the accumulator to
{   overflow an abnormal status of SFE$ACCUMULATOR_OVERFLOW is returned (for the
{   system job, the accumulator is reset to zero and the overflow is ignored).
{   Otherwise, the update value is added to the accumulator.
{
{   For replacement update, the update value is stored in the accumulator field.
{
{   If the job is executing in the job class or system prolog/epilog, limits are
{   not enforced.  Otherwise, limits are enforced.
{
{   If SFC$ACCUMULATION_ENFORCEMENT was selected and the maximum limit has been
{   exceeded, the maximum limit will be raised to unlimited.  If the warning limit
{   has been exceeded, a job warning condition is initiated.
{
{   If SFC$OTHER_ENFORCEMENT was selected, a status error will be returned for
{   either case.
{
{   Clear the signature lock on the job routing control table (only if this
{   procedure sets the lock).

  PROCEDURE [XDCL, #GATE] sfp$update_job_limit_accum
    (    limit_name: ost$name;
         update_value: sft$counter;
         update_kind: sft$accumulator_update_kind;
     VAR status: ost$status);

    VAR
      clear_lock_on_exit: boolean,
      ignore_status: ost$status,
      job_mode: jmt$job_mode,
      limit_chain_entry: ^sft$limit_chain_entry,
      lock_status: ost$signature_lock_status;

?? NEWTITLE := 'update_accumulator', EJECT ??

    PROCEDURE update_accumulator
      (    limit_chain_entry: ^sft$limit_chain_entry;
           update_value: sft$counter;
           update_kind: sft$accumulator_update_kind;
       VAR status: ost$status);

      VAR
        overflow: boolean;

      status.normal := TRUE;

      CASE update_kind OF

      = sfc$replacement_update =

        limit_chain_entry^.limit.accumulator := update_value;

      = sfc$incremental_update =

{   If the accumulator will overflow as a result of the this increment, the
{   accumulator is not updated and an abnormal status is returned (unless
{   it occurs in the system job).

        IF limit_chain_entry^.limit.accumulator >= 0 THEN
          overflow := (UPPERVALUE (sft$counter) - limit_chain_entry^.limit.accumulator) <= update_value;
        ELSE
          overflow := (LOWERVALUE (sft$counter) - limit_chain_entry^.limit.accumulator) > update_value;
        IFEND;
        IF overflow THEN
          IF jmp$system_job () THEN
            limit_chain_entry^.limit.accumulator := update_value;
          ELSE
            osp$set_status_abnormal ('SF', sfe$accumulator_overflow, limit_name, status);
          IFEND;
        ELSE
          limit_chain_entry^.limit.accumulator := limit_chain_entry^.limit.accumulator + update_value;
        IFEND;

      ELSE
        osp$set_status_abnormal ('SF', sfe$unknown_update_kind, 'sfp$update_job_limit_accum', status);
      CASEND;

    PROCEND update_accumulator;
?? OLDTITLE ??
?? NEWTITLE := 'check_limit', EJECT ??

    PROCEDURE check_limit
      (    limit_chain_entry: ^sft$limit_chain_entry;
       VAR status: ost$status);

      VAR
        ignore_status: ost$status,
        processing_phase: clt$processing_phase;

      status.normal := TRUE;

{ Skip limit checking for job class epilog and rest of job.

      processing_phase := clc$command_phase;
      clp$get_processing_phase (processing_phase, ignore_status);
      IF processing_phase >= clc$class_epilog_phase THEN
        RETURN;
      IFEND;

      CASE limit_chain_entry^.limit.enforcement OF

      = sfc$accumulation_enforcement =

        IF (limit_chain_entry^.limit.job_abort_limit <> sfc$unlimited) AND
              (limit_chain_entry^.limit.accumulator >= limit_chain_entry^.limit.job_abort_limit) THEN
          osp$set_status_abnormal ('SF', sfe$job_maximum_limit_exceeded, limit_chain_entry^.limit.name,
                status);
        ELSEIF (limit_chain_entry^.limit.job_resource_limit <> sfc$unlimited) AND
              (limit_chain_entry^.limit.accumulator >= limit_chain_entry^.limit.job_resource_limit) THEN
          sfp$initiate_resource_condition (limit_chain_entry, status);
        IFEND;

      = sfc$other_enforcement =

        IF (limit_chain_entry^.limit.job_abort_limit <> sfc$unlimited) AND
              (limit_chain_entry^.limit.accumulator > limit_chain_entry^.limit.job_abort_limit) THEN
          osp$set_status_abnormal ('SF', sfe$job_maximum_limit_exceeded, limit_chain_entry^.limit.name,
                status);
        ELSEIF (limit_chain_entry^.limit.job_resource_limit <> sfc$unlimited) AND
              (limit_chain_entry^.limit.accumulator > limit_chain_entry^.limit.job_resource_limit) THEN
          osp$set_status_abnormal ('SF', sfe$job_warning_limit_exceeded, limit_chain_entry^.limit.name,
                status);
        IFEND;

      ELSE
        osp$set_status_abnormal ('SF', sfe$unknown_enforcement, limit_chain_entry^.limit.name, status);

      CASEND;

    PROCEND check_limit;
?? OLDTITLE, EJECT ??
    status.normal := TRUE;

    IF jmp$system_job () THEN

{ No limits exist in the system job so no action is necessary.

      RETURN;
    IFEND;

    IF sfv$job_routing_control_table = NIL THEN
      osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'Job', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'sfp$update_job_limit_accum', status);
      RETURN;
    IFEND;

    osp$test_signature_lock (sfv$job_routing_control_lock, lock_status);

    IF lock_status <> osc$sls_locked_by_current_task THEN
      osp$set_job_signature_lock (sfv$job_routing_control_lock);
      clear_lock_on_exit := TRUE;
    ELSE
      clear_lock_on_exit := FALSE;
    IFEND;

    limit_chain_entry := sfp$job_limit_chain_entry (limit_name);

    IF limit_chain_entry <> NIL THEN
      update_accumulator (limit_chain_entry, update_value, update_kind, status);
      IF status.normal THEN
        check_limit (limit_chain_entry, status);
        IF (status.condition = sfe$job_warning_limit_exceeded) OR
              (status.condition = sfe$job_maximum_limit_exceeded) THEN
          update_accumulator (limit_chain_entry, -update_value, update_kind, ignore_status);
        IFEND;
      IFEND;
    ELSE
      osp$set_status_abnormal ('SF', sfe$limit_not_activated, limit_name, status);
    IFEND;
    IF clear_lock_on_exit THEN
      osp$clear_job_signature_lock (sfv$job_routing_control_lock);
    IFEND;

    IF (NOT status.normal) AND (status.condition = sfe$job_maximum_limit_exceeded) AND
          (limit_chain_entry^.limit.enforcement = sfc$accumulation_enforcement) THEN
      job_mode := pmf$job_mode ();
      IF (job_mode = jmc$batch) OR (job_mode = jmc$interactive_connected) THEN
        osp$generate_message (status, ignore_status);
      IFEND;

{ Test monitor stats lock and clear if set.

      lock_status := osc$sls_not_locked;
      osp$test_signature_lock (avv$monitor_statistics_lock, lock_status);
      IF lock_status = osc$sls_locked_by_current_task THEN
        osp$clear_job_signature_lock (avv$monitor_statistics_lock);
      IFEND;

      pmv$job_maximum_limit_exceeded := TRUE;
      jmp$logout (status);

{ Set monitor stats lock if we cleared it.

      IF lock_status = osc$sls_locked_by_current_task THEN
        osp$set_job_signature_lock (avv$monitor_statistics_lock);
      IFEND;

    IFEND;

  PROCEND sfp$update_job_limit_accum;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$update_job_warning_limit', EJECT ??
*copyc sfh$update_job_warning_limit

{ NOTES:
{   The warning limit can be changed to any value greater than the current
{   accumulator value and less than or equal to the maximum limit.
{
{ DESIGN:
{   If the pointer to the job routing control table is NIL, this routine returns
{   with an abnormal status of SFE$STATISTICS_NOT_AVAILABLE.  Otherwise a
{   signature lock is set for the job routing control table.
{
{   The limit chain is searched for an entry with the specified name.  If the
{   entry cannot be found, an abnormal status of SFE$LIMIT_NOT_ACTIVATED is
{   returned.
{
{   If the entry is found, the value specified as the new warning limit is
{   compared with the current value of the accumulator and the maximum limit.  If
{   the value of the new warning limit is greater than the current accumulator
{   value and less than or equal to the maximum limit, the warning limit field is
{   updated.  Otherwise, an abnormal status of SFE$INVALID_RESOURCE_LIMIT is
{   returned.
{
{   In addition, if the warning limit value for CP time or SRUs is being changed,
{   AVP$MONITOR_STATISTICS_HANDLER is called to update the accumulators and reset
{   the calculation interval.
{
{   Clear the signature lock on the job routing control table.

  PROCEDURE [XDCL, #GATE] sfp$update_job_warning_limit
    (    limit_name: ost$name;
         warning_limit: sft$counter;
     VAR status: ost$status);

    VAR
      job_warning_checking: boolean,
      limit_chain_entry: ^sft$limit_chain_entry;

    status.normal := TRUE;
    job_warning_checking := TRUE;

    IF sfv$job_routing_control_table = NIL THEN
      osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'Job', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'sfp$update_job_warning_limit', status);
      RETURN;
    IFEND;

    osp$set_job_signature_lock (sfv$job_routing_control_lock);

    limit_chain_entry := sfp$job_limit_chain_entry (limit_name);
    IF limit_chain_entry <> NIL THEN
      IF (limit_chain_entry^.limit.name = avc$cpu_time_limit_name) OR
            (limit_chain_entry^.limit.name = avc$sru_limit_name) THEN
        avp$monitor_statistics_handler (avc$monitor_statistics_flag);
      IFEND;
      IF (limit_chain_entry^.limit.name = avc$pfs_limit_name) AND
            (warning_limit > jmv$jcb.ijle_p^.statistics.perm_file_space) AND
            (warning_limit <= jmv$jcb.perm_file_job_maximum_limit) THEN
        sfp$change_file_space_limit (sfc$perm_file_space_limit, ^warning_limit,
             {job_maximum_limit = } NIL, {accumulator = } NIL, ^job_warning_checking);
      ELSEIF (limit_chain_entry^.limit.name = avc$tfs_limit_name) AND
            (warning_limit > jmv$jcb.ijle_p^.statistics.temp_file_space) AND
            (warning_limit <= jmv$jcb.temp_file_job_maximum_limit) THEN
        sfp$change_file_space_limit (sfc$temp_file_space_limit, ^warning_limit,
             {job_maximum_limit = } NIL, {accumulator = } NIL, ^job_warning_checking);
      ELSEIF (warning_limit > limit_chain_entry^.limit.accumulator) AND
            (warning_limit <= limit_chain_entry^.limit.job_abort_limit) THEN
        limit_chain_entry^.limit.job_resource_limit := warning_limit;
        limit_chain_entry^.cpu_time_of_job_resource_signal := 0;
        IF (limit_chain_entry^.limit.name = avc$cpu_time_limit_name) OR
              (limit_chain_entry^.limit.name = avc$sru_limit_name) THEN
          avp$monitor_statistics_handler (avc$monitor_statistics_flag);
        IFEND;
      ELSE
        osp$set_status_abnormal ('SF', sfe$invalid_warning_limit, limit_chain_entry^.limit.name, status);
        IF warning_limit = sfc$unlimited THEN
          osp$append_status_parameter (osc$status_parameter_delimiter, 'UNLIMITED', status);
        ELSE
          osp$append_status_integer (osc$status_parameter_delimiter, warning_limit, 10, FALSE, status);
        IFEND;
        osp$append_status_integer (osc$status_parameter_delimiter, limit_chain_entry^.limit.accumulator, 10,
              FALSE, status);
        IF limit_chain_entry^.limit.job_abort_limit = sfc$unlimited THEN
          osp$append_status_parameter (osc$status_parameter_delimiter, 'UNLIMITED', status);
        ELSE
          osp$append_status_integer (osc$status_parameter_delimiter,
                limit_chain_entry^.limit.job_abort_limit, 10, FALSE, status);
        IFEND;
      IFEND;
    ELSE
      osp$set_status_abnormal ('SF', sfe$limit_not_activated, limit_name, status);
    IFEND;

    osp$clear_job_signature_lock (sfv$job_routing_control_lock);

  PROCEND sfp$update_job_warning_limit;
?? OLDTITLE ??
MODEND sfm$job_limits_manager;
