?? LEFT := 1, RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Operating System : System idle/resume processing' ??
MODULE osm$idle_resume_system;

{ PURPOSE:
{   This module contains various procedures used to idle, resume and terminate the system.

?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cml$system_continuation
*copyc oss$job_paged_literal
*copyc syc$monitor_request_codes
*copyc tmc$signal_identifiers
*copyc jme$queued_file_conditions
*copyc ofe$error_codes
*copyc ose$system_task_exceptions
*copyc mmt$rb_idle_system
*copyc ost$free_running_clock
*copyc ost$system_flag
*copyc ost$terminate_continue_stats
*copyc syt$180_idle_code
*copyc tmt$rb_update_job_task_enviro
?? POP ??
*copyc avp$system_operator
*copyc clp$include_line
*copyc clp$log_comment
*copyc clp$put_job_command_response
*copyc dfp$flush_served_family_table
*copyc dmp$idle_system
*copyc dmp$resume_system
*copyc dpp$put_critical_message
*copyc dsp$attach_label_for_upgrade
*copyc dsp$attach_rdf_for_idle
*copyc dsp$detach_rdf_after_resume
*copyc dsp$idle_system
*copyc dsp$log_system_message
*copyc dsp$resume_system
*copyc jmp$get_job_ijl_ordinal
*copyc jmp$system_job
*copyc osp$check_sys_task_completions
*copyc osp$copy_local_status_to_status
*copyc osp$establish_condition_handler
*copyc osp$executing_in_job_monitor
*copyc osp$fatal_system_error
*copyc osp$generate_message
*copyc osp$get_cause_of_idle
*copyc osp$get_running_system_tasks
*copyc osp$idle_requested
*copyc osp$jt_begin_system_activity
*copyc osp$jt_end_system_activity
*copyc osp$set_status_abnormal
*copyc osp$terminate_system_r1
*copyc osp$terminate_system_task
*copyc osp$update_idle_state_r1
*copyc osp$verify_system_privilege
*copyc pmp$cause_task_condition
*copyc pmp$continue_to_cause
*copyc pmp$get_date_time_at_timestamp
*copyc pmp$wait
*copyc tmp$ready_system_task1
*copyc i#call_monitor
*copyc dfv$job_recovery_enabled
*copyc mtv$idle_step_message
?? OLDTITLE, NEWTITLE := '[XDCL, #GATE] osp$update_idle_state', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$update_idle_state
    (    idle_state: ost$idle_state;
     VAR status: ost$status);

    status.normal := TRUE;
    IF NOT (jmp$system_job () AND osp$executing_in_job_monitor ()) THEN
      osp$set_status_abnormal ('  ', ose$not_system_job_monitor, 'osp$update_idle_state', status);
      RETURN;
    IFEND;

    osp$update_idle_state_r1 (idle_state);

  PROCEND osp$update_idle_state;
?? OLDTITLE, NEWTITLE := '[XDCL, #GATE] osp$terminate_system', EJECT ??

{  PURPOSE:
{     This procedure initiates the termination of the system.  Once the
{     process has been initiated, the procedure waits for a job recovery
{     condition to occur.  This is necessary to ensure that the
{     executing job is recoverable.
{

  PROCEDURE [XDCL, #GATE] osp$terminate_system
    (VAR status: ost$status);

    CONST
      one_second = 1000;

    VAR
      job_recovery_complete: boolean,
      prolog_status: ost$status;

?? NEWTITLE := '  abort_handler', EJECT ??

    PROCEDURE condition_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      IF (condition.selector = pmc$user_defined_condition) AND (condition.user_condition_name =
            'OSC$JOB_RECOVERY') THEN
        job_recovery_complete := TRUE;
        #SPOIL (job_recovery_complete);
      IFEND;

      pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);

    PROCEND condition_handler;
?? OLDTITLE, EJECT ??
    IF NOT avp$system_operator () THEN
      osp$set_status_abnormal ('OF', ofe$sou_not_active, 'SYSTEM_OPERATION', status);
      RETURN;
    IFEND;

    dpp$put_critical_message ('Initiating system termination sequence', status);
    clp$include_line ('$system.osf$builtin_library.rap$run_initiation_commands icn=system_termination_prolog',
          TRUE, osc$null_name, prolog_status);
    IF NOT prolog_status.normal THEN
      osp$generate_message (prolog_status, status);
    IFEND;

    status.normal := TRUE;
    job_recovery_complete := FALSE;
    #SPOIL (job_recovery_complete);

{ Establish handler for job_recovery condition.

    osp$establish_condition_handler (^condition_handler, FALSE);

    osp$terminate_system_r1;
    tmp$ready_system_task (tmc$stid_job_monitor, {ignore} status);
    status.normal := TRUE;
    IF jmp$system_job () THEN
      RETURN;
    ELSE
{ Wait for job_recovery condition to occur.

      WHILE TRUE DO
        pmp$wait (20 * one_second, 20 * one_second);
        #SPOIL (job_recovery_complete);
        IF job_recovery_complete THEN
          RETURN;
        IFEND;
      WHILEND;
    IFEND;

  PROCEND osp$terminate_system;
?? OLDTITLE, NEWTITLE := '[XDCL, #GATE] osp$idle_resume_system_job', EJECT ??

{  PURPOSE:
{     This procedure is used to initiate the last phase of system idle-down,
{     the idle of the system job.  It is called within the job monitor task
{     of the system job after all other system activity has ceased.
{

  PROCEDURE [XDCL, #GATE] osp$idle_resume_system_job
    (VAR status: ost$status);

    CONST
      ten_milliseconds = 10,
      one_second = 1000,
      thirty_seconds = 30000000;

{ Text is defined below for each possible code.  However, most of the errors will be detected elsewhere
{ and thus only the codes marked "valid" should be seen by the operator.

?? FMT (FORMAT := OFF) ??
    VAR
      table: [STATIC, READ, oss$job_paged_literal] array [syt$180_idle_code] of string (40) := [
{ syc$ic_null                  } 'ERR=VEOS3599- Invalid IDLE CODE=      ',
{ syc$ic_system_terminated     } 'VEOS3500- Termination is complete.',
{ syc$ic_fatal_hardware_error  } 'ERR=VEOS3599- Invalid IDLE CODE=      ',
{ syc$ic_fatal_software_error  } 'ERR=VEOS3599- Invalid IDLE CODE=      ',
{ syc$ic_long_power            } ' ', { Message comes from monitor variable MTV$IDLE_STEP_MESSAGE }
{ syc$ic_hardware_idle         } ' ', { Message comes from monitor variable MTV$IDLE_STEP_MESSAGE }
{ syc$ic_idle_command          } 'VEOS3503- SYSTEM IDLED',
{ syc$ic_step_command          } 'ERR=VEOS3599- Invalid IDLE CODE=      ',
{ syc$ic_short_power           } 'ERR=VEOS3599- Invalid IDLE CODE=      ',
{ syc$ic_disk_error            } 'ERR=VEOS3599- Invalid IDLE CODE=      ',
{ syc$ic_software_breakpoint   } 'ERR=VEOS3599- Invalid IDLE CODE=      '];
?? FMT (FORMAT := ON) ??

    VAR
      change_sys_tasks_request_block: tmt$rb_update_job_task_enviro,
      endtime: integer,
      i: integer,
      idle_code: syt$180_idle_code,
      line: string (32),
      logs: array [1 .. 1] of ost$name,
      number_of_running_tasks: integer,
      request_block: mmt$rb_idle_system,
      running_tasks: ^array [1 .. * ] of ost$name,
      tasks_terminated: boolean;

    logs [1] := 'SYSTEM                         ';

    IF NOT (jmp$system_job () AND osp$executing_in_job_monitor ()) THEN
      osp$set_status_abnormal ('  ', ose$not_system_job_monitor, 'osp$idle_resume_system_job', status);
      RETURN;
    IFEND;

    IF dfv$job_recovery_enabled THEN
      dfp$flush_served_family_table ({ignore} status);
      status.normal := TRUE;
    IFEND;

{ Wait until all tasks which must terminate do so, or until thirty (30) seconds has elapsed.

    endtime := #FREE_RUNNING_CLOCK (0) + thirty_seconds;
    tasks_terminated := FALSE;
    PUSH running_tasks: [1 .. 1];

  /wait_for_tasks_to_complete/
    REPEAT
      osp$check_sys_task_completions;
      osp$get_running_system_tasks (running_tasks^, number_of_running_tasks);
      IF number_of_running_tasks > UPPERBOUND (running_tasks^) THEN
        PUSH running_tasks: [1 .. number_of_running_tasks];
        CYCLE /wait_for_tasks_to_complete/;
      ELSEIF number_of_running_tasks = 0 THEN
        EXIT /wait_for_tasks_to_complete/;
      ELSEIF NOT tasks_terminated THEN
        tasks_terminated := TRUE;
        FOR i := 1 TO number_of_running_tasks DO
          osp$terminate_system_task (running_tasks^ [i]);
        FOREND;
      IFEND;
      pmp$wait (one_second, one_second);
    UNTIL (#FREE_RUNNING_CLOCK (0) > endtime) OR (number_of_running_tasks = 0);

{ If there are still tasks running, inform the system log and the operator, and then wait for the
{ tasks to complete.

    IF number_of_running_tasks <> 0 THEN
      clp$log_comment ('The system was delayed during idle-down because', logs, status);
      clp$log_comment ('a system task would not terminate:', logs, status);
      clp$put_job_command_response (' ** System idle delayed due to a hung task: **', status);

      FOR i := 1 TO number_of_running_tasks DO
        clp$log_comment (running_tasks^ [i], logs, status);
        line := ' ';
        line (2, 31) := running_tasks^ [i];
        clp$put_job_command_response (line, status);
      FOREND;

      REPEAT
        osp$check_sys_task_completions;
        osp$get_running_system_tasks (running_tasks^, number_of_running_tasks);
        pmp$wait (2 * one_second, 2 * one_second);
      UNTIL (#FREE_RUNNING_CLOCK (0) > endtime) OR (number_of_running_tasks = 0);
    IFEND;

    osp$get_cause_of_idle (idle_code);
    CASE idle_code OF
    = syc$ic_system_terminated =
      clp$log_comment ('System idling due to TERMINATE_SYSTEM command', logs, status);
    = syc$ic_idle_command =
      clp$log_comment ('System idling due to IDLE_SYSTEM command', logs, status);
    = syc$ic_hardware_idle, syc$ic_long_power =
      clp$log_comment ('System idling due to hardware idle or long power', logs, status);
    ELSE
      clp$log_comment ('System idling due to fatal software error', logs, status);
    CASEND;

{ Call monitor to idle the other system tasks: Memory_link, etc., but NOT Device_Management.

    osp$jt_begin_system_activity;
    REPEAT
      change_sys_tasks_request_block.reqcode := syc$rc_update_job_task_enviro;
      change_sys_tasks_request_block.status.normal := TRUE;
      change_sys_tasks_request_block.subcode := tmc$ujte_idle_other_sys_tasks;
      i#call_monitor (#LOC (change_sys_tasks_request_block), #SIZE (change_sys_tasks_request_block));
      IF NOT change_sys_tasks_request_block.status.normal THEN
        pmp$wait (ten_milliseconds, ten_milliseconds);
      IFEND;
    UNTIL change_sys_tasks_request_block.status.normal;

{ Log the time and reason for the idle (or terminate).

    IF idle_code = syc$ic_system_terminated THEN
      osp$log_idle_resume (osc$terminate_statistic, idle_code, status);
    ELSE
      osp$log_idle_resume (osc$idle_statistic, idle_code, status);
    IFEND;

    dsp$attach_label_for_upgrade;
    dsp$attach_rdf_for_idle;
    dmp$idle_system;
    dsp$idle_system (idle_code = syc$ic_system_terminated);

{ At this point the system is idle, but not stepped. Update the status of the system.

    osp$update_idle_state (osc$system_idle, status);

{ Call monitor to process the STEP portion of IDLE_SYSTEM.

    request_block.reqcode := syc$rc_idle_system;
    request_block.idle_code := idle_code;

    CASE idle_code OF
    = syc$ic_system_terminated, syc$ic_idle_command =
      request_block.error_message := table [idle_code];
    = syc$ic_hardware_idle, syc$ic_long_power =
      request_block.error_message := mtv$idle_step_message (1, 71);
    ELSE
      request_block.error_message := table [idle_code];
      STRINGREP (request_block.error_message (33, 3), i, idle_code: 3);
      request_block.idle_code := syc$ic_fatal_software_error;
      i#call_monitor (#LOC (request_block), #SIZE (request_block));
    CASEND;

{ If system conditions are such that we don't need to idle (e.g. a long warning which did not
{ complete before a short warning occured, both of which cleared) we can resume immediately.

    IF osp$idle_requested () THEN
      i#call_monitor (#LOC (request_block), #SIZE (request_block));
    IFEND;

{ At this point the system is ready to resume. Update the status of the system.

    osp$update_idle_state (osc$resume_system_in_progress, status);

    dsp$resume_system;
    dmp$resume_system;
    dsp$detach_rdf_after_resume;

{ Call monitor to restart the system tasks which were previously idled in this procedure.

    change_sys_tasks_request_block.reqcode := syc$rc_update_job_task_enviro;
    change_sys_tasks_request_block.status.normal := TRUE;
    change_sys_tasks_request_block.subcode := tmc$ujte_restart_other_systasks;
    i#call_monitor (#LOC (change_sys_tasks_request_block), #SIZE (change_sys_tasks_request_block));
    osp$jt_end_system_activity;

  PROCEND osp$idle_resume_system_job;
?? OLDTITLE, NEWTITLE := '[XDCL, #GATE] osp$log_idle_resume', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$log_idle_resume
    (    statistic_type: ost$terminate_continue_stats;
         idle_code: syt$180_idle_code;
     VAR status: ost$status);

    VAR
      data_to_log_p: ^SEQ ( * ),
      terminate_continue_message: ost$terminate_continue_record;

    osp$verify_system_privilege;

    pmp$get_date_time_at_timestamp (#FREE_RUNNING_CLOCK (0), pmc$use_system_local_time,
          terminate_continue_message.date_time, status);

    terminate_continue_message.log_reason := idle_code;

    terminate_continue_message.log_statistic := statistic_type;

    CASE statistic_type OF
    = osc$idle_statistic =
      terminate_continue_message.log_message.value := 'SYSTEM IDLE';
      terminate_continue_message.log_message.size := 11;
    = osc$resume_statistic =
      terminate_continue_message.log_message.value := 'SYSTEM RESUME';
      terminate_continue_message.log_message.size := 13;
    = osc$terminate_statistic =
      terminate_continue_message.log_message.value := 'SYSTEM TERMINATE';
      terminate_continue_message.log_message.size := 16;
    ELSE
      ;
    CASEND;

    data_to_log_p := #SEQ (terminate_continue_message);
    dsp$log_system_message (cml$system_continuation, data_to_log_p, status);

  PROCEND osp$log_idle_resume;
?? OLDTITLE, NEWTITLE := '[XDCL, #GATE] osp$unstep_resume_flag_handler', EJECT ??
*copyc osh$unstep_resume_flag_handler

  PROCEDURE [XDCL, #GATE] osp$unstep_resume_flag_handler
    (    flag_id: ost$system_flag);

    VAR
      status: ost$status,
      log_name_selections: array [1 .. 1] of ost$name;

    osp$verify_system_privilege;

    log_name_selections [1] := 'JOB                            ';
    clp$log_comment (' *** SYSTEM UNSTEP/RESUME CONDITION OCCURRED. ***', log_name_selections, status);
    pmp$cause_task_condition ('SYSTEM_UNSTEP_RESUME           ', NIL, FALSE, FALSE, FALSE, TRUE, status);

  PROCEND osp$unstep_resume_flag_handler;
?? OLDTITLE ??
MODEND osm$idle_resume_system;
