?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Operating System : System Task Management - Ring 1' ??
MODULE osm$system_task_maint_113;

{ PURPOSE:
{   This module contains all procedures that access the system task table.
{
{   The system task table is a system-wide data structure that contains the definition and current status
{   of all system tasks.

?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc clt$parameter_list
*copyc llt$program_description
*copyc osd$virtual_address
*copyc ose$system_task_exceptions
*copyc oss$mainframe_pageable
*copyc ost$name
*copyc ost$system_task_data_criteria
*copyc ost$system_task_display_data
*copyc ost$system_task_work_to_do
*copyc ost$termination_type
*copyc pme$execution_exceptions
*copyc pmt$spy_identifier
*copyc pmt$task_status
?? POP ??
*copyc osp$append_status_parameter
*copyc osp$clear_mainframe_sig_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc osv$mainframe_pageable_heap
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by this Module', EJECT ??

  TYPE
    ost$system_task_table_entry = record
      next_entry: ^ost$system_task_table_entry,
      task_name: ost$name,
      automatic_restart: boolean,
      deactivate_task_option: ost$termination_type,
      idle_task_option: ost$termination_type,
      restart_after_idle: boolean,
      spy_identifier: pmt$spy_identifier,
      execution_ring: ost$valid_ring,
      active: boolean,
      task_has_been_started: boolean,
      task_has_been_terminated: boolean,
      program_description: ^llt$program_description,
      parameters: ^clt$parameter_list,
      task_status: pmt$task_status
    recend;

{ NOTE: It is assumed that the program_description and parameters fields (and the values pointed to)
{       may NOT be changed while task_status.complete is FALSE.

  VAR
    system_task_table_lock: [oss$mainframe_pageable] ost$signature_lock := [0],
    system_task_table: [oss$mainframe_pageable] ^ost$system_task_table_entry := NIL;

?? OLDTITLE ??
?? NEWTITLE := 'find_system_task_entry', EJECT ??

{ PURPOSE:
{   The purpose of this request is to return a pointer to the system task table entry for a specified
{   system task.  A NIL pointer is returned if no entry exists for the task.
{ NOTES:
{   This request assumes the caller has interlocked the system task table.

  PROCEDURE find_system_task_entry
    (    name: ost$name;
     VAR entry: ^ost$system_task_table_entry);

    entry := system_task_table;
    WHILE entry <> NIL DO
      IF entry^.task_name = name THEN
        RETURN;
      IFEND;
      entry := entry^.next_entry;
    WHILEND;

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

{ PURPOSE:
{   The purpose of this request is to update the system task table when a system task is activated.
{ NOTES:
{   When called in the system job monitor task, this request returns the values needed to execute the
{   system task and updates the system task table to indicate that the task has been executed.  It is assumed
{   the caller will execute the system task.

  PROCEDURE [XDCL, #GATE] osp$activate_system_task_r1
    (    name: ost$name;
         system_job_monitor: boolean;
     VAR program_description: ^llt$program_description;
     VAR parameters: ^clt$parameter_list;
     VAR spy_identifier: pmt$spy_identifier;
     VAR execution_ring: ost$valid_ring;
     VAR status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry;

    status.normal := TRUE;
    osp$set_mainframe_sig_lock (system_task_table_lock);
    find_system_task_entry (name, entry);
    IF entry = NIL THEN
      osp$set_status_abnormal ('  ', ose$system_task_not_defined, name, status);
    ELSEIF entry^.active THEN
      osp$set_status_abnormal ('  ', ose$system_task_active, name, status);
    ELSEIF NOT entry^.task_status.complete THEN
      osp$set_status_abnormal ('  ', ose$system_task_still_running, name, status);
    ELSEIF (entry^.task_name = 'CONSOLE_INTERACTION') AND (NOT system_job_monitor) THEN
      osp$set_status_abnormal ('  ', ose$task_not_under_oper_control, name, status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'ACTIVATE_SYSTEM_TASK', status);
    ELSE
      entry^.active := TRUE;
      entry^.task_has_been_started := FALSE;
      IF system_job_monitor THEN

{ The following code must modify the system task table entry in EXACTLY the same way as does
{ osp$scan_system_table when it finds a task to be executed.

        entry^.task_status.complete := FALSE;
        entry^.task_has_been_terminated := FALSE;
        entry^.task_has_been_started := TRUE;
        program_description := entry^.program_description;
        parameters := entry^.parameters;
        spy_identifier := entry^.spy_identifier;
        execution_ring := entry^.execution_ring;
      IFEND;
    IFEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

  PROCEND osp$activate_system_task_r1;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE, UNSAFE] osp$active_system_task_r1', EJECT ??

{ PURPOSE:
{   The purpose of this function is to return a boolean value indicating whether a specified system task
{   is defined and active.

  FUNCTION [XDCL, #GATE, UNSAFE] osp$active_system_task_r1
    (    task_name: ost$name): boolean;

    VAR
      entry: ^ost$system_task_table_entry;

    osp$set_mainframe_sig_lock (system_task_table_lock);
    find_system_task_entry (task_name, entry);
    osp$clear_mainframe_sig_lock (system_task_table_lock);
    IF entry = NIL THEN
      osp$active_system_task_r1 := FALSE;
    ELSE
      osp$active_system_task_r1 := entry^.active;
    IFEND;

  FUNCEND osp$active_system_task_r1;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] osp$deactivate_system_task_r1', EJECT ??

{ PURPOSE:
{   The purpose of this request is to update the system task table when a system task is deactivated.

  PROCEDURE [XDCL, #GATE] osp$deactivate_system_task_r1
    (    name: ost$name;
         system_job_monitor: boolean;
     VAR status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry;

    status.normal := TRUE;
    osp$set_mainframe_sig_lock (system_task_table_lock);
    find_system_task_entry (name, entry);
    IF entry = NIL THEN
      osp$set_status_abnormal ('  ', ose$system_task_not_defined, name, status);
    ELSEIF NOT entry^.active THEN
      osp$set_status_abnormal ('  ', ose$system_task_not_active, name, status);
    ELSEIF entry^.deactivate_task_option = osc$tt_ignore_or_prohibited THEN
      osp$set_status_abnormal ('  ', ose$task_not_under_oper_control, name, status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'DEACTIVATE_SYSTEM_TASK', status);
    ELSE
      entry^.active := FALSE;
      IF system_job_monitor THEN

{ The following code must modify the system task table entry in EXACTLY the same way as does
{ osp$scan_system_table when it finds a task to be terminated.

        entry^.task_has_been_terminated := TRUE;
      IFEND;
    IFEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

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

{ PURPOSE:
{   The purpose of this request is to update the system task table when a system task is defined.

  PROCEDURE [XDCL, #GATE] osp$define_system_task_r1
    (    name: ost$name;
         automatic_restart: boolean;
         deactivate_task_option: ost$termination_type;
         idle_task_option: ost$termination_type;
         restart_after_idle: boolean;
         spy_identifier: pmt$spy_identifier;
         execution_ring: ost$valid_ring;
         program_description: ^llt$program_description;
         parameters: ^clt$parameter_list;
     VAR status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry,
      existing_entry: ^ost$system_task_table_entry;

    status.normal := TRUE;

{ Allocate and initialize a new system task table entry.

    ALLOCATE entry IN osv$mainframe_pageable_heap^;
    entry^.task_name := name;
    entry^.automatic_restart := automatic_restart;
    entry^.deactivate_task_option := deactivate_task_option;
    entry^.idle_task_option := idle_task_option;
    entry^.restart_after_idle := restart_after_idle;
    entry^.spy_identifier := spy_identifier;
    entry^.execution_ring := execution_ring;
    entry^.active := FALSE;
    ALLOCATE entry^.program_description: [[REP (#SIZE (program_description^)) OF cell]] IN
          osv$mainframe_pageable_heap^;
    entry^.program_description^ := program_description^;
    ALLOCATE entry^.parameters: [[REP (#SIZE (parameters^)) OF cell]] IN osv$mainframe_pageable_heap^;
    entry^.parameters^ := parameters^;
    entry^.task_status.complete := TRUE;
    entry^.task_status.status.normal := TRUE;

{ Verify that the system task name is unique and link the new entry into the system task table.

    osp$set_mainframe_sig_lock (system_task_table_lock);
    find_system_task_entry (name, existing_entry);
    IF existing_entry = NIL THEN
      entry^.next_entry := system_task_table;
      system_task_table := entry;
    IFEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

    IF existing_entry <> NIL THEN
      osp$set_status_abnormal ('  ', ose$system_task_already_defined, name, status);
      FREE entry IN osv$mainframe_pageable_heap^;
    IFEND;

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

{ PURPOSE:
{   The purpose of this request is to update the system task table when a system task is deleted.

  PROCEDURE [XDCL, #GATE] osp$delete_system_task_r1
    (    name: ost$name;
     VAR task_executing: boolean;
     VAR status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry,
      ptr_to_entry: ^^ost$system_task_table_entry;

    status.normal := TRUE;
    osp$set_mainframe_sig_lock (system_task_table_lock);

    entry := system_task_table;
    ptr_to_entry := ^system_task_table;
    WHILE (entry <> NIL) AND (entry^.task_name <> name) DO
      ptr_to_entry := ^entry^.next_entry;
      entry := entry^.next_entry;
    WHILEND;

    IF entry = NIL THEN
      osp$set_status_abnormal ('  ', ose$system_task_not_defined, name, status);
    ELSEIF entry^.active THEN
      osp$set_status_abnormal ('  ', ose$system_task_active, name, status);
    ELSEIF entry^.task_status.complete THEN
      task_executing := FALSE;
      ptr_to_entry^ := entry^.next_entry;
    ELSE
      task_executing := TRUE;
    IFEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

    IF status.normal AND (task_executing = FALSE) THEN
      FREE entry IN osv$mainframe_pageable_heap^;
    IFEND;

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

{ PURPOSE:
{   The purpose of this request is to return the names of all executing system tasks.
{ NOTES:
{   This request assumes that it is called while the system is being idled.  The termination status of any
{   system task that was terminated by its parent (the system job monitor task) is changed to indicate that
{   the task was terminated due to a system idle request.  The task will then be restarted when the system
{   is resumed.

  PROCEDURE [XDCL, #GATE] osp$get_running_system_tasks
    (VAR running_tasks: array [1 .. * ] of ost$name;
     VAR number_of_running_tasks: integer);

    VAR
      entry: ^ost$system_task_table_entry;

    number_of_running_tasks := 0;
    osp$set_mainframe_sig_lock (system_task_table_lock);
    entry := system_task_table;
    WHILE entry <> NIL DO
      IF (entry^.idle_task_option = osc$tt_terminate) AND (NOT entry^.task_status.complete) THEN
        number_of_running_tasks := number_of_running_tasks + 1;
        IF number_of_running_tasks <= UPPERBOUND (running_tasks) THEN
          running_tasks [number_of_running_tasks] := entry^.task_name;
        IFEND;
      ELSEIF (entry^.idle_task_option = osc$tt_terminate) AND entry^.task_status.complete AND
            (NOT entry^.task_status.status.normal AND (entry^.task_status.status.condition =
            pme$terminated_by_parent)) THEN
        entry^.task_status.status.condition := ose$terminated_by_idle_system;
      IFEND;
      entry := entry^.next_entry;
    WHILEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

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

{ PURPOSE:
{   The purpose of this request is to return display information for one or all system tasks.
{ NOTES:
{   If display information is requested for all tasks, this request always returns a count of the number of
{   executing system tasks.  This allows the caller to allocate a data structure sufficiently large to hold
{   display information for all tasks.

  PROCEDURE [XDCL, #GATE] osp$get_system_task_data_r1
    (    criteria: ost$system_task_data_criteria;
     VAR system_task_data: ost$system_task_display_data;
     VAR system_task_count: integer;
     VAR status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry;

    status.normal := TRUE;
    system_task_count := 0;
    osp$set_mainframe_sig_lock (system_task_table_lock);
    entry := system_task_table;

  /scan_system_task_table/
    WHILE entry <> NIL DO
      IF (criteria.all_tasks) OR (entry^.task_name = criteria.task_name) THEN
        system_task_count := system_task_count + 1;
        IF system_task_count <= UPPERBOUND (system_task_data) THEN
          system_task_data [system_task_count].task_name := entry^.task_name;
          system_task_data [system_task_count].automatic_restart := entry^.automatic_restart;
          system_task_data [system_task_count].deactivate_task_option := entry^.deactivate_task_option;
          system_task_data [system_task_count].idle_task_option := entry^.idle_task_option;
          system_task_data [system_task_count].restart_after_idle := entry^.restart_after_idle;
          system_task_data [system_task_count].spy_identifier := entry^.spy_identifier;
          system_task_data [system_task_count].execution_ring := entry^.execution_ring;
          system_task_data [system_task_count].active := entry^.active;
          system_task_data [system_task_count].task_status := entry^.task_status;
        IFEND;
        IF NOT criteria.all_tasks THEN
          EXIT /scan_system_task_table/;
        IFEND;
      IFEND;
      entry := entry^.next_entry;
    WHILEND /scan_system_task_table/;
    osp$clear_mainframe_sig_lock (system_task_table_lock);
    IF (NOT criteria.all_tasks) AND (system_task_count = 0) THEN
      osp$set_status_abnormal ('  ', ose$system_task_not_defined, criteria.task_name, status);
    IFEND;

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

{ PURPOSE:
{   The purpose of this request is to scan the system task table to determine whether there is an action that
{   needs to be performed.  If an action is required, it must be performed in ring 3; the request returns a
{   valuing indicating the action to be performed and updates the data structure assuming that the action
{   will be performed.
{ NOTES:
{   This procedure assumes that the program_description and parameters pointers in a system task table entry
{   may not be changed while the value of task_status.complete is FALSE.  This assumption allows these
{   pointers to be (returned to and) used by ring 3 while the system task table is NOT locked.  This
{   assumption is currently satisfied by the fact that a system task table entry cannot be deleted while
{   task_status.complete is FALSE.

  PROCEDURE [XDCL, #GATE] osp$scan_system_task_table
    (VAR work_to_do: ost$system_task_work_to_do);

    VAR
      entry: ^ost$system_task_table_entry;

    work_to_do.kind := osc$st_no_work_to_do;
    work_to_do.log_task_status := FALSE;
    osp$set_mainframe_sig_lock (system_task_table_lock);
    entry := system_task_table;
    WHILE (entry <> NIL) AND (work_to_do.kind = osc$st_no_work_to_do) DO
      IF entry^.active THEN
        IF entry^.task_status.complete THEN
          work_to_do.task_name := entry^.task_name;
          IF (NOT entry^.task_has_been_started) OR (entry^.automatic_restart AND
                (entry^.task_status.status.normal OR (entry^.task_status.status.condition <>
                ose$terminated_by_idle_system))) OR (entry^.restart_after_idle AND
                (NOT entry^.task_status.status.normal AND (entry^.task_status.status.condition =
                ose$terminated_by_idle_system))) THEN

{ Task needs to be (re)started.

            work_to_do.kind := osc$st_execute_task;
            IF entry^.task_has_been_started AND (entry^.task_status.status.normal OR
                  ((entry^.task_status.status.condition <> pme$terminated_by_parent) AND
                  (entry^.task_status.status.condition <> ose$terminated_by_idle_system))) THEN
              work_to_do.log_task_status := TRUE;
              work_to_do.task_status := entry^.task_status.status;
            IFEND;

            entry^.task_status.complete := FALSE;
            entry^.task_has_been_terminated := FALSE;
            work_to_do.first_execution := NOT entry^.task_has_been_started;
            entry^.task_has_been_started := TRUE;
            work_to_do.spy_identifier := entry^.spy_identifier;
            work_to_do.execution_ring := entry^.execution_ring;
            work_to_do.program_description := entry^.program_description;
            work_to_do.parameters := entry^.parameters;

          ELSE

{ Task has completed, but is not to be restarted.  Notify the operator and deactivate the task so that
{ the operator can activate (manually restart) or delete the task.

            work_to_do.kind := osc$st_deactivate_task;
            IF NOT entry^.task_status.status.normal THEN
              work_to_do.log_task_status := TRUE;
              work_to_do.task_status := entry^.task_status.status;
            IFEND;
            entry^.active := FALSE;
          IFEND;
        IFEND;
      ELSE

{ Task has been deactivated.  If the task is still executing and has not already been terminated,
{ then terminate it.

        IF (NOT entry^.task_status.complete) AND (entry^.deactivate_task_option = osc$tt_terminate) AND
              (NOT entry^.task_has_been_terminated) THEN
          work_to_do.kind := osc$st_terminate_task;
          work_to_do.task_name := entry^.task_name;
          entry^.task_has_been_terminated := TRUE;
        IFEND;

      IFEND;
      entry := entry^.next_entry;
    WHILEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

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

{ PURPOSE:
{   The purpose of this request is to update the automatic_restart field in the system task table
{   entry for a specified system task.

  PROCEDURE [XDCL, #GATE] osp$set_system_task_restart
    (    name: ost$name;
         automatic_restart: boolean;
     VAR status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry;

    status.normal := TRUE;
    osp$set_mainframe_sig_lock (system_task_table_lock);
    find_system_task_entry (name, entry);
    IF entry = NIL THEN
      osp$set_status_abnormal ('  ', ose$system_task_not_defined, name, status);
    ELSE
      entry^.automatic_restart := automatic_restart;
    IFEND;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

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

{ PURPOSE:
{   The purpose of this request is to update the system task table when a system task completes
{   execution.  The task's termination status is stored in the table so that it may be displayed and
{   acted upon (by osp$scan_system_task_table).

  PROCEDURE [XDCL, #GATE] osp$store_system_task_status
    (    name: ost$name;
         status: ost$status);

    VAR
      entry: ^ost$system_task_table_entry;

    osp$set_mainframe_sig_lock (system_task_table_lock);
    find_system_task_entry (name, entry);
    IF entry = NIL THEN
      osp$system_error ('System task tables out of synch', NIL);
    IFEND;
    entry^.task_status.status := status;
    entry^.task_status.complete := TRUE;
    osp$clear_mainframe_sig_lock (system_task_table_lock);

  PROCEND osp$store_system_task_status;
?? OLDTITLE ??
MODEND osm$system_task_maint_113;





