?? NEWTITLE := 'NOS/VE Job Management : job scheduler monitor mode' ??
MODULE jmm$job_scheduler_monitor_mode;
?? RIGHT := 110 ??

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc oss$mainframe_wired
*copyc jmc$sched_profile_deadstart_id
*copyc jme$job_scheduler_conditions
*copyc jmk$keypoints
*copyc jmt$ajl_ordinal
*copyc jmt$change_dispatching_list
*copyc jmt$dispatching_control
*copyc jmt$dispatching_control_index
*copyc jmt$dual_state_priority_control
*copyc jmt$idle_dispatching_controls
*copyc jmt$ijl_entry_status_statistics
*copyc jmt$ijl_swap_status
*copyc jmt$initiated_job_list_entry
*copyc jmt$job_category_set
*copyc jmt$job_counts
*copyc jmt$job_sched_serv_class_stats
*copyc jmt$job_scheduler_event
*copyc jmt$job_scheduler_statistics
*copyc jmt$job_scheduler_table
*copyc jmt$long_wait_swap_threshold
*copyc jmt$rb_scheduler_requests
*copyc jmt$rb_service_class_statistics
*copyc jmt$service_class_entry
*copyc jmt$service_class_index
*copyc jmt$service_class_set
*copyc jmt$ssn_sequence_number
*copyc jmt$swapin_candidate_q_header
*copyc jmt$swapout_reasons
*copyc jmt$system_supplied_name
*copyc jmt$system_supplied_name_mask
*copyc jmt$trick_ijlo_variant_record
*copyc jmt$working_set_size
*copyc jst$ijl_swap_queue_link
*copyc mmt$page_frame_index
*copyc ost$free_running_clock
*copyc ost$hardware_subranges
*copyc syt$monitor_status
*copyc tmt$dispatching_control_sets
*copyc tmt$dispatching_controls
*copyc tmt$dispatching_prio_controls
*copyc tmt$fnx_search_type
*copyc tmt$new_ptl_lock
?? POP ??
*copyc jmf$ijle_p
*copyc jmp$calculate_service
*copyc jmp$change_ijl_entry_status
*copyc jmp$find_jsn
{copyc jmp$get_ijle_p
*copyc jsp$monitor_advance_swap
*copyc jsp$monitor_swap_in
*copyc jsp$monitor_swap_out
*copyc jsp$relink_swap_queue
*copyc mmp$nudge_periodic_call
*copyc mtp$error_stop
*copyc mtp$set_status_abnormal
*copyc osp$fetch_locked_variable
*copyc osp$set_locked_variable
*copyc tmp$calculate_dct_priority_int
*copyc tmp$clear_lock
*copyc tmp$free_unrecovered_tasks
*copyc tmp$monitor_ready_system_task
*copyc tmp$new_clear_lock
*copyc tmp$new_set_lock
*copyc tmp$reset_dispatching_control
*copyc tmp$set_lock
*copyc tmp$update_job_task_environment
*copyc jmv$ajl_p
*copyc jmv$ijl_p
*copyc jmv$max_ajl_ordinal_in_use
*copyc jmv$number_free_ajl_entries
*copyc jmv$swap_jobs_in_long_wait
*copyc jmv$system_ajl_ordinal
*copyc jmv$system_ijl_ordinal
*copyc jsv$ijl_swap_queue_list
*copyc mmv$reduce_jws_for_thrashing
*copyc tmv$cpu_execution_statistics
*copyc tmv$dispatch_priority_integer
*copyc tmv$dispatching_control_sets
*copyc tmv$dispatching_control_time
*copyc tmv$dispatching_controls
*copyc tmv$new_ptl_lock
*copyc tmv$ptl_lock
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    jmv$classes_in_maxaj_limit_wait: [XDCL, #GATE] jmt$service_class_set := $jmt$service_class_set [],
    jmv$classes_in_resource_wait: [XDCL, #GATE] jmt$service_class_set := $jmt$service_class_set [],
    jmv$change_dispatching_list: [XDCL, #GATE, oss$mainframe_wired] jmt$change_dispatching_list := [[0], NIL],
    jmv$idle_dispatching_controls: [XDCL, #GATE, oss$mainframe_wired] jmt$idle_dispatching_controls,
    jmv$ijl_entry_status_statistics: [XDCL, #GATE, oss$mainframe_wired] jmt$ijl_entry_status_statistics,

{ NOTE:  Because jmv$ijl_ready_task_list is read/written by both job mode and monitor mode scheduler,
{ it is a locked variable and can be referenced only via the compare_swap procedures.

    jmv$ijl_ready_task_list: [XDCL, #GATE, oss$mainframe_wired] integer,
    jmv$job_counts: [XDCL, #GATE] jmt$job_counts,
    jmv$job_scheduler_event: [XDCL, #GATE, oss$mainframe_wired] jmt$job_scheduler_event := [REP 19 of FALSE],
    jmv$job_sched_events_selected: [XDCL, #GATE, oss$mainframe_wired] jmt$job_sched_event_selections :=
          [TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
          FALSE, FALSE, FALSE],

    jmv$job_scheduler_table: [XDCL, #GATE, oss$mainframe_wired] jmt$job_scheduler_table :=
          [40000, FALSE, 60, jmc$sched_profile_deadstart_id, 10, [REP 8 of [0, 100, FALSE]], 1, [[1, 8],
          [1, 8], [2, 8], [2, 8], [3, 8], [3, 8], [4, 8], [4, 8], [5, 8], [5, 8]], 360000000, [], [],
          [20, 60], NIL, 0],

    jmv$last_service_calc_time: [XDCL, #GATE, oss$mainframe_wired] ost$free_running_clock := 0,
    jmv$long_wait_swap_threshold: [XDCL, #GATE] jmt$long_wait_swap_threshold,
    jmv$max_class_working_set: [XDCL, #GATE] jmt$working_set_size := 3000,
    jmv$max_service_class_in_use: [XDCL, #GATE] jmt$service_class_index,

{Dont update THINK TIME if estimated think time is less than this value.
    jmv$min_think_time: [XDCL, #GATE] integer := 500000,

{THINK TIMEs > this value are rounded to this value.
    jmv$max_think_time: [XDCL, #GATE] integer := 60000000,
    jmv$memory_needed_by_scheduler: [XDCL, #GATE] mmt$page_frame_index,
    jmv$null_ijl_ordinal: [XDCL, #GATE] jmt$ijl_ordinal := [0, 0],
    jmv$prevent_activation_of_jobs: [XDCL, #GATE] boolean := TRUE,
    jmv$scan_idle_dispatch_interval: [XDCL, #GATE] integer := 15000000,
    jmv$sched_profile_is_loading: [XDCL, #GATE, oss$mainframe_wired] boolean := FALSE,
    jmv$sched_service_calc_time: [XDCL, #GATE] ost$free_running_clock,
    jmv$service_class_stats_lock: [XDCL] tmt$new_ptl_lock := [FALSE, 0],
    jmv$service_classes: [XDCL, #GATE, oss$mainframe_wired] array [jmt$service_class_index] of
          ^jmt$service_class_entry := [REP jmc$maximum_service_classes + 1 of NIL],
    jmv$ssn_previous_sequence: [XDCL, #GATE] jmt$ssn_sequence_number,
    jmv$subsystem_priority_changes: [XDCL, #GATE] packed array [jmt$service_class_index] of boolean,
    jmv$swapin_candidate_queue: [XDCL, #GATE] array [jmt$service_class_index] of
          jmt$swapin_candidate_q_header,
    jmv$swapped_idle_disp_count: [XDCL] integer := 0,
    jmv$system_supplied_name: [XDCL, #GATE] jmt$system_supplied_name_mask,
    jmv$time_to_wake_scheduler: [XDCL, #GATE] ost$free_running_clock;

?? TITLE := 'check_for_class_switch', EJECT ??

  PROCEDURE [INLINE] check_for_class_switch
    (    ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      new_class: jmt$service_class_index,
      rb: jmt$rb_scheduler_requests,
      service_class_p: ^jmt$service_class_attributes;

{ Change the job's service class if the job has reached the class service threshold.
{ Only switch classes if the new class to switch to is currently defined.

    service_class_p := ^jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes;
    IF (ijle_p^.job_scheduler_data.service_accumulator > service_class_p^.class_service_threshold) AND
          (service_class_p^.class_service_threshold <> jmc$unlimited_service_accum) AND
          (NOT jmv$sched_profile_is_loading) THEN
      IF ijle_p^.job_scheduler_data.service_class <> service_class_p^.next_service_class_index THEN
        new_class := service_class_p^.next_service_class_index;
        IF (jmv$service_classes [new_class] <> NIL) AND jmv$service_classes [new_class]^.attributes.
              defined THEN
          rb.reqcode := syc$rc_job_scheduler_request;
          rb.sub_reqcode := jmc$src_class_switch;
          rb.system_supplied_name := ijle_p^.system_supplied_name;
          rb.new_service_class := new_class;
          rb.new_service_accumulator := 0;
          rb.old_service_class := ijle_p^.job_scheduler_data.service_class;
          rb.old_service_accumulator := ijle_p^.job_scheduler_data.service_accumulator;

          jmp$process_class_switch (rb);

        IFEND;
      IFEND;
    IFEND;

  PROCEND check_for_class_switch;

?? TITLE := 'insert_job_in_ready_task_list', EJECT ??

{ PURPOSE:
{   This procedure inserts a job with a ready task into the list for job mode
{   scheduler to process.
{ DESIGN:
{   The head of the list is a global variable which must be changed by both
{   monitor mode and job mode scheduler.  To synchronize the monitor/job mode
{   references, the head of the list is a locked variable which can be referenced
{   only via the #compare_swap procedures.

  PROCEDURE [INLINE] insert_job_in_ready_task_list
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      ijlo: jmt$trick_ijlo_variant_record,
      list_head: jmt$trick_ijlo_variant_record,
      old_list_head: integer,
      succeeded: boolean;

    ijlo.ijl_ordinal := ijl_ordinal;

    REPEAT
      osp$fetch_locked_variable (jmv$ijl_ready_task_list, list_head.ijl_integer);
      ijle_p^.job_scheduler_data.ready_task_link := list_head.ijl_ordinal;
      old_list_head := list_head.ijl_integer;
      osp$set_locked_variable (jmv$ijl_ready_task_list, old_list_head, ijlo.ijl_integer,
            list_head.ijl_integer, succeeded);
    UNTIL succeeded;

  PROCEND insert_job_in_ready_task_list;

?? TITLE := 'remove_class_from_maxaj_limit', EJECT ??

  PROCEDURE [INLINE] remove_class_from_maxaj_limit
    (    service_class: jmt$service_class_index);

    VAR
      ignore_status: syt$monitor_status;

    jmv$classes_in_maxaj_limit_wait := jmv$classes_in_maxaj_limit_wait - $jmt$service_class_set
          [service_class];
    jmv$job_scheduler_event [jmc$scheduler_wake_time] := TRUE;
    jmv$job_scheduler_event [jmc$examine_input_queue] := TRUE;
    jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
    tmp$monitor_ready_system_task (tmc$stid_job_scheduler, ignore_status);

  PROCEND remove_class_from_maxaj_limit;

?? OLDTITLE ??
?? NEWTITLE := '[INLINE, UNSAFE] swapin_queue_empty', EJECT ??

{ PURPOSE:
{   This function is called when determining if a monitor swapin should be allowed to take place.
{   If the input dispatching priority is blocked, or if there are queued jobs with an equal or higher
{   dispatching priority, FALSE will be returned.

  FUNCTION [INLINE, UNSAFE] swapin_queue_empty
    (    dispatching_priority: jmt$dispatching_priority): boolean;

    VAR
{     ijle_p: ^jmt$initiated_job_list_entry,
      service_class: jmt$service_class_index;

    swapin_queue_empty := TRUE;

    IF jmv$idle_dispatching_controls.controls [dispatching_priority].blocked THEN
      swapin_queue_empty := FALSE;
      RETURN; {----->
    IFEND;

  /check_swapin_queue/
    FOR service_class := jmc$system_service_class TO jmv$max_service_class_in_use DO
      IF (jmv$swapin_candidate_queue [service_class].swapin_candidate_queue <> jmv$null_ijl_ordinal) AND
            (NOT too_many_active_jobs_for_class (service_class)) THEN
{       jmp$get_ijle_p (jmv$swapin_candidate_queue [service_class].swapin_candidate_queue, ijle_p);
        IF jmf$ijle_p (jmv$swapin_candidate_queue [service_class].swapin_candidate_queue) ^.
              scheduling_dispatching_priority >= dispatching_priority THEN
          swapin_queue_empty := FALSE;
          EXIT /check_swapin_queue/; {----->
        IFEND;
      IFEND;
    FOREND /check_swapin_queue/;

  FUNCEND swapin_queue_empty;

?? TITLE := '[INLINE] too_many_active_jobs_for_class', EJECT ??

  FUNCTION [INLINE] too_many_active_jobs_for_class
    (    service_class: jmt$service_class_index): boolean;

    too_many_active_jobs_for_class := (jmv$job_counts.service_class_counts [service_class].
          scheduler_initiated_jobs - jmv$job_counts.service_class_counts [service_class].swapped_jobs) >=
          jmv$service_classes [service_class]^.attributes.maximum_active_jobs;

  FUNCEND too_many_active_jobs_for_class;

?? TITLE := '[XDCL] jmp$activate_job_mode_swapper', EJECT ??

  PROCEDURE [XDCL] jmp$activate_job_mode_swapper;

    VAR
      status: syt$monitor_status;

    jmp$set_scheduler_event (jmc$call_job_swapper);

  PROCEND jmp$activate_job_mode_swapper;

?? TITLE := 'jmp$change_dispatching_alloc', EJECT ??

{ PURPOSE:
{   This procedure changes the dispatching allocation controls in dispatcher's
{   tables.
{ DESIGN:
{   The scheduler table has been changed in job mode.  Dispatcher's tables must
{   be changed in mtr mode with the PTL lock set so that task switch cannot be
{   referencing the tables.  The scheduler table is kept in units of seconds for
{   the time interval and percentages for the minimum and maximum values; those
{   values must all be converted to microseconds for the dispatching table.
{   This procedure is called infrequently (only when a site is changing its
{   dispatching allocation controls).

  PROCEDURE jmp$change_dispatching_alloc;

    CONST
      u_second = 1000000;

    VAR
      controls_defined: boolean,
      dp: jmt$user_dispatching_priority,
      dp_unblocked: boolean,
      local_set: tmt$dispatching_control_sets,
      normalized_interval: integer,
      unblock_higher_dp: boolean;

{ Decide if controls are being defined; the site may be setting controls back to defaults
{ (0% minimum and 100% maximum).

    controls_defined := FALSE;
    dp_unblocked := FALSE;

  /check_controls/
    FOR dp := jmc$priority_p1 TO jmc$priority_p8 DO
      IF (jmv$job_scheduler_table.cpu_dispatching_allocation [dp].minimum <> 0) OR
            (jmv$job_scheduler_table.cpu_dispatching_allocation [dp].maximum <> 100) THEN
        controls_defined := TRUE;
        EXIT /check_controls/; {----->
      IFEND;
    FOREND /check_controls/;

{ Set the PTL lock while changing the dispatching tables.

    tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );

{ Reset the dispatching control sets.  Reset the minimums_to_satisfy field in the dispatching table
{ (it is used in mmp$periodic to determine if a dispatching priority is blocked).
{ NOTE:  'System' priorities always have minimums_to_satisfy.  This guarantees that they will always be
{ in the first set considered by task selection in task switch.
{ If controls are not defined, clear the controls_defined field in the dispatching table.  Other fields
{ in the dispatching table can be left with 'garbage' in them; nothing references them when
{ controls_defined is FALSE.
{ If controls are defined, reset the values in the dispatching table.
{ NOTE:  Elements in dispatching priority sets are converted so that the highest priority in the set
{ is the leftmost bit in the set.  Setting bit 1 in a dispatching priority set is adding priority 15
{ to the set.  (See jmt$dispatching_priority.)

    tmv$dispatching_control_sets.minimums_to_satisfy := $jmt$dispatching_priority_set [1, 2, 3, 4, 5, 6];
    tmv$dispatching_control_sets.maximums_exceeded := $jmt$dispatching_priority_set [];
    tmv$dispatching_control_sets.enforce_maximums := $jmt$dispatching_priority_set [];
    tmv$dispatching_controls.minimums_to_satisfy := $jmt$dispatching_priority_set [1, 2, 3, 4, 5, 6];

    IF NOT controls_defined THEN
      tmv$dispatching_controls.controls_defined := FALSE;
    ELSE
      tmv$dispatching_controls.controls_defined := TRUE;
      tmv$dispatching_controls.maximums_defined := $jmt$dispatching_priority_set [];
      tmv$dispatching_controls.enforce_maximums := $jmt$dispatching_priority_set [];
      tmv$dispatching_controls.controls.time_left_in_interval :=
            jmv$job_scheduler_table.dispatching_allocation_interval * u_second;
      normalized_interval := tmv$dispatching_controls.controls.time_left_in_interval DIV 100;
      unblock_higher_dp := FALSE;
      FOR dp := jmc$priority_p1 TO jmc$priority_p8 DO

{ Set the minimums.

        IF jmv$job_scheduler_table.cpu_dispatching_allocation [dp].minimum <> 0 THEN
          unblock_higher_dp := TRUE;
          tmv$dispatching_controls.controls.dispatching_priority_time [dp].
                minimum_time := (normalized_interval) * jmv$job_scheduler_table.
                cpu_dispatching_allocation [dp].minimum;
          tmv$dispatching_controls.minimums_to_satisfy := tmv$dispatching_controls.minimums_to_satisfy +
                $jmt$dispatching_priority_set [jmc$dp_conversion - dp];
        ELSE
          tmv$dispatching_controls.controls.dispatching_priority_time [dp].minimum_time := 0;
        IFEND;

{ If this dispatching priority is blocked and this or a lower dispatching priority has a minimum allocated,
{ this priority must be unblocked.

        IF unblock_higher_dp AND (jmv$idle_dispatching_controls.controls [dp].blocked OR
              jmv$idle_dispatching_controls.controls [dp].idle_noticed_once) THEN
          jmv$idle_dispatching_controls.controls [dp].blocked := FALSE;
          jmv$idle_dispatching_controls.controls [dp].idle_noticed_once := FALSE;
          jmv$idle_dispatching_controls.controls [dp].timestamp := #FREE_RUNNING_CLOCK (0);
          jmv$idle_dispatching_controls.controls [dp].last_cp_time :=
                tmv$cpu_execution_statistics [dp].time_spent_in_job_mode +
                tmv$cpu_execution_statistics [dp].time_spent_in_mtr_mode;
          jmv$idle_dispatching_controls.unblocked_priorities :=
                jmv$idle_dispatching_controls.unblocked_priorities +
                $jmt$dispatching_priority_set [jmc$dp_conversion - dp];
          dp_unblocked := TRUE;
        IFEND;

{ Set the maximums.

        IF jmv$job_scheduler_table.cpu_dispatching_allocation [dp].maximum <> 100 THEN
          tmv$dispatching_controls.controls.dispatching_priority_time [dp].
                maximum_time := (normalized_interval) * jmv$job_scheduler_table.
                cpu_dispatching_allocation [dp].maximum;
          tmv$dispatching_controls.maximums_defined := tmv$dispatching_controls.maximums_defined +
                $jmt$dispatching_priority_set [jmc$dp_conversion - dp];
        ELSE
          tmv$dispatching_controls.controls.dispatching_priority_time [dp].maximum_time :=
                tmv$dispatching_controls.controls.time_left_in_interval;
        IFEND;
        IF jmv$job_scheduler_table.cpu_dispatching_allocation [dp].enforce_maximum THEN
          tmv$dispatching_controls.enforce_maximums := tmv$dispatching_controls.enforce_maximums +
                $jmt$dispatching_priority_set [jmc$dp_conversion - dp];
        IFEND;
      FOREND;
      tmv$dispatching_control_sets.minimums_to_satisfy := tmv$dispatching_controls.minimums_to_satisfy;
      tmv$dispatching_control_time := tmv$dispatching_controls.controls;
    IFEND;

{ Calculate the dispatching priority integers used by task switch and ready task to determine
{ which dispatching priority is the highest.

    tmp$calculate_dct_priority_int;

    local_set := tmv$dispatching_control_sets;

    FOR dp := jmc$priority_p1 TO jmc$priority_p8 DO
      local_set.ready_tasks := $jmt$dispatching_priority_set [jmc$dp_conversion - dp];
      local_set.minimums_to_satisfy := local_set.minimums_to_satisfy * local_set.ready_tasks;
      local_set.ready_tasks := local_set.ready_tasks XOR local_set.minimums_to_satisfy;
      #UNCHECKED_CONVERSION (local_set, tmv$dispatch_priority_integer [dp]);
    FOREND;

    tmp$clear_lock (tmv$ptl_lock);

    IF dp_unblocked THEN
      jmp$set_scheduler_event (jmc$examine_swapin_queue);
      jmp$set_scheduler_event (jmc$examine_input_queue);
    IFEND;

  PROCEND jmp$change_dispatching_alloc;

?? TITLE := 'jmp$change_dispatching_mtr_req', EJECT ??

{ PURPOSE:
{   This procedure changes the dispatching control information in the service class table
{   and resets the dispatching control information for all jobs in classes being changed.
{ DESIGN:
{   The PTL lock must be set while the table is being changed and affected job updated to
{   prevent task switch from using obsolete/uninitialized dispatching control information.

  PROCEDURE jmp$change_dispatching_mtr_req;

    VAR
      changes_pointer: ^jmt$dispatching_control_changes,
      circular_service: array [jmt$service_class_index] of integer,
      class: jmt$service_class_index,
      classes_changed: jmt$service_class_set,
      dispatching_control_index: jmt$dispatching_control_index,
      dispatching_control_p: ^jmt$dispatching_control,
      ijl_bn: jmt$ijl_block_number,
      ijl_bi: jmt$ijl_block_index,
      ijl_ordinal: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      service_used: integer;

    classes_changed := $jmt$service_class_set [];

{ Set the ptl lock so that task switch cannot be accessing the service class attribute table

    tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );

{ Change the service class attribute table

    changes_pointer := jmv$change_dispatching_list.dispatching_control_changes_p;

    WHILE changes_pointer <> NIL DO
      class := changes_pointer^.change_service_class;
      classes_changed := classes_changed + $jmt$service_class_set [class];
      dispatching_control_p := ^changes_pointer^.dispatching_control_info;
      jmv$service_classes [class]^.attributes.dispatching_control := dispatching_control_p^;

      circular_service [class] := 0;

    /calculate_circular_service/
      FOR dispatching_control_index := jmc$max_dispatching_control DOWNTO jmc$min_dispatching_control DO
        IF dispatching_control_p^ [dispatching_control_index].set_defined THEN
          IF dispatching_control_p^ [dispatching_control_index].service_limit <>
                jmc$dc_maximum_service_limit THEN
            circular_service [class] := circular_service [class] +
                  dispatching_control_p^ [dispatching_control_index].service_limit;
          ELSE
            EXIT /calculate_circular_service/; {----->
          IFEND;
        IFEND;
      FOREND /calculate_circular_service/;
      changes_pointer := changes_pointer^.dispatching_control_changes_p;
    WHILEND;

{ Scan the ijl to find all jobs belonging to classes that have been changed--those jobs may need to have
{ their dispatching priority reset.  If the dispatching control sets are circular, MOD the service used
{ before calling tmp$reset_dispatching_control.  For batch jobs, total job service is used; for interactive
{ jobs, use zero for the service.  Interactive jobs should be reset to the first dispatching control set.

    ijle_p := NIL;

  /scan_ijl/
    FOR ijl_bn := LOWERBOUND (jmv$ijl_p.block_p^) TO jmv$ijl_p.max_block_in_use DO
      IF (jmv$ijl_p.block_p^ [ijl_bn].index_p <> NIL) THEN
        ijl_ordinal.block_number := ijl_bn;
        FOR ijl_bi := LOWERVALUE (jmt$ijl_block_index) TO UPPERVALUE (jmt$ijl_block_index) DO
          ijl_ordinal.block_index := ijl_bi;
          IF ijl_ordinal <> jmv$system_ijl_ordinal THEN
{           jmp$get_ijle_p (ijl_ordinal, ijle_p);
            ijle_p := jmf$ijle_p (ijl_ordinal);
            IF ijle_p^.job_scheduler_data.service_class IN classes_changed THEN
              IF ijle_p^.job_mode = jmc$batch THEN
                service_used := ijle_p^.statistics.cp_time.time_spent_in_job_mode +
                      ijle_p^.statistics.cp_time.time_spent_in_mtr_mode -
                      ijle_p^.dispatching_control.cp_service_at_class_switch;
                IF circular_service [ijle_p^.job_scheduler_data.service_class] <> 0 THEN
                  service_used := service_used MOD circular_service
                        [ijle_p^.job_scheduler_data.service_class];
                IFEND;
              ELSE
                service_used := 0;
              IFEND;
              tmp$reset_dispatching_control (ijle_p, ijl_ordinal, service_used, FALSE);
            IFEND;
          IFEND;
        FOREND;
      IFEND;
    FOREND /scan_ijl/;

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jmp$change_dispatching_mtr_req;

?? TITLE := '[XDCL, INLINE] jmp$decrement_swapped_job_count', EJECT ??

  PROCEDURE [XDCL, INLINE] jmp$decrement_swapped_job_count
    (    ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      job_class: jmt$job_class,
      service_class: jmt$service_class_index;

    job_class := ijle_p^.job_scheduler_data.job_class;
    service_class := ijle_p^.job_scheduler_data.service_class;
    jmv$job_counts.job_class_counts [job_class].swapped_jobs :=
          jmv$job_counts.job_class_counts [job_class].swapped_jobs - 1;
    jmv$job_counts.service_class_counts [service_class].swapped_jobs := jmv$job_counts.
          service_class_counts [service_class].swapped_jobs - 1;

  PROCEND jmp$decrement_swapped_job_count;

?? TITLE := '[XDCL, INLINE] jmp$increment_swapped_job_count', EJECT ??

  PROCEDURE [XDCL, INLINE] jmp$increment_swapped_job_count
    (    ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      job_class: jmt$job_class,
      service_class: jmt$service_class_index;

    job_class := ijle_p^.job_scheduler_data.job_class;
    service_class := ijle_p^.job_scheduler_data.service_class;
    jmv$job_counts.job_class_counts [job_class].swapped_jobs :=
          jmv$job_counts.job_class_counts [job_class].swapped_jobs + 1;
    jmv$job_counts.service_class_counts [service_class].swapped_jobs := jmv$job_counts.
          service_class_counts [service_class].swapped_jobs + 1;

  PROCEND jmp$increment_swapped_job_count;

?? TITLE := '[XDCL] jmp$mtr_job_scheduler_requests', EJECT ??

  PROCEDURE [XDCL] jmp$mtr_job_scheduler_requests
    (VAR request_block: jmt$rb_scheduler_requests);

    request_block.status.normal := TRUE;

{ Process the job scheduler sub requests.

    CASE request_block.sub_reqcode OF
    = jmc$src_operator_swap_in =
      jmp$process_oper_swapin_mtr_req (request_block.ijl_ordinal, request_block.status);

    = jmc$src_idling_advance_swaps =
      jmp$process_idling_adv_swaps;

    = jmc$src_class_switch =
      jmp$process_class_switch (request_block);

    = jmc$src_change_dispatching_ctrl =
      jmp$change_dispatching_mtr_req;

    = jmc$src_cleanup_unrecovered_job =
      jmp$process_unrecovered_job (request_block);

    = jmc$src_sched_profile_loading =
      jmp$set_sched_profile_loading;

    = jmc$src_dispatching_allocation =
      jmp$change_dispatching_alloc;

    = jmc$src_swapin_recovered_jobs =
      jmp$mtr_swapin_recovered_jobs;

    ELSE
      mtp$set_status_abnormal ('JM', jme$invalid_scheduler_request, request_block.status);
    CASEND;

  PROCEND jmp$mtr_job_scheduler_requests;

?? TITLE := 'jmp$mtr_swapin_recovered_jobs', EJECT ??

{ PURPOSE:
{   This procedure scans the IJL and readies all jobs so that they can swapin for job recovery.
{ DESIGN:
{   The PTL lock is set to prevent any kind of ready task being processed asynchronously.
{   The job_fixed_asid in the ijl is zeroed out.  The old asid cannot be referenced because the
{   recovered system has a new AST.  A new asid will be assigned when the job swaps in.

  PROCEDURE jmp$mtr_swapin_recovered_jobs;

    VAR
      ijl_bn: jmt$ijl_block_number,
      ijl_bi: jmt$ijl_block_index,
      ijl_ordinal: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry;

    tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );

    FOR ijl_bn := LOWERBOUND (jmv$ijl_p.block_p^) TO jmv$ijl_p.max_block_in_use DO
      IF jmv$ijl_p.block_p^ [ijl_bn].index_p <> NIL THEN
        FOR ijl_bi := LOWERVALUE (jmt$ijl_block_index) TO UPPERVALUE (jmt$ijl_block_index) DO
          ijl_ordinal.block_number := ijl_bn;
          ijl_ordinal.block_index := ijl_bi;
{         jmp$get_ijle_p (ijl_ordinal, ijle_p);
          ijle_p := jmf$ijle_p (ijl_ordinal);
          IF ijle_p^.entry_status <> jmc$ies_entry_free THEN
            IF jmc$dsw_job_recovery IN ijle_p^.delayed_swapin_work THEN
              ijle_p^.job_fixed_asid := 0;
              jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_swapped);
              jmp$ready_task_in_swapped_job (ijl_ordinal, ijle_p);
            IFEND;
          IFEND;
        FOREND;
      IFEND;
    FOREND;

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jmp$mtr_swapin_recovered_jobs;

?? TITLE := 'jmp$process_class_switch', EJECT ??

{ PURPOSE:
{   This procedure updates dispatching control information when a job switches
{   service classes.  The PTL lock must be set while the dispatching control
{   information is changed to prevent task switch from referencing obsolete/
{   uninitialized information.

  PROCEDURE jmp$process_class_switch
    (VAR rb: jmt$rb_scheduler_requests);

    VAR
      old_class: jmt$service_class_index,
      service_class_p: ^jmt$service_class_attributes,
      ijle_p: ^jmt$initiated_job_list_entry,
      ijlo: jmt$ijl_ordinal;

    jmp$find_jsn (rb.system_supplied_name, ijle_p, ijlo);

    tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );

    IF (ijle_p <> NIL) AND (ijle_p^.entry_status > jmc$ies_job_in_memory_non_swap) AND
          (NOT jmv$sched_profile_is_loading) THEN
      old_class := ijle_p^.job_scheduler_data.service_class;

      IF rb.old_service_class = jmc$null_service_class THEN
        rb.old_service_class := old_class;
        rb.old_service_accumulator := ijle_p^.job_scheduler_data.service_accumulator;
      ELSEIF (rb.old_service_class <> old_class) OR (rb.old_service_accumulator >
            ijle_p^.job_scheduler_data.service_accumulator) THEN
        tmp$clear_lock (tmv$ptl_lock);
        RETURN; {----->
      IFEND;

      jmp$update_service_class_stats (ijle_p);

      IF (ijle_p^.entry_status > jmc$ies_swapped_in) THEN
        jmp$decrement_swapped_job_count (ijle_p);
        ijle_p^.job_scheduler_data.service_class := rb.new_service_class;
        jmp$increment_swapped_job_count (ijle_p);
      ELSE
        ijle_p^.job_scheduler_data.service_class := rb.new_service_class;
      IFEND;

      jmv$job_counts.service_class_counts [old_class].scheduler_initiated_jobs :=
            jmv$job_counts.service_class_counts [old_class].scheduler_initiated_jobs - 1;

      jmv$job_counts.service_class_counts [rb.new_service_class].scheduler_initiated_jobs :=
            jmv$job_counts.service_class_counts [rb.new_service_class].scheduler_initiated_jobs + 1;

      service_class_p := ^jmv$service_classes [rb.new_service_class]^.attributes;

      ijle_p^.job_scheduler_data.service_accumulator := 0;
      ijle_p^.dispatching_control.dispatching_control_index := jmc$min_dispatching_control;
      IF ijle_p^.dispatching_control.dispatching_priority = ijle_p^.scheduling_dispatching_priority THEN
        ijle_p^.scheduling_dispatching_priority := service_class_p^.
              dispatching_control [jmc$min_dispatching_control].dispatching_priority;
      IFEND;
      ijle_p^.dispatching_control.dispatching_priority := service_class_p^.
            dispatching_control [jmc$min_dispatching_control].dispatching_priority;
      ijle_p^.dispatching_control.service_remaining := service_class_p^.
            dispatching_control [jmc$min_dispatching_control].service_limit;
      ijle_p^.dispatching_control.cp_service_at_class_switch :=
            ijle_p^.statistics.cp_time.time_spent_in_job_mode +
            ijle_p^.statistics.cp_time.time_spent_in_mtr_mode;
      tmp$update_job_task_environment (ijle_p, ijlo, tmc$fnx_job);

{ Check active job limits for the new class; cause a job to swapout if necessary.

      IF (jmv$job_counts.service_class_counts [rb.new_service_class].scheduler_initiated_jobs -
            jmv$job_counts.service_class_counts [rb.new_service_class].swapped_jobs) >
            service_class_p^.maximum_active_jobs THEN
        jmp$set_scheduler_event (jmc$swap_jobs_for_lower_maxaj);
      IFEND;
    IFEND;

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jmp$process_class_switch;

?? TITLE := 'jmp$process_idling_adv_swaps', EJECT ??

  PROCEDURE jmp$process_idling_adv_swaps;

    VAR
      ijl_ordinal: jmt$ijl_ordinal,
{     ijle_p: ^jmt$initiated_job_list_entry,
      next_ijl_ordinal: jmt$ijl_ordinal;

    ijl_ordinal := jsv$ijl_swap_queue_list [jsc$isqi_swapped_io_completed].forward_link;

    WHILE ijl_ordinal <> jmv$null_ijl_ordinal DO
{     jmp$get_ijle_p (ijl_ordinal, ijle_p);
      next_ijl_ordinal := jmf$ijle_p (ijl_ordinal) ^.swap_queue_link.forward_link;
      jsp$monitor_advance_swap (ijl_ordinal);
      ijl_ordinal := next_ijl_ordinal;
    WHILEND;

  PROCEND jmp$process_idling_adv_swaps;

?? TITLE := 'jmp$process_oper_swapin_mtr_req', EJECT ??

{ PURPOSE:
{   Process an operator swapin job request.
{ DESIGN:
{   Re-check entry status.  Entry status was operator force out when the monitor request was issued.
{   The following (very unlikely) timing sequence could occur though:
{     The job was swapping in (swapin I/O was active) when the operator swapout occurred.  Entry status
{     was changed to operator force out.  The operator swapped the job in right away (I/O was still
{     active); the job mode operator swapin code found entry status still set to operator force out.
{     Before exchanging to monitor for the swapin request, process I/O completions executed.  Swapin I/O
{     errors would cause the entry status to be changed to system force out.
{   If entry status is still operator force out, then change entry_status to swapped.  Call
{   jmp$ready_task_in_swapped_job if the job has any ready tasks.


  PROCEDURE jmp$process_oper_swapin_mtr_req
    (    ijl_ordinal: jmt$ijl_ordinal;
     VAR status: syt$monitor_status);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry;

{   jmp$get_ijle_p (ijl_ordinal, ijle_p);
    ijle_p := jmf$ijle_p (ijl_ordinal);

    IF ijle_p^.entry_status = jmc$ies_operator_force_out THEN

{ Set the PTL lock to synchronize with the dispatcher/ready task path.

      status.normal := TRUE;
      tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );
      jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_swapped);
      IF ijle_p^.statistics.ready_task_count > 0 THEN
        ijle_p^.job_scheduler_data.swapin_q_priority_timestamp := 0;
        jmp$ready_task_in_swapped_job (ijl_ordinal, ijle_p);
      IFEND;
      tmp$clear_lock (tmv$ptl_lock);

    ELSE
      IF ijle_p^.entry_status <> jmc$ies_system_force_out THEN
        mtp$error_stop ('OPER SWAPIN REQUEST ERROR');
      IFEND;
      mtp$set_status_abnormal ('JM', jme$job_dead_cannot_swap, status);
    IFEND;

  PROCEND jmp$process_oper_swapin_mtr_req;

?? TITLE := 'jmp$process_unrecovered_job', EJECT ??

{ PURPOSE:
{   This procedure relinks a job to the null swapping queue and changes job class counts
{   when a job must be terminated during job recovery.  The PTL entries for the tasks of
{   the job are freed.  The two reasons for the termination are that a job class is not
{   defined for a job or a job could not be swapped in for recovery due to an io error.

  PROCEDURE jmp$process_unrecovered_job
    (    rb: jmt$rb_scheduler_requests);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry;

{   jmp$get_ijle_p (rb.ijl_ordinal, ijle_p);
    ijle_p := jmf$ijle_p (rb.ijl_ordinal);

    tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );

    jmv$job_counts.service_class_counts [ijle_p^.job_scheduler_data.service_class].scheduler_initiated_jobs :=
          jmv$job_counts.service_class_counts [ijle_p^.job_scheduler_data.service_class].
          scheduler_initiated_jobs - 1;
    jmv$job_counts.service_class_counts [ijle_p^.job_scheduler_data.service_class].swapped_jobs :=
          jmv$job_counts.service_class_counts [ijle_p^.job_scheduler_data.service_class].swapped_jobs - 1;
    jmv$job_counts.job_class_counts [ijle_p^.job_scheduler_data.job_class].swapped_jobs :=
          jmv$job_counts.job_class_counts [ijle_p^.job_scheduler_data.job_class].swapped_jobs - 1;

    jsp$relink_swap_queue (rb.ijl_ordinal, ijle_p, jsc$isqi_null);

    tmp$free_unrecovered_tasks (ijle_p);

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jmp$process_unrecovered_job;

?? TITLE := '[XDCL] jmp$ready_task_in_swapped_job', EJECT ??

  PROCEDURE [XDCL] jmp$ready_task_in_swapped_job
    (    ijl_ord: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      current_time: integer,
      service_class: jmt$service_class_index,
      status: syt$monitor_status,
      swap_stats_p: ^jmt$service_class_swap_stats,
      think_time: integer;

{ If a job with a memory reserve request posted has a task go ready, cancel the request.
{ The ready task may be because of a user interrupt; do not wait for the requested memory
{ to become available.

    IF ijle_p^.memory_reserve_request.requested_page_count > 0 THEN
      ijle_p^.memory_reserve_request.requested_page_count := 0;
      jmv$job_sched_events_selected [jmc$examine_swapin_queue] := TRUE;
      jmp$set_scheduler_event (jmc$examine_swapin_queue);
    IFEND;

    current_time := #FREE_RUNNING_CLOCK (0);
    IF ijle_p^.entry_status = jmc$ies_job_swapped THEN

      think_time := current_time - (ijle_p^.estimated_ready_time - ijle_p^.last_think_time);
      IF (think_time > jmv$max_think_time) THEN
        ijle_p^.last_think_time := jmv$max_think_time;
      ELSEIF (think_time > jmv$min_think_time) THEN
        ijle_p^.last_think_time := think_time;
      IFEND;

      ijle_p^.swap_data.timestamp := current_time;

      service_class := ijle_p^.job_scheduler_data.service_class;

      tmp$new_set_lock (jmv$service_class_stats_lock);
      swap_stats_p := ^jmv$service_classes [service_class]^.statistics.swap_stats;
      swap_stats_p^.swap_to_ready_time := swap_stats_p^.swap_to_ready_time +
            (current_time - ijle_p^.swap_data.swapout_timestamp);
      swap_stats_p^.swap_to_ready_count := swap_stats_p^.swap_to_ready_count + 1;
      tmp$new_clear_lock (jmv$service_class_stats_lock);

{ If possible, swap the job in immediately through the monitor interface; otherwise notify job mode
{ scheduler to swap the job in.  If the dispatching priority of the job is blocked, the swapin must
{ be handled by job mode scheduler.

      IF (NOT jmv$prevent_activation_of_jobs) AND (NOT jmv$job_scheduler_event [jmc$examine_input_queue]) AND
            (ijle_p^.job_scheduler_data.swapout_reason <> jmc$sr_thrashing) AND
            (swapin_queue_empty (ijle_p^.scheduling_dispatching_priority)) AND
            (ijle_p^.swap_status <= jmc$iss_swapped_io_complete) AND (jmv$number_free_ajl_entries > 0) AND
            (NOT too_many_active_jobs_for_class (service_class)) THEN

        jsp$monitor_swap_in (ijl_ord);

{ Reset fields for scheduler data -- only reset service accumulator since swap if the job used
{ its whole guaranteed service allotment the last time it was swapped in.

        IF ijle_p^.job_scheduler_data.guaranteed_service_remaining = 0 THEN
          ijle_p^.job_scheduler_data.service_accumulator_since_swap := 0;
        IFEND;
        ijle_p^.job_scheduler_data.guaranteed_service_remaining := 0;
        ijle_p^.job_scheduler_data.priority := jmv$service_classes [service_class]^.attributes.
              scheduling_priority.maximum;

      ELSE {The swapin could not take place in monitor so notify job mode scheduler to handle the swapin.}
        jmp$change_ijl_entry_status (ijle_p, jmc$ies_ready_task);
        insert_job_in_ready_task_list (ijl_ord, ijle_p);
        jmv$job_scheduler_event [jmc$ready_task_in_job] := TRUE;
        IF (NOT (service_class IN jmv$classes_in_maxaj_limit_wait)) THEN
          jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
          IF (NOT (service_class IN jmv$classes_in_resource_wait)) THEN
            jmv$job_sched_events_selected [jmc$examine_swapin_queue] := TRUE;
            tmp$monitor_ready_system_task (tmc$stid_job_scheduler, status);
          IFEND;
        IFEND;
      IFEND;
    IFEND;

  PROCEND jmp$ready_task_in_swapped_job;

?? TITLE := '[XDCL] jmp$recognize_job_dead', EJECT ??

{ PURPOSE:
{   This procedure is called when a swapin I/O error occurs to change a job's status to reflect that the
{   job cannot swapin.
{ DESIGN:
{   The current status is checked before changing it to system_force_out.  The various statuses
{   are handled as follows:
{     Free, Terminating, In memory non swap, In memory, System force out -- CANNOT possibly be these statuses.
{     Swapin in progress, Swapped, Operator force out -- Change the entry status to system force out.
{          Swapin in progress is the usual case; swapped requires an IDLE_SYSTEM swapout while the swapin
{          I/O was active; operator force out would be set if the operator swapped out the job while swapin
{          I/O was active.
{     Job damaged -- do not change the entry status; job damaged is more important to know.
{     Ready task, Swapin candidate -- do not change the entry status; only JOB SCHEDULER can change
{          these statuses.  The job will be swapped in again and the I/O error will be processed then.
{          The possible timing for a job to be in one of these states is very remote:  An IDLE_SYSTEM
{          swapout would have to be processed while swapin I/O was active (a swap cannot be aborted
{          while swapin I/O is active).  Then RESUME_SYSTEM would have to queue the job to swap in again
{          while the original swapin I/O was still active.
{
{   The PTL must be locked while changing the entry status because the swapped job count will be changed
{   in the swapin in progress ---> system force out transition.

  PROCEDURE [XDCL] jmp$recognize_job_dead
    (    ijl_o: jmt$ijl_ordinal);

    VAR
      ijl_p: ^jmt$initiated_job_list_entry;

{   jmp$get_ijle_p (ijl_o, ijl_p);
    ijl_p := jmf$ijle_p (ijl_o);

    IF (ijl_p^.entry_status = jmc$ies_swapin_in_progress) OR (ijl_p^.entry_status = jmc$ies_job_swapped) OR
          (ijl_p^.entry_status = jmc$ies_operator_force_out) THEN

      tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );
      jmp$change_ijl_entry_status (ijl_p, jmc$ies_system_force_out);
      tmp$clear_lock (tmv$ptl_lock);

      IF jmc$dsw_job_recovery IN ijl_p^.delayed_swapin_work THEN
        ijl_p^.delayed_swapin_work := ijl_p^.delayed_swapin_work +
              $jmt$delayed_swapin_work [jmc$dsw_recovery_swap_io_error];
        jmp$set_scheduler_event (jmc$recovery_swap_io_error);
      IFEND;
    IFEND;

  PROCEND jmp$recognize_job_dead;

?? TITLE := '[XDCL] jmp$recognize_thrashing', EJECT ??

  PROCEDURE [XDCL] jmp$recognize_thrashing;

    VAR
      ajlo: jmt$ajl_ordinal,
      count: jmt$ajl_ordinal;

    count := 0;

{ Determine if there is more than one user job active.

  /count_active_jobs/
    FOR ajlo := jmv$system_ajl_ordinal + 1 TO jmv$max_ajl_ordinal_in_use DO
      IF jmv$ajl_p^ [ajlo].in_use <> 0 THEN
        count := count + 1;
        IF count = 2 THEN
          EXIT /count_active_jobs/; {----->
        IFEND;
      IFEND;
    FOREND /count_active_jobs/;

{ If there is more than one user job active, cause scheduler to swap for thrashing.  If there is only
{ one user job active, cause mmp$periodic_call to run so the jobs working set can be shrunk to fit in memory.

    IF count = 2 THEN
      jmp$set_scheduler_event (jmc$system_is_thrashing);
    ELSE
      mmv$reduce_jws_for_thrashing := TRUE;
      mmp$nudge_periodic_call;
    IFEND;

  PROCEND jmp$recognize_thrashing;

?? TITLE := '[XDCL] jmp$reset_job_to_swapped_out', EJECT ??

{ PURPOSE:
{   This procedure is called from swapper when a swapin could not be completed because there
{   was not enough memory or there was not a free AJL ordinal.  The entry status has
{   to be swapin in progress when this procedure is called.

  PROCEDURE [XDCL] jmp$reset_job_to_swapped_out
    (    ijl_o: jmt$ijl_ordinal);

    VAR
      ijl_p: ^jmt$initiated_job_list_entry,
      status: syt$monitor_status;

{   jmp$get_ijle_p (ijl_o, ijl_p);
    ijl_p := jmf$ijle_p (ijl_o);
    IF ijl_p^.entry_status <> jmc$ies_swapin_in_progress THEN
      mtp$error_stop ('RESET TO SWAPPED OUT ERROR');
    IFEND;

    tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );
    jmp$change_ijl_entry_status (ijl_p, jmc$ies_ready_task);
    insert_job_in_ready_task_list (ijl_o, ijl_p);
    tmp$clear_lock (tmv$ptl_lock);

    jmv$job_scheduler_event [jmc$ready_task_in_job] := TRUE;
    IF (NOT (ijl_p^.job_scheduler_data.service_class IN jmv$classes_in_maxaj_limit_wait)) THEN
      jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
      IF (NOT (ijl_p^.job_scheduler_data.service_class IN jmv$classes_in_resource_wait)) THEN
        tmp$monitor_ready_system_task (tmc$stid_job_scheduler, status);
      IFEND;
    IFEND;

  PROCEND jmp$reset_job_to_swapped_out;

?? TITLE := '[XDCL] jmp$resurrect_dead_jobs', EJECT ??

  PROCEDURE [XDCL] jmp$resurrect_dead_jobs;

{ The purpose of this procedure is to find all jobs that have been marked as system_force_out
{ because a disk unit was down, and find all jobs that could not be swapped completely because
{ a disk unit was down.  This procedure is called whenenver a disk unit comes back up.
{ Swapper will try to procede swapping the jobs normally.

    VAR
      call_job_swapper: boolean,
      ijle_p: ^jmt$initiated_job_list_entry,
      ijl_bn: jmt$ijl_block_number,
      ijl_bi: jmt$ijl_block_index,
      ijl_ordinal: jmt$ijl_ordinal,
      status: syt$monitor_status;

    call_job_swapper := FALSE;

  /search_ijl/
    FOR ijl_bn := LOWERBOUND (jmv$ijl_p.block_p^) TO jmv$ijl_p.max_block_in_use DO
      IF jmv$ijl_p.block_p^ [ijl_bn].index_p <> NIL THEN
        FOR ijl_bi := LOWERVALUE (jmt$ijl_block_index) TO UPPERVALUE (jmt$ijl_block_index) DO
          ijle_p := ^jmv$ijl_p.block_p^ [ijl_bn].index_p^ [ijl_bi];
          ijl_ordinal.block_number := ijl_bn;
          ijl_ordinal.block_index := ijl_bi;

          IF (ijle_p^.entry_status = jmc$ies_system_force_out) AND
                (ijle_p^.swap_data.swapping_io_error <> ioc$no_error) THEN

{ The job was swapped out, swapping in when the io error occurred.  Try to swap the job in now.

            tmp$set_lock (tmv$ptl_lock {, mtc$abandon} );
            jmp$change_ijl_entry_status (ijle_p, jmc$ies_ready_task);
            insert_job_in_ready_task_list (ijl_ordinal, ijle_p);
            tmp$clear_lock (tmv$ptl_lock);

            jmv$job_scheduler_event [jmc$ready_task_in_job] := TRUE;
            jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
            tmp$monitor_ready_system_task (tmc$stid_job_scheduler, status);

          ELSEIF (ijle_p^.swap_status = jmc$iss_swapped_io_cannot_init) OR
                (ijle_p^.swap_status = jmc$iss_job_allocate_swap_file) THEN

{ The job was swapping out when the condition occurred.  Call job mode swapper to check on
{ the state of the swap file and allocate it if necessary, then advance the swapout.

            call_job_swapper := TRUE;

          IFEND;
        FOREND;
      IFEND;
    FOREND /search_ijl/;

    IF call_job_swapper THEN
      jmp$activate_job_mode_swapper;
    IFEND;

  PROCEND jmp$resurrect_dead_jobs;

?? TITLE := '[XDCL] jmp$set_entry_status_to_rt', EJECT ??

{ PURPOSE:
{   This procedure is called from swapper (job_mode_swapout) to set the entry status of the
{   job being swapped out to jmc$ies_ready_task and insert the job in the ready task list.
{   The caller has the PTL lock set.

  PROCEDURE [XDCL] jmp$set_entry_status_to_rt
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      status: syt$monitor_status;

    jmp$change_ijl_entry_status (ijle_p, jmc$ies_ready_task);
    insert_job_in_ready_task_list (ijl_ordinal, ijle_p);

    jmv$job_scheduler_event [jmc$ready_task_in_job] := TRUE;
    IF (NOT (ijle_p^.job_scheduler_data.service_class IN jmv$classes_in_maxaj_limit_wait)) THEN
      jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
      IF (NOT (ijle_p^.job_scheduler_data.service_class IN jmv$classes_in_resource_wait)) THEN
        tmp$monitor_ready_system_task (tmc$stid_job_scheduler, status);
      IFEND;
    IFEND;

  PROCEND jmp$set_entry_status_to_rt;

?? TITLE := 'jmp$set_job_terminated', EJECT ??

{ PURPOSE:
{   This procedure sets a job's entry status to terminating, and JOB SCHEDULER event to
{   process the terminating job.

  PROCEDURE [XDCL] jmp$set_job_terminated
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_terminating);
    jmv$ijl_p.block_p^ [ijl_ordinal.block_number].terminated_job := TRUE;
    jmp$set_scheduler_event (jmc$job_terminated);

  PROCEND jmp$set_job_terminated;

?? TITLE := 'jmp$set_sched_profile_loading', EJECT ??

{ PURPOSE:
{   The purpose of this request is to set the flag which indicates that a
{   scheduling profile is being installed in the scheduler tables.

  PROCEDURE jmp$set_sched_profile_loading;

    jmv$sched_profile_is_loading := TRUE;

  PROCEND jmp$set_sched_profile_loading;

?? TITLE := '[XDCL, INLINE] jmp$set_scheduler_event', EJECT ??

  PROCEDURE [XDCL, INLINE] jmp$set_scheduler_event
    (    event: jmt$job_scheduler_events);

    VAR
      status: syt$monitor_status;

    IF NOT jmv$job_scheduler_event [event] THEN
      jmv$job_scheduler_event [event] := TRUE;
      IF jmv$job_sched_events_selected [event] THEN
        tmp$monitor_ready_system_task (tmc$stid_job_scheduler, status);
      IFEND;
    IFEND;
  PROCEND jmp$set_scheduler_event;

?? TITLE := '[XDCL] jmp$set_scheduler_memory_event', EJECT ??

  PROCEDURE [XDCL] jmp$set_scheduler_memory_event;

    VAR
      status: syt$monitor_status;

    jmv$job_scheduler_event [jmc$needed_memory_available] := TRUE;
    tmp$monitor_ready_system_task (tmc$stid_job_scheduler, status);

  PROCEND jmp$set_scheduler_memory_event;

?? TITLE := '[XDCL] jmp$set_swapout_candidate', EJECT ??

  PROCEDURE [XDCL] jmp$set_swapout_candidate
    (    ajl_o: jmt$ajl_ordinal;
         swapout_reason: jmt$swapout_reasons);

    VAR
      ajle_p: ^jmt$active_job_list_entry,
      ijle_p: ^jmt$initiated_job_list_entry,
      ijl_ord: jmt$ijl_ordinal,
      service_used: integer;

    ajle_p := ^jmv$ajl_p^ [ajl_o];
    ijle_p := ajle_p^.ijle_p;
    IF ijle_p^.entry_status = jmc$ies_job_in_memory THEN
      IF jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes.swap_jobs_in_longwait
{   } AND jmv$swap_jobs_in_long_wait THEN
        ijl_ord := ajle_p^.ijl_ordinal;
        ijle_p^.estimated_ready_time := #FREE_RUNNING_CLOCK (0) + ijle_p^.last_think_time;
        jmp$calculate_service (ijle_p, service_used);
        check_for_class_switch (ijle_p);
        ijle_p^.job_scheduler_data.swapout_reason := swapout_reason;
        IF swapout_reason = jmc$sr_idle_dispatching THEN
          IF jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes.
                guaranteed_service_quantum = jmc$unlimited_service_accum THEN
            ijle_p^.job_scheduler_data.guaranteed_service_remaining := jmc$unlimited_service_accum;
          ELSEIF ijle_p^.job_scheduler_data.service_accumulator_since_swap <
                jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes.
                guaranteed_service_quantum THEN
            ijle_p^.job_scheduler_data.guaranteed_service_remaining :=
                  jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes.
                  guaranteed_service_quantum - ijle_p^.job_scheduler_data.service_accumulator_since_swap;
          IFEND;
        IFEND;
        ijle_p^.job_scheduler_data.job_swap_counts.long_wait :=
              ijle_p^.job_scheduler_data.job_swap_counts.long_wait + 1;
        jsp$monitor_swap_out (ijl_ord);

{ If the service class is at the maxaj limit, remove the class so a job
{ with this service class can be activated.

        IF (ijle_p^.job_scheduler_data.service_class IN jmv$classes_in_maxaj_limit_wait) THEN
          remove_class_from_maxaj_limit (ijle_p^.job_scheduler_data.service_class);
        IFEND;
      ELSE
        ajle_p^.job_is_good_swap_candidate := TRUE;
      IFEND;
    IFEND;

  PROCEND jmp$set_swapout_candidate;

?? TITLE := '[XDCL] jmp$subsystem_priority_change', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to set a scheduler event if a swapin candidate
{   job has had its scheduling dispatching priority changed because of subsystem locks.
{ DESIGN:
{   The caller of this procedure must set the PTL lock.

  PROCEDURE [XDCL] jmp$subsystem_priority_change
    (    ijle_p: ^jmt$initiated_job_list_entry);

    IF (ijle_p^.entry_status = jmc$ies_swapin_candidate) AND
          (ijle_p^.scheduling_dispatching_priority > ijle_p^.dispatching_control.dispatching_priority) THEN
      jmv$subsystem_priority_changes [ijle_p^.job_scheduler_data.service_class] := TRUE;
      jmp$set_scheduler_event (jmc$subsystem_priority_change);
    IFEND;

  PROCEND jmp$subsystem_priority_change;

?? TITLE := '[XDCL] jmp$swap_non_dispatchable_job', EJECT ??

  PROCEDURE [XDCL] jmp$swap_non_dispatchable_job
    (    ajl_ordinal: jmt$ajl_ordinal);

    VAR
      ijl_ordinal: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry;

    ijle_p := jmv$ajl_p^ [ajl_ordinal].ijle_p;
    ijl_ordinal := jmv$ajl_p^ [ajl_ordinal].ijl_ordinal;
    jmp$set_swapout_candidate (ajl_ordinal, jmc$sr_idle_dispatching);

{ Jmp$set_swapout_candidate swapped the job and caused entry status to be changed to job_swapped.
{ The job was artificially idled, so it must be put in the ready task list to swap back in.

    jmv$swapped_idle_disp_count := jmv$swapped_idle_disp_count + 1;
    jmp$change_ijl_entry_status (ijle_p, jmc$ies_ready_task);
    insert_job_in_ready_task_list (ijl_ordinal, ijle_p);
    jmv$job_scheduler_event [jmc$ready_task_in_job] := TRUE;

  PROCEND jmp$swap_non_dispatchable_job;

?? TITLE := '[XDCL] jmp$update_serv_class_stats_req', EJECT ??

{ PURPOSE:
{   This procedure processes the monitor request to update service class statistics.
{ DESIGN:
{   The service class statistics updated by this procedure must be updated in monitor mode
{   in order to synchronize writing the statistics variable.  This procedure is called via
{   a monitor request at statistics emission time.  All initiated jobs are scanned for
{   statistics information.

  PROCEDURE [XDCL] jmp$update_serv_class_stats_req
    (VAR request_block: jmt$rb_service_class_statistics);

    VAR
      ijl_bn: jmt$ijl_block_number,
      ijl_bi: jmt$ijl_block_index,
      ijl_ordinal: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry;

    request_block.status.normal := TRUE;

    FOR ijl_bn := LOWERBOUND (jmv$ijl_p.block_p^) TO jmv$ijl_p.max_block_in_use DO
      IF (jmv$ijl_p.block_p^ [ijl_bn].index_p <> NIL) THEN
        ijl_ordinal.block_number := ijl_bn;
        FOR ijl_bi := LOWERVALUE (jmt$ijl_block_index) TO UPPERVALUE (jmt$ijl_block_index) DO
          ijl_ordinal.block_index := ijl_bi;
{         jmp$get_ijle_p (ijl_ordinal, ijle_p);
          ijle_p := jmf$ijle_p (ijl_ordinal);
          IF ijle_p^.entry_status <> jmc$ies_entry_free THEN
            jmp$update_service_class_stats (ijle_p);
          IFEND;
        FOREND;
      IFEND;
    FOREND;

  PROCEND jmp$update_serv_class_stats_req;

?? TITLE := '[XDCL] jmp$update_service_class_stats', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to update the service class statistics for a
{   service class with the information of a specific job.  The service class statistics
{   accumulators for the job are updated.

  PROCEDURE [XDCL] jmp$update_service_class_stats
    (    ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      statistics_p: ^jmt$mtr_serv_class_stat_entry;

{ Update cp statistics.

    tmp$new_set_lock (jmv$service_class_stats_lock);

    statistics_p := ^jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics;
    statistics_p^.cp_time.job_mode := statistics_p^.cp_time.job_mode +
          (ijle_p^.statistics.cp_time.time_spent_in_job_mode -
          ijle_p^.service_class_statistics.cp_time.time_spent_in_job_mode);
    ijle_p^.service_class_statistics.cp_time.time_spent_in_job_mode :=
          ijle_p^.statistics.cp_time.time_spent_in_job_mode;

    statistics_p^.cp_time.monitor_mode := statistics_p^.cp_time.monitor_mode +
          (ijle_p^.statistics.cp_time.time_spent_in_mtr_mode -
          ijle_p^.service_class_statistics.cp_time.time_spent_in_mtr_mode);

    ijle_p^.service_class_statistics.cp_time.time_spent_in_mtr_mode :=
          ijle_p^.statistics.cp_time.time_spent_in_mtr_mode;

{ Update page fault statistics.
    statistics_p^.page_faults.disk := statistics_p^.page_faults.disk +
          (ijle_p^.statistics.paging_statistics.page_in_count -
          ijle_p^.service_class_statistics.page_faults.disk);
    ijle_p^.service_class_statistics.page_faults.disk := ijle_p^.statistics.paging_statistics.page_in_count;

    statistics_p^.page_faults.reclaimed := statistics_p^.page_faults.reclaimed +
          (ijle_p^.statistics.paging_statistics.pages_reclaimed_from_queue -
          ijle_p^.service_class_statistics.page_faults.reclaimed);
    ijle_p^.service_class_statistics.page_faults.reclaimed :=
          ijle_p^.statistics.paging_statistics.pages_reclaimed_from_queue;

    statistics_p^.page_faults.assigned := statistics_p^.page_faults.assigned +
          (ijle_p^.statistics.paging_statistics.new_pages_assigned -
          ijle_p^.service_class_statistics.page_faults.assigned);
    ijle_p^.service_class_statistics.page_faults.assigned :=
          ijle_p^.statistics.paging_statistics.new_pages_assigned;

{ Update swapping statistics.
    statistics_p^.swap_stats.long_wait_swaps := statistics_p^.swap_stats.long_wait_swaps +
          (ijle_p^.job_scheduler_data.job_swap_counts.long_wait -
          ijle_p^.service_class_statistics.swapouts.long_wait);
    ijle_p^.service_class_statistics.swapouts.long_wait :=
          ijle_p^.job_scheduler_data.job_swap_counts.long_wait;

    statistics_p^.swap_stats.job_mode_swaps := statistics_p^.swap_stats.job_mode_swaps +
          (ijle_p^.job_scheduler_data.job_swap_counts.job_mode -
          ijle_p^.service_class_statistics.swapouts.job_mode);
    ijle_p^.service_class_statistics.swapouts.job_mode := ijle_p^.job_scheduler_data.job_swap_counts.job_mode;

    tmp$new_clear_lock (jmv$service_class_stats_lock);

  PROCEND jmp$update_service_class_stats;

MODEND jmm$job_scheduler_monitor_mode;
