?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE : Tasking : Child task management' ??
MODULE pmm$child_task_management;

{  PURPOSE:
{    This module contains procedure which allow an executing task to
{    control the execution of its child tasks.

?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc clt$processing_phase
*copyc mme$condition_codes
*copyc osd$virtual_address
*copyc oss$job_pageable
*copyc oss$job_paged_literal
*copyc ost$caller_identifier
*copyc ost$status
*copyc ost$system_flag
*copyc pme$execution_exceptions
*copyc pme$insufficient_privilege
*copyc pme$task_term_while_inhibited
*copyc pmt$task_id
*copyc tmc$wait_times
?? POP ??
*copyc clp$get_processing_phase
*copyc osp$establish_condition_handler
*copyc osp$generate_log_message
*copyc osp$system_error
*copyc osp$set_status_condition
*copyc pmp$exit
*copyc pmp$find_executing_task_tcb
*copyc pmp$find_executing_task_xcb
*copyc pmp$continue_to_cause
*copyc pmp$flag_all_child_tasks
*copyc pmp$long_term_wait
*copyc pmp$set_system_flag
*copyc pmp$verify_current_child
*copyc pmp$get_global_task_id
*copyc pmp$await_task_termination
*copyc pmv$debug_logging_enabled
*copyc pmv$task_execution_phase
*copyc pmv$task_term_inhibit_count
*copyc pmv$task_termination_attempted
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', ??

  VAR
    pmv$job_maximum_limit_exceeded: [XDCL, oss$job_pageable] boolean := FALSE;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$await_task', EJECT ??
*copyc pmh$await_task

{  PURPOSE:
{    This procedure waits until a specified child task of the executing
{    task terminates.

  PROCEDURE [XDCL, #GATE] pmp$await_task
    (    task_id: pmt$task_id;
     VAR wait_complete: boolean;
     VAR status: ost$status);

    VAR
      current_child: boolean;

?? NEWTITLE := 'condition_handler', EJECT ??

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

      CASE condition.selector OF

      = ifc$interactive_condition, jmc$job_resource_condition =
        pmp$verify_current_child (task_id, current_child);
        wait_complete := NOT current_child;
        pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
        EXIT pmp$await_task;

      ELSE
        pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
      CASEND;
    PROCEND condition_handler;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    IF (task_id < LOWERVALUE (pmt$task_id)) OR (task_id > UPPERVALUE (pmt$task_id)) THEN
      osp$set_status_condition (pme$invalid_task_id, status);
    ELSE
      current_child := TRUE;
      osp$establish_condition_handler (^condition_handler, {block_exit} FALSE);
      REPEAT
        pmp$verify_current_child (task_id, current_child);
        wait_complete := NOT current_child;
        IF current_child THEN
          pmp$long_term_wait (tmc$infinite_wait, tmc$infinite_wait);
        IFEND;
      UNTIL NOT current_child;
    IFEND;
  PROCEND pmp$await_task;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$terminate', EJECT ??
*copy pmh$terminate

{ PURPOSE:
{   This procedure terminates a specified child task of the executing task.
{   The executing task is suspended until the specified child task
{   terminates.
{
{ WARNING! This procedure follows the same protocol as pmp$terminate_task_without_wait (below).
{          Any change in this procedure also must be made to pmp$terminate_task_without_wait
{          to keep the procedures in sync.

  PROCEDURE [XDCL, #GATE] pmp$terminate
    (    task_id: pmt$task_id;
     VAR status: ost$status);

    VAR
      current_child: boolean,
      child_gtid: ost$global_task_id,
      local_status: ost$status;

    IF (task_id < LOWERVALUE (pmt$task_id)) OR (task_id > UPPERVALUE (pmt$task_id)) THEN
      osp$set_status_condition (pme$invalid_task_id, status);
    ELSE
      status.normal := TRUE;
      pmp$verify_current_child (task_id, current_child);
      IF NOT current_child THEN
        osp$set_status_condition (pme$task_not_current_child, status);
      ELSE
        pmp$get_global_task_id (task_id, child_gtid, local_status);
        IF NOT local_status.normal THEN
          osp$system_error ('child XCB lost', NIL);
        ELSE
          pmp$set_system_flag (pmc$sf_terminate_task, child_gtid, local_status);
          IF NOT local_status.normal THEN
            IF local_status.condition = pme$unknown_recipient_task THEN

{ If we get to this point, the following has happened.  The child task is gone.
{ The parent has not recognized that fact yet.  The following request will
{ force the parent to recognize that fact when the task goes into wait.

              pmp$await_task_termination (task_id, status);
            ELSE
              osp$system_error ('unexpected abnormal status', ^local_status);
            IFEND;
          ELSE
            pmp$await_task_termination (task_id, status);
          IFEND;
        IFEND;
      IFEND;
    IFEND;

  PROCEND pmp$terminate;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$terminate_task_without_wait', EJECT ??

{ PURPOSE:
{   This procedure terminates a specified child task of the executing task.  The
{   executing task does NOT wait for termination of the child.  It is up to the
{   user of this procedure to check for termination of the child task.
{
{ WARNING! This procedure follows the same protocol as pmp$terminate (above).
{          Any change in this procedure also must be made to pmp$terminate
{          to keep the procedures in sync.

  PROCEDURE [XDCL, #GATE] pmp$terminate_task_without_wait
    (    task_id: pmt$task_id;
     VAR status: ost$status);

    VAR
      current_child: boolean,
      child_gtid: ost$global_task_id,
      local_status: ost$status;

    IF (task_id < LOWERVALUE (pmt$task_id)) OR (task_id > UPPERVALUE (pmt$task_id)) THEN
      osp$set_status_condition (pme$invalid_task_id, status);
    ELSE
      status.normal := TRUE;
      pmp$verify_current_child (task_id, current_child);
      IF NOT current_child THEN
        osp$set_status_condition (pme$task_not_current_child, status);
      ELSE
        pmp$get_global_task_id (task_id, child_gtid, local_status);
        IF NOT local_status.normal THEN
          osp$system_error ('child XCB lost', NIL);
        ELSE
          pmp$set_system_flag (pmc$sf_terminate_task, child_gtid, local_status);
          IF NOT local_status.normal THEN
            IF local_status.condition = pme$unknown_recipient_task THEN
              osp$set_status_condition (pme$task_not_current_child, status);
            ELSE
              osp$system_error ('unexpected abnormal status', ^local_status);
            IFEND;
          IFEND;
        IFEND;
      IFEND;
    IFEND;
  PROCEND pmp$terminate_task_without_wait;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] pmp$terminate_flag_handler', EJECT ??

{  PURPOSE:
{    This procedure is the handler for the system flag pmc$sf_terminate_task.
{    This system flag has a recognition ring of 4.
{
{  NOTE:
{    This procedure is triggered as a consequence of a PMP$TERMINATE request being issued in
{    the parent task of the executing task.

  PROCEDURE [XDCL] pmp$terminate_flag_handler
    (    flag_id: ost$system_flag);

    VAR
      xcb_p: ^ost$execution_control_block,
      local_status: ost$status,
      log_status: ^ost$status,
      processing_phase: clt$processing_phase,
      tcb_p: ^pmt$task_control_block;

{ If the task was terminated because the job file tables are full, abort
{ the task-specifying the reason for the task termination.

    IF flag_id = pmc$sf_terminate_task THEN
      pmp$find_executing_task_xcb (xcb_p);
      IF xcb_p^.ring1_termination_reason = osc$rtr_sft_full THEN
        osp$set_status_condition (mme$job_file_tables_full, local_status);
        pmp$exit (local_status);
      IFEND;
    IFEND;

{ This inhibits termination of child tasks in system epilog processing, and in system prolog
{ processing unless a job maximum limit has been reached.

    clp$get_processing_phase (processing_phase, local_status);
    IF ((processing_phase = clc$system_prolog_phase) AND (NOT pmv$job_maximum_limit_exceeded)) OR
          (processing_phase = clc$system_epilog_phase) THEN
      RETURN;
    IFEND;

    IF flag_id <> pmc$sf_terminate_task THEN
      osp$system_error ('misrouted flag', NIL);
    ELSE

{ If the task is executing and termination inhibit is not selected, the task will be
{ aborted.  If inhibit is selected, the termination attempt is postponed until the
{ inhibit is cleared.  If the task is terminating, the request is ignored.

      IF pmv$task_execution_phase = pmc$task_executing THEN
        IF pmv$task_term_inhibit_count > 0 THEN
          IF pmv$debug_logging_enabled THEN
            osp$set_status_condition (pme$task_term_while_inhibited, local_status);
            PUSH log_status;
            osp$generate_log_message ($pmt$ascii_logset [pmc$system_log, pmc$job_log], local_status,
                  log_status^);
          IFEND;
          pmv$task_termination_attempted := TRUE;
          RETURN;
        IFEND;
        osp$set_status_condition (pme$terminated_by_parent, local_status);
        pmp$exit (local_status);
      ELSE

{ The task is in the process of termination.  If it is a "normal" termination
{ set the termination status to indicate that the task was terminated by its parent.
{ This has the impact of delaying the popping of frames and loaded ring cleanup from being
{ exited due to a terminate task flag.  If a task is going through normal termination,
{ we need to set its termination status abnormal so it understands that its parent expects
{ it to terminate its children and complete termination.  After loaded ring cleanup is
{ complete, the task checks its termination status - if it is abnormal it flags its child
{ tasks - the task then waits for all of its children to termination.

        pmp$find_executing_task_tcb (tcb_p);
        IF (tcb_p^.task_kind = osc$tk_nosve_task) AND tcb_p^.nosve.termination_status^.normal THEN
          osp$set_status_condition (pme$terminated_by_parent, tcb_p^.nosve.termination_status^);
        IFEND;

{ The child tasks will be flagged if the parent is beyond executing any user code, ie. the
{ task execution phase is >= to pmc$task_termination_cleanup.  To execute this code
{ the parent had to be terminated abnormally.  Because of this, it is appropriate to
{ terminate the child tasks.

        IF pmv$task_execution_phase >= pmc$task_termination_cleanup THEN
          pmp$flag_all_child_tasks (pmc$sf_terminate_task, local_status);
        IFEND;
      IFEND;
    IFEND;

  PROCEND pmp$terminate_flag_handler;
?? OLDTITLE ??

MODEND pmm$child_task_management;
