?? RIGHT := 110 ??
?? TITLE := 'NOS/VE : Tasking : Task termination' ??
?? NEWTITLE := '  Global declarations', EJECT ??
MODULE pmm$task_termination;

{  PURPOSE:
{    This module contains procedures which direct that portion of task termination which occurs
{    in the ring(s) of the user program.  This consists primarily of activating the debug facility,
{    if appropriate, and activating any block_exit handlers outstanding in the user program.
?? EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc ost$status
*copyc ost$stack_frame_save_area
*copyc oss$job_paged_literal
*copyc pme$debug_exceptions
?? POP ??
*copyc clp$set_processing_phase
*copyc clp$execute_job_epilog
*copyc osp$executing_in_job_monitor
*copyc osp$set_status_from_condition
*copyc osp$set_status_abnormal
*copyc osp$generate_log_message
*copyc osp$system_error
*copyc bap$loaded_ring_cleanup
*copyc bap$monitor_loaded_ring_cleanup
*copyc pmp$establish_condition_handler
*copyc pmp$execute_job_epilogs
*copyc pmp$get_termination_status
*copyc pmp$task_debug_mode_on
*copyc pmp$task_debug_ring
*copyc pmp$pop_all_stack_frames
*copyc pmp$find_end_debug
*copyc pmp$task_state
*copyc pmp$cleanup_loaded_rings
*copyc pmp$condition_task_termination
*copyc pmp$record_program_termination
*copyc pmp$get_termination_status
*copyc pmp$debug_abort_file_specified
*copyc pmp$set_debug_ending
*copyc pmp$load_debug_procedures
*copyc clv$processing_phase

  VAR
    conditions: [STATIC, READ, oss$job_paged_literal] pmt$condition := [pmc$condition_combination,
      $pmt$condition_combination [pmc$system_conditions, mmc$segment_access_condition]];

?? TITLE := '  [XDCL, #GATE] pmp$abort', EJECT ??

  PROCEDURE [XDCL, #GATE] pmp$abort (status: ost$status);

{  PURPOSE:
{    This procedure terminates the executing task.  It is intended that this interface be used
{    to indicate task termination due to some failure internal to the task.
{  NOTE:
{    This procedure must not return to its caller under any circumstances.

    VAR
      established_handler: pmt$established_handler,
      psa: ^ost$minimum_save_area,
      stack_frames_to_pop: boolean,
      local_status: ost$status;

{ The job monitor task should never "abort".  That is, the debugger cannot run
{ in the job monitor task.

    IF osp$executing_in_job_monitor () THEN
      pmp$exit (status);
    ELSE
      pmp$establish_condition_handler (conditions, ^terminate_program_cond_handler, ^established_handler,
            local_status);
      pmp$condition_task_termination;
      IF NOT local_status.normal THEN
        pmp$cleanup_loaded_rings;
      ELSE
        psa := #previous_save_area ();
        stack_frames_to_pop := psa^.a2_previous_save_area <> NIL;
        terminate_program (pmc$program_aborting, stack_frames_to_pop, status);
      IFEND;
      WHILE TRUE DO
      WHILEND;
    IFEND;

  PROCEND pmp$abort;
?? TITLE := '  [XDCL, #GATE] pmp$exit', EJECT ??

  PROCEDURE [XDCL, #GATE] pmp$exit (status: ost$status);

{  PURPOSE:
{    This procedure terminates the executing task.  It is intended that this interface be used
{    to indicate normal task termination or task termination due to some failure external to
{    the task (e.g., bad input parameters).
{  NOTE:
{    This procedure must not return to its caller under any circumstances.

    VAR
      established_handler: pmt$established_handler,
      psa: ^ost$minimum_save_area,
      stack_frames_to_pop: boolean,
      local_status: ost$status;

    pmp$establish_condition_handler (conditions, ^terminate_program_cond_handler, ^established_handler,
          local_status);
    pmp$condition_task_termination;
    IF NOT local_status.normal THEN
      pmp$cleanup_loaded_rings;
    ELSE
      psa := #previous_save_area ();
      stack_frames_to_pop := psa^.a2_previous_save_area <> NIL;
      terminate_program (pmc$program_exiting, stack_frames_to_pop, status);
    IFEND;
    WHILE TRUE DO
    WHILEND;

  PROCEND pmp$exit;
?? TITLE := '  terminate_program', EJECT ??

  PROCEDURE terminate_program (program_termination_mode: pmt$program_termination_mode;
        stack_frames_to_pop: boolean;
        status: ost$status);

{  PURPOSE:
{    This procedure directs the actual task termination for both PMP$EXIT and PMP$ABORT.
{  NOTE:
{    This procedure may be called more than once for a single task and must be reentrant.

    VAR
      end_debug_should_be_called: boolean;

    pmp$record_program_termination (status, program_termination_mode);

    IF stack_frames_to_pop THEN
      pmp$pop_all_stack_frames;
    ELSE
      pmp$end_debug_should_be_called (end_debug_should_be_called);
      IF end_debug_should_be_called THEN
        pmp$call_end_debug;
      IFEND;

      IF (osp$executing_in_job_monitor()) AND
           (clv$processing_phase <> clc$job_end_phase) THEN
        pmp$execute_job_epilogs;
      IFEND;

      pmp$cleanup_loaded_rings;
    IFEND;

  PROCEND terminate_program;
?? TITLE := '  terminate_program_cond_handler', EJECT ??

  PROCEDURE terminate_program_cond_handler (condition: pmt$condition;
        descriptor: ^pmt$condition_information;
        save_area: ^ost$stack_frame_save_area;
    VAR status: ost$status);

    VAR
      message: ost$status,
      ignore_status: ost$status;

    osp$set_status_from_condition ('PM', condition, save_area, message, ignore_status);
    osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], message, ignore_status);
    status.normal := TRUE;
    pmp$cleanup_loaded_rings;

  PROCEND terminate_program_cond_handler;
?? TITLE := '  [XDCL] pmp$end_debug_should_be_called', EJECT ??

  PROCEDURE [XDCL] pmp$end_debug_should_be_called (VAR end_debug_should_be_called: boolean);

    VAR
      of_execution: cell,
      abort_file_specified: boolean;

    pmp$debug_abort_file_specified (abort_file_specified);

    end_debug_should_be_called := (pmp$task_debug_mode_on () OR (abort_file_specified AND (pmp$task_state () =
          pmc$program_aborting))) AND (#ring (^of_execution) >= pmp$task_debug_ring ());


  PROCEND pmp$end_debug_should_be_called;
?? TITLE := '  [XDCL] pmp$call_end_debug', EJECT ??

  PROCEDURE [XDCL] pmp$call_end_debug;

{  PURPOSE:
{    This procedure is responsible for invoking the debug facility at program termination.

    VAR
      termination_status: ost$status,
      end_debug: dbt$end_debug,
      task_state: pmt$task_state,
      local_status: ost$status,
      log_status: ost$status;

    task_state := pmp$task_state ();
    IF (task_state < pmc$debug_ending) THEN
      pmp$set_debug_ending;
      pmp$find_end_debug (end_debug);
      IF end_debug <> NIL THEN
        pmp$get_termination_status (termination_status);
        end_debug^ (task_state = pmc$program_aborting, termination_status);
      ELSE
        osp$set_status_abnormal ('PM', pme$unable_to_load_debug, 'DBP$END_DEBUG', local_status);
        osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, log_status);
      IFEND;
    IFEND;

  PROCEND pmp$call_end_debug;
?? TITLE := '  [XDCL] pmp$loaded_ring_cleanup', EJECT ??

  PROCEDURE [XDCL] pmp$loaded_ring_cleanup;

{  PURPOSE:
{    This procedure exists to drive task cleanup procedures which must be executed in each
{    ring in which the user program executed.

    VAR
      ignored_status: ost$status;

    bap$loaded_ring_cleanup;
    ignored_status.normal := TRUE;
    pmp$exit (ignored_status);

  PROCEND pmp$loaded_ring_cleanup;

?? TITLE := '  [XDCL] pmp$monitor_loaded_ring_cleanup', EJECT ??

  PROCEDURE [XDCL] pmp$monitor_loaded_ring_cleanup;

{  PURPOSE:
{    This procedure exists to drive job monitor task cleanup procedures which must be executed in each
{    ring in which the user program executed.

    VAR
      ignored_status: ost$status;

    bap$monitor_loaded_ring_cleanup;
{  The following procedure call allows pmp$execute_job_epilogs to be re-entrant.
    clp$set_processing_phase (PRED (clv$processing_phase), ignored_status);
    ignored_status.normal := TRUE;
    pmp$exit (ignored_status);

  PROCEND pmp$monitor_loaded_ring_cleanup;

?? TITLE := '  [XDCL] pmp$execute_epilog', EJECT ??

  PROCEDURE [XDCL] pmp$execute_epilog;

{  PURPOSE:
{    This procedure exists to call the procedure clp$execute_job_epilog in the job monitor
{    execution ring.
{

    VAR
      status: ost$status;

    clp$execute_job_epilog;
    status.normal := TRUE;
    pmp$exit (status);

  PROCEND pmp$execute_epilog;

?? TITLE := '  [XDCL] pmp$call_end_handler', EJECT ??

  PROCEDURE [XDCL] pmp$call_end_handler;

{  PURPOSE:
{    This procedure exists to call the task end handler in the
{    ring in which it was established.

*copyc pmt$end_handler
*copyc pmv$end_handler_to_call

    VAR
      end_handler_term_status: ost$status,
      handler_status: ost$status;

    pmp$get_termination_status (end_handler_term_status);

{ Call the end handler.

    handler_status.normal := TRUE;
    pmv$end_handler_to_call^ (end_handler_term_status, handler_status);

    pmp$exit (handler_status);

  PROCEND pmp$call_end_handler;

MODEND pmm$task_termination;
