?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Job Management : job scheduler ring 1' ??
MODULE jmm$job_scheduler_ring_1;

{ Purpose
{     This module contains procedures to adjust a jobs priority
{      and to perform functions necessary to  accomplish swapout,
{      swapin, and job initiation.
{

{  EXTERNALS REFERENCED BY THIS MODULE:

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
*copyc i#fill
*copyc gfp$assign_fde
*copyc gfp$get_fde_p
*copyc gfp$free_fde
*copyc jmp$calculate_service
*copyc jmp$change_dispatching_prior_r1
*copyc jmp$jm_change_ijl_entry_status
*copyc jmp$compute_total_memory_used
*copyc jmp$determine_serv_class_name
*copyc jmf$ijle_p
*copyc jmp$incr_scheduler_statistic_sc
*copyc jmp$notify_queued_files_job_end
*copyc jmp$refresh_job_candidate_class
*copyc jmp$reset_max_class_working_set
*copyc jsp$advance_long_wait_jobs
*copyc jsp$help_monitor_mode_swapper
*copyc jsp$special_job_swapout
*copyc jsp$swap_job_out
*copyc jsp$swap_job_in
*copyc i#build_adaptable_array_ptr
*copyc i#call_monitor
*copyc i#move
*copyc i#program_error
*copyc mmp$build_segment
*copyc mmp$close_device_file
*copyc mmp$free_pages
*copyc mmp$get_max_sdt_sdtx_pointer
*copyc mmp$get_sdtx_entry_p
*copyc mmp$invalidate_segment
*copyc mmp$open_file_by_sfid
*copyc mmp$write_modified_pages
*copyc osp$append_status_parameter
*copyc osp$clear_signature_lock
*copyc osp$expand_ptl
*copyc osp$set_locked_variable
*copyc osp$set_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$find_executing_task_xcb
*copyc pmp$get_executing_task_gtid
*copyc pmp$ready_task
*copyc pmp$zero_out_table
*copyc syp$create_job_template
*copyc syp$establish_condition_handler
*copyc syp$order_job_fixed_pages
*copyc syp$set_status_from_mtr_status
*copyc syp$update_flags
*copyc syp$write_job_fixed_pages
*copyc tmp$find_mainframe_signal
*copyc tmp$ready_system_task1
*copyc jmv$ajl_p
*copyc jmv$candidate_queued_jobs
*copyc jmv$classes_in_maxaj_limit_wait
*copyc jmv$classes_in_resource_wait
*copyc jmv$idle_dispatching_controls
*copyc jmv$ijl_entry_status_statistics
*copyc jmv$ijl_p
*copyc jmv$ijl_ready_task_list
*copyc jmv$job_class_table_p
*copyc jmv$job_counts
*copyc jmv$job_scheduler_event
*copyc jmv$job_scheduler_table
*copyc jmv$job_sched_events_selected
*copyc jmv$last_service_calc_time
*copyc jmv$long_wait_swap_threshold
*copyc jmv$max_ajl_ordinal_in_use
*copyc jmv$max_class_working_set
*copyc jmv$max_service_class_in_use
*copyc jmv$maximum_service_classes
*copyc jmv$maximum_job_class_in_use
*copyc jmv$memory_needed_by_scheduler
*copyc jmv$memory_queue_update_by_swap
*copyc jmv$null_ijl_ordinal
*copyc jmv$prevent_activation_of_jobs
*copyc jmv$refresh_job_candidates
*copyc jmv$sched_profile_is_loading
*copyc jmv$sched_service_calc_time
*copyc jmv$sdtx
*copyc jmv$service_classes
*copyc jmv$subsystem_priority_changes
*copyc jmv$swapin_candidate_queue
*copyc jmv$system_ajl_ordinal
*copyc jmv$system_core_template
*copyc jmv$system_ijl_ordinal
*copyc jmv$system_job_template_p
*copyc jmv$time_to_wake_scheduler
*copyc mmv$default_sdtx_entry
*copyc mmv$max_template_segment_number
*copyc mmv$min_avail_pages
*copyc mmv$reassignable_page_frames
*copyc mmv$resident_job_target
*copyc osv$mainframe_pageable_heap
*copyc osv$mainframe_wired_heap
*copyc osv$page_size
*copyc tmv$ptl_p

?? PUSH (LISTEXT := ON) ??
*copyc jmc$maximum_job_count
*copyc jmc$null_ajl_ordinal
*copyc jme$job_scheduler_conditions
*copyc jmt$dispatching_control
*copyc jmt$dispatching_control_index
*copyc jmt$job_class_attributes
*copyc jmt$job_scheduler_event
*copyc jmt$ijl_ordinal
*copyc jmt$initiated_job_list_entry
*copyc jmt$job_template_entry
*copyc jmt$operator_request_list
*copyc jmt$rb_scheduler_requests
*copyc jmt$service_class_attributes
*copyc jmt$service_class_set
*copyc jmt$system_supplied_name
*copyc jmt$trick_ijlo_variant_record
*copyc jse$condition_codes
*copyc mmc$segment_manager_defaults
*copyc mme$condition_codes
*copyc mmt$page_frame_queue_id
*copyc mmt$page_frame_index
*copyc syt$monitor_request_code
*copyc syt$system_core_condition
*copyc jmt$lock_functions
*copyc jmt$node
*copyc jmc$special_dispatch_priorities
*copyc syc$monitor_request_codes
*copyc ost$hardware_subranges
*copyc ost$status
*copyc ost$heap
*copyc tme$monitor_mode_exceptions
*copyc tmt$rb_initiate_job
*copyc jmt$job_control_block
*copyc ost$execution_control_block
*copyc oss$job_fixed
*copyc oss$mainframe_paged_literal
*copyc oss$mainframe_pageable
?? POP ??
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    gtid_of_task_waiting_for_idle: ost$global_task_id := [0, 0],
    jmv$operator_request_list: [STATIC, oss$mainframe_pageable] jmt$operator_request_list := [[0], * ],
    jmv$all_jobs_swapped_for_idling: [STATIC, oss$mainframe_pageable] boolean := FALSE,
    jmv$sched_memory_wait_factor: [XDCL] integer := 5,
    jmv$scheduler_wait_time: [XDCL] integer := 500000;


?? TITLE := '[inline] ADJUST_ACTIVE_JOB_PRIORITY', EJECT ??

  PROCEDURE [INLINE] adjust_active_job_priority
    (    ijle_p: ^jmt$initiated_job_list_entry;
         service_used: integer);

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

    service_class_p := ^jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes;

{ Decrement the job's priority by the amount of service used.

    priority := ijle_p^.job_scheduler_data.priority - service_used;
    IF priority < service_class_p^.scheduling_priority.minimum THEN
      ijle_p^.job_scheduler_data.priority := service_class_p^.scheduling_priority.minimum;
    ELSE
      ijle_p^.job_scheduler_data.priority := priority;
    IFEND;

{ 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.

    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
        current_class := ijle_p^.job_scheduler_data.service_class;
        new_class := service_class_p^.next_service_class_index;
        IF 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 := current_class;
          rb.old_service_accumulator := ijle_p^.job_scheduler_data.service_accumulator;

          i#call_monitor (#LOC (rb), #SIZE (rb));

        IFEND;
      IFEND;
    IFEND;

  PROCEND adjust_active_job_priority;

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

{ PURPOSE:
{   Adjust the scheduling priority of a swapin candidate.

  PROCEDURE [INLINE] adjust_swapin_cand_prio
    (    ijle_p: ^jmt$initiated_job_list_entry;
         current_time: jmt$clock_time);

    VAR
      age_interval: integer,
      priority: integer,
      service_class_list_p: ^jmt$service_class_attributes,
      swap_age_interval: integer;

      service_class_list_p := ^jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes;
      swap_age_interval := service_class_list_p^.swap_age_interval;
      IF swap_age_interval <> jmc$unlimited_prio_age_interval THEN
        age_interval := ((current_time - ijle_p^.job_scheduler_data.swapin_q_priority_timestamp)
              DIV swap_age_interval);
      ELSE
        age_interval := 0;  { no aging
      IFEND;
      priority := age_interval * service_class_list_p^.scheduling_priority.swap_age_increment + ijle_p^.
            job_scheduler_data.unaged_swap_queue_priority;
      IF priority > service_class_list_p^.scheduling_priority.maximum THEN
        ijle_p^.job_scheduler_data.priority := service_class_list_p^.scheduling_priority.maximum;
      ELSE
        ijle_p^.job_scheduler_data.priority := priority;
      IFEND;

  PROCEND adjust_swapin_cand_prio;

?? TITLE := 'create_and_initialize_job_fixed', EJECT ??

  PROCEDURE create_and_initialize_job_fixed
    (    job_class: jmt$job_class,
         service_class: jmt$service_class_index,
         ijl_ord: jmt$ijl_ordinal;
     VAR xcb_p: ^ost$execution_control_block;
     VAR create_status: ost$status);

    VAR
      jcb_p: ^jmt$job_control_block,
      jf_seg_no: ost$segment,
      jf_seg_p: ^cell,
      max_segs: ost$segment,
      max_seg_len: ost$segment_length,
      scan: ost$segment,
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      seg_no: ost$segment,
      seg_templ_p: ^jmt$job_templ_segment,
      segment_attributes: [READ, oss$mainframe_paged_literal] mmt$segment_attrib_descriptor :=
         [1, sfc$no_limit, mmc$cell_pointer, [0, gfc$tr_null_residence, gfc$null_file_hash],
         ^software_attributes],
      segment_pointer: mmt$segment_pointer,
      software_attributes: [READ, oss$mainframe_paged_literal] array [1 .. 1] of mmt$attribute_descriptor :=
            [[mmc$kw_software_attributes, $mmt$software_attribute_set [mmc$sa_fixed]]],
      status: ost$status,
      template_created: boolean,
      st_rma: integer;

    create_status.normal := TRUE;
    mmp$build_segment (segment_attributes, NIL {shared_taskid_array}, segment_pointer, status);
    IF NOT status.normal THEN
      create_status.normal := FALSE;
      i#program_error;
      RETURN;
    IFEND;

{ move the job fixed base template into the job fixed segment.

    jf_seg_p := segment_pointer.cell_pointer;
    i#move (#LOC (jmv$system_core_template.job_fixed_template_p^),
          jf_seg_p, #SIZE (jmv$system_core_template.job_fixed_template_p^));


{ Create JCB descriptor for new segment.

    jcb_p := jf_seg_p;
    jcb_p^.cptime_next_age_working_set := 200000;
    jcb_p^.signal_interval := 0ffffffff(16);

{ Initialize job identity.

    jcb_p^.job_id := jmv$candidate_queued_jobs [job_class].kjl_index;
    jcb_p^.page_aging_interval := jmv$job_class_table_p^ [job_class].page_aging_interval.default;
    jcb_p^.cyclic_aging_interval := jmv$job_class_table_p^ [job_class].cyclic_aging_interval.default;
    jcb_p^.min_working_set_size := jmv$job_class_table_p^ [job_class].minimum_working_set.default;
    jcb_p^.max_working_set_size := jmv$job_class_table_p^ [job_class].maximum_working_set.default;
    jcb_p^.detached_job_wait_time := jmv$job_class_table_p^ [job_class].detached_job_wait_time.default;
    jcb_p^.next_cyclic_aging_time := 0ffffffffffff(16);
    jcb_p^.ijle_p := jmf$ijle_p (ijl_ord);
    jcb_p^.jobname := jmv$candidate_queued_jobs [job_class].user_supplied_name;
    jcb_p^.system_name := jmv$candidate_queued_jobs [job_class].system_supplied_name;

{ create XCB descriptor for new segment.

    jf_seg_no := #SEGMENT (segment_pointer.cell_pointer);

    xcb_p := #ADDRESS (#RING (jf_seg_p), jf_seg_no, jmv$system_core_template.jmtr_xcb_offset);

    xcb_p^.timeslice := jmv$service_classes [service_class]^.
          attributes.dispatching_control [jmc$min_dispatching_control].dispatching_timeslice;

{ Create SDT descriptor for new segment.

    sdt_p := #ADDRESS (1, jf_seg_no, xcb_p^.sdt_offset);
    sdtx_p := #ADDRESS (1, jf_seg_no, xcb_p^.sdtx_offset);
    sdt_p^.st [osc$segnum_job_fixed_heap].ste.vl := osc$vl_cache_bypass;
    sdt_p^.st [osc$segnum_job_fixed_heap].ste.r2 := 3;
    sdtx_p^.sdtx_table [osc$segnum_job_fixed_heap].inheritance := mmc$si_share_segment;

{ Set rma pointer of SDT in XP.

    #real_memory_address (sdt_p, st_rma);
    xcb_p^.xp.segment_table_address_1 := st_rma DIV 10000(16);
    xcb_p^.xp.segment_table_address_2 := st_rma MOD 10000(16);

    xcb_p^.dispatching_priority := jmc$priority_system_job;


{Search for alternate template to match the job class.
{If none found, then initiate NOS/VE template

    syp$create_job_template (ijl_ord, job_class, sdt_p, sdtx_p, template_created);
    IF NOT template_created THEN

{ Load job template segment descriptors in SDT.

{ remember to re init the adaptable pointers in the xcb when
{ multiple job templates become a reality.

      max_segs := UPPERBOUND (jmv$system_job_template_p^.job_template);

    /load_new_jobs_sdt/

      FOR scan := 1 TO max_segs DO
        seg_templ_p := ^jmv$system_job_template_p^.job_template [scan];
        seg_no := seg_templ_p^.seg_no;
        sdt_p^.st [seg_no] := seg_templ_p^.sdt;
        sdtx_p^.sdtx_table [seg_no] := seg_templ_p^.sdtx;
      FOREND /load_new_jobs_sdt/;
    IFEND;

  PROCEND create_and_initialize_job_fixed;

?? TITLE := 'decrement_swapin_cand_counts', EJECT ??

  PROCEDURE [INLINE] decrement_swapin_cand_counts
    (    class: jmt$service_class_index);

{ Decrement the number of jobs in the queue.

    jmv$swapin_candidate_queue [class].number_of_jobs_in_queue :=
          jmv$swapin_candidate_queue [class].number_of_jobs_in_queue - 1;

  PROCEND decrement_swapin_cand_counts;

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

{ PURPOSE:
{   This procedure finds the end of the next higher dispatching priority sub_queue in the
{   swapin candidate queue, if there is one.

  PROCEDURE [INLINE] find_end_of_higher_dp_q
    (    swapin_cand_dp: jmt$dispatching_priority;
         class: jmt$service_class_index;
     VAR end_of_higher_dp_q: jmt$ijl_ordinal;
     VAR head_of_current_q: jmt$ijl_ordinal);

    VAR
      dp: jmt$dispatching_priority;

    FOR dp := swapin_cand_dp + 1 TO jmc$priority_p10 DO
      IF jmv$swapin_candidate_queue [class].end_of_dp_q [dp] <> jmv$null_ijl_ordinal THEN
        end_of_higher_dp_q := jmv$swapin_candidate_queue [class].end_of_dp_q [dp];
        head_of_current_q := jmf$ijle_p (end_of_higher_dp_q)^.swapin_candidate_queue;
        RETURN;
      IFEND;
    FOREND;

    end_of_higher_dp_q := jmv$null_ijl_ordinal;
    head_of_current_q := jmv$swapin_candidate_queue [class].swapin_candidate_queue;

  PROCEND find_end_of_higher_dp_q;

?? TITLE := '[inline] GET_READY_TASK_LIST', EJECT ??

{ PURPOSE:
{   This procedure gets the head of the ready task list, which monitor mode
{   scheduler builds.
{ 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] get_ready_task_list
    (VAR head_of_list: jmt$ijl_ordinal);

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

    old_list_head.ijl_integer := 0;

    REPEAT
      osp$set_locked_variable (jmv$ijl_ready_task_list, old_list_head.ijl_integer, 0, list_head.ijl_integer,
            succeeded);
      IF NOT succeeded THEN
        old_list_head.ijl_integer := list_head.ijl_integer;
      IFEND;
    UNTIL succeeded;
    head_of_list := old_list_head.ijl_ordinal;

  PROCEND get_ready_task_list;

?? TITLE := '[XDCL, #GATE] jmp$add_to_maxaj_limit_set', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$add_to_maxaj_limit_set
    (    class: jmt$service_class_index);

    jmv$classes_in_maxaj_limit_wait := jmv$classes_in_maxaj_limit_wait + $jmt$service_class_set [class];

  PROCEND jmp$add_to_maxaj_limit_set;

?? TITLE := '[XDCL, #GATE] jmp$adjust_swapin_cand_prio', EJECT ??

{ PURPOSE:
{   Process the calls from ring 2 to adjust the scheduling priority for a swapin candidate.

  PROCEDURE [XDCL, #GATE] jmp$adjust_swapin_cand_prio
    (    ijl_ordinal: jmt$ijl_ordinal;
         current_time: jmt$clock_time);

      adjust_swapin_cand_prio (jmf$ijle_p (ijl_ordinal), current_time);

  PROCEND jmp$adjust_swapin_cand_prio;

?? TITLE := '[XDCL, #GATE] jmp$all_jobs_swapped_for_idling', EJECT ??

  FUNCTION [XDCL, #GATE] jmp$all_jobs_swapped_for_idling: boolean;

    jmp$all_jobs_swapped_for_idling := jmv$all_jobs_swapped_for_idling;

  FUNCEND jmp$all_jobs_swapped_for_idling;

?? TITLE := '[XDCL, #GATE] jmp$allocate_more_ijl_space', EJECT ??

*copy jmh$allocate_more_ijl_space

  PROCEDURE [XDCL, #GATE] jmp$allocate_more_ijl_space
    (    ijl_block_number: jmt$ijl_block_number);

    jmv$ijl_p.block_p^ [ijl_block_number].in_use_count := 0;
    ALLOCATE jmv$ijl_p.block_p^ [ijl_block_number].index_p IN osv$mainframe_wired_heap^;

    pmp$zero_out_table (#LOC (jmv$ijl_p.block_p^ [ijl_block_number].index_p^),
          #SIZE (jmv$ijl_p.block_p^ [ijl_block_number].index_p^));

    IF ijl_block_number > jmv$ijl_p.max_block_in_use THEN
      jmv$ijl_p.max_block_in_use := jmv$ijl_p.max_block_in_use + 1;

{  This loop is necessary because when this procedure is called from job recovery,
{  the IJL in not constructed sequentially.

      WHILE jmv$ijl_p.max_block_in_use <> ijl_block_number DO
        jmv$ijl_p.block_p^ [jmv$ijl_p.max_block_in_use].index_p := NIL;
        jmv$ijl_p.block_p^ [jmv$ijl_p.max_block_in_use].in_use_count := 0;
        jmv$ijl_p.max_block_in_use := jmv$ijl_p.max_block_in_use + 1;
      WHILEND;
    IFEND;

  PROCEND jmp$allocate_more_ijl_space;

?? TITLE := '[XDCL, #GATE] jmp$call_job_swapper', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$call_job_swapper;

{
{     The purpose of this procedure is to call the job swapper to perform some activity that
{  is neeeded by monitor mode job swapper.
{

    jmp$incr_scheduler_statistic_sc (jmc$advance_swap_event_count);
    jmp$clear_scheduler_event (jmc$call_job_swapper);
    jsp$help_monitor_mode_swapper;

  PROCEND jmp$call_job_swapper;

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

  PROCEDURE [XDCL] jmp$check_active_job_limits
    (    service_class_set: jmt$service_class_set);

    VAR
      class: jmt$service_class_index,
      ignore_status: ost$status;

  /check_maxaj/
    FOR class := jmc$system_service_class TO jmv$max_service_class_in_use DO
      IF class IN service_class_set THEN
        IF ((jmv$job_counts.service_class_counts [class].scheduler_initiated_jobs -
              jmv$job_counts.service_class_counts [class].swapped_jobs) >
              jmv$service_classes [class]^.attributes.maximum_active_jobs) THEN
          jmp$set_event_and_ready_sched (jmc$swap_jobs_for_lower_maxaj);
          EXIT /check_maxaj/;
        IFEND;
      IFEND;
    FOREND /check_maxaj/;
  PROCEND jmp$check_active_job_limits;

?? TITLE := '[XDCL, #GATE] jmp$clear_memory_res_swap_field', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$clear_memory_res_swap_field
    (    ijl_ord: jmt$ijl_ordinal);

    jmf$ijle_p (ijl_ord)^.memory_reserve_request.swapout_job := FALSE;

  PROCEND jmp$clear_memory_res_swap_field;

?? TITLE := '[XDCL, #GATE] jmp$clear_scheduler_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$clear_scheduler_event
    (    event: jmt$job_scheduler_events);

    jmv$job_scheduler_event [event] := FALSE;

  PROCEND jmp$clear_scheduler_event;

?? TITLE := '[XDCL, #GATE] jmp$clear_sched_event_selection', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$clear_sched_event_selection
    (    event: jmt$job_scheduler_events);

    jmv$job_sched_events_selected [event] := FALSE;

  PROCEND jmp$clear_sched_event_selection;

?? TITLE := '[XDCL, #GATE] jmp$decrement_lw_threshold', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$decrement_lw_threshold
    (    node: jmt$node);

    VAR
      dp: jmt$dispatching_priority;

    FOR dp := node.dispatching_priority DOWNTO jmc$lowest_dispatching_priority DO
      jmv$long_wait_swap_threshold [dp] := jmv$long_wait_swap_threshold [dp] - node.ws;
      IF jmv$long_wait_swap_threshold [dp] < mmv$min_avail_pages THEN
        jmv$long_wait_swap_threshold [dp] := mmv$min_avail_pages;
      IFEND;
    FOREND;

  PROCEND jmp$decrement_lw_threshold;

?? TITLE := '[XDCL, #GATE] jmp$delete_swapin_candidate', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$delete_swapin_candidate
    (    delete_ijl_ordinal: jmt$ijl_ordinal;
         class: jmt$service_class_index);

    VAR
      class_scan: jmt$service_class_index,
      current_ijle_p: ^jmt$initiated_job_list_entry,
      current_ijlo: jmt$ijl_ordinal,
      delete_ijle_p: ^jmt$initiated_job_list_entry,
      dispatch_prio: jmt$dispatching_priority,
      found: boolean,
      status: ost$status;

    found := FALSE;
    delete_ijle_p := jmf$ijle_p (delete_ijl_ordinal);
    dispatch_prio := delete_ijle_p^.swapin_candidate_queue_dp;

    IF delete_ijl_ordinal = jmv$swapin_candidate_queue [class].swapin_candidate_queue THEN


{ Delete from the head of the queue.

      jmv$swapin_candidate_queue [class].swapin_candidate_queue := delete_ijle_p^.swapin_candidate_queue;
      found := TRUE;
      decrement_swapin_cand_counts (class);
      IF delete_ijl_ordinal = jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] THEN
        jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] := jmv$null_ijl_ordinal;
      IFEND;

    ELSE


{ Delete from the middle or end of the queue.

      current_ijlo := jmv$swapin_candidate_queue [class].swapin_candidate_queue;

    /find_delete_candidate/
      WHILE (current_ijlo <> delete_ijl_ordinal) AND (current_ijlo <> jmv$null_ijl_ordinal) DO
        current_ijle_p := jmf$ijle_p (current_ijlo);
        IF current_ijle_p^.swapin_candidate_queue = delete_ijl_ordinal THEN
          found := TRUE;
          current_ijle_p^.swapin_candidate_queue := delete_ijle_p^.swapin_candidate_queue;


          decrement_swapin_cand_counts (class);
          IF delete_ijl_ordinal = jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] THEN
            IF current_ijle_p^.swapin_candidate_queue_dp = dispatch_prio THEN
              jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] := current_ijlo;
            ELSE
              jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] := jmv$null_ijl_ordinal;
            IFEND;
          IFEND;
          EXIT /find_delete_candidate/;
        IFEND;
        current_ijlo := current_ijle_p^.swapin_candidate_queue;
      WHILEND /find_delete_candidate/;

    IFEND;

    IF NOT found THEN


{ The job was not in the queue we expected it to be in.  Check the queues for
{ each class (in case of a service class switch).

    /scan_all_service_class_queues/
      FOR class_scan := jmc$system_service_class TO jmv$max_service_class_in_use DO
        IF delete_ijl_ordinal = jmv$swapin_candidate_queue [class_scan].swapin_candidate_queue THEN


{ Delete from the head of the queue.

          jmv$swapin_candidate_queue [class_scan].swapin_candidate_queue := delete_ijle_p^.
                swapin_candidate_queue;
          found := TRUE;
          decrement_swapin_cand_counts (class_scan);
          IF delete_ijl_ordinal = jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] THEN
            jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] := jmv$null_ijl_ordinal;
          IFEND;
          EXIT /scan_all_service_class_queues/;
        ELSE


{ Delete from the middle or end of the queue.

          current_ijlo := jmv$swapin_candidate_queue [class_scan].swapin_candidate_queue;

        /find_delete_candidate_2/
          WHILE (current_ijlo <> delete_ijl_ordinal) AND (current_ijlo <> jmv$null_ijl_ordinal) DO
            current_ijle_p := jmf$ijle_p (current_ijlo);
            IF current_ijle_p^.swapin_candidate_queue = delete_ijl_ordinal THEN
              found := TRUE;
              current_ijle_p^.swapin_candidate_queue := delete_ijle_p^.swapin_candidate_queue;

{ Change the deleted candidate's swapin_candidate field.

              decrement_swapin_cand_counts (class_scan);
              IF delete_ijl_ordinal = jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] THEN
                IF current_ijle_p^.swapin_candidate_queue_dp = dispatch_prio THEN
                  jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] := current_ijlo;
                ELSE
                  jmv$swapin_candidate_queue [class].end_of_dp_q [dispatch_prio] := jmv$null_ijl_ordinal;
                IFEND;
              IFEND;
              EXIT /scan_all_service_class_queues/;
            IFEND;
            current_ijlo := current_ijle_p^.swapin_candidate_queue;
          WHILEND /find_delete_candidate_2/;

        IFEND;
      FOREND /scan_all_service_class_queues/;

      IF {still} NOT found THEN
        osp$system_error ('COULD NOT FIND DELETE CANDIDATE', ^status);
      IFEND;
    IFEND;
    delete_ijle_p^.swapin_candidate_queue := jmv$null_ijl_ordinal;

  PROCEND jmp$delete_swapin_candidate;

?? TITLE := '[XDCL, #GATE] jmp$delete_ijl_entry', EJECT ??

*copy jmh$delete_ijl_entry

  PROCEDURE [XDCL, #GATE] jmp$delete_ijl_entry
    (    ijl_ordinal: jmt$ijl_ordinal);

    VAR
      ijl_bn: jmt$ijl_block_number;

    ijl_bn := ijl_ordinal.block_number;

    IF jmv$ijl_p.block_p^ [ijl_bn].in_use_count = 1 THEN
      IF ijl_bn = jmv$ijl_p.max_block_in_use THEN
        ijl_bn := ijl_bn - 1;
        WHILE (jmv$ijl_p.block_p^ [ijl_bn].index_p = NIL) DO
          ijl_bn := ijl_bn - 1;
        WHILEND;
        jmv$ijl_p.max_block_in_use := ijl_bn;
      IFEND;

      FREE jmv$ijl_p.block_p^ [ijl_ordinal.block_number].index_p IN osv$mainframe_wired_heap^;
    ELSE
      i#fill ($char(0), jmf$ijle_p (ijl_ordinal), #SIZE (jmt$initiated_job_list_entry));
    IFEND;

    jmv$ijl_p.block_p^ [ijl_ordinal.block_number].in_use_count := jmv$ijl_p.
          block_p^ [ijl_ordinal.block_number].in_use_count - 1;

{  Update jmv$ijl_p.start_search_block if necessary.

    IF jmv$ijl_p.start_search_block > ijl_bn THEN
      jmv$ijl_p.start_search_block := ijl_bn;
    IFEND;

  PROCEND jmp$delete_ijl_entry;

?? TITLE := '[XDCL, #GATE] jmp$get_page_count_of_lw_queue', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$get_page_count_of_lw_queue
    (VAR pages_in_lw_queue: mmt$page_frame_index);

    pages_in_lw_queue := mmv$reassignable_page_frames.swapout_io_not_initiated;

  PROCEND jmp$get_page_count_of_lw_queue;

?? TITLE := '[XDCL, #GATE] jmp$find_and_insert_swapin_cand', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to move all jobs that are in the ready task list to the swapin
{   candidate queues.  A job is prioritized based on its service class attributes and the time that
{   it went ready.  A swapin_candidate_q_timestamp of 0 indicates that the job has been swapped in
{   via an operator command; it should be given high priority.

  PROCEDURE [XDCL, #GATE] jmp$find_and_insert_swapin_cand
    (    current_time: jmt$clock_time);

    VAR
      current_job: jmt$ijl_ordinal,
      ijl_p: ^jmt$initiated_job_list_entry,
      next_job_with_ready_task: jmt$ijl_ordinal,
      status: ost$status;

?? NEWTITLE := '  [inline] prioritize_swapin_candidate', EJECT ??

    PROCEDURE [INLINE] prioritize_swapin_candidate
      (    ijl_p: ^jmt$initiated_job_list_entry;
           current_time: jmt$clock_time);

      VAR
        age_interval: integer,
        new_priority: integer,
        service_class_p: ^jmt$service_class_attributes,
        swap_age_interval: integer;

      IF ijl_p^.job_scheduler_data.swapin_q_priority_timestamp = 0 THEN
        ijl_p^.job_scheduler_data.unaged_swap_queue_priority := UPPERVALUE (jmt$job_priority);
        ijl_p^.job_scheduler_data.priority := UPPERVALUE (jmt$job_priority);
      ELSE
        service_class_p := ^jmv$service_classes [ijl_p^.job_scheduler_data.service_class]^.attributes;
        swap_age_interval := service_class_p^.swap_age_interval;
        IF swap_age_interval <> jmc$unlimited_prio_age_interval THEN
          age_interval := ((current_time - ijl_p^.swap_data.timestamp) DIV swap_age_interval);
        ELSE
          age_interval := 0; { no aging
        IFEND;
        new_priority := service_class_p^.scheduling_priority.minimum +
              service_class_p^.scheduling_priority.ready_task_increment +
              (age_interval * service_class_p^.scheduling_priority.swap_age_increment);
        IF new_priority > service_class_p^.scheduling_priority.maximum THEN
          ijl_p^.job_scheduler_data.unaged_swap_queue_priority :=
                service_class_p^.scheduling_priority.maximum;
          ijl_p^.job_scheduler_data.priority := service_class_p^.scheduling_priority.maximum;
        ELSE
          ijl_p^.job_scheduler_data.unaged_swap_queue_priority := new_priority;
          ijl_p^.job_scheduler_data.priority := new_priority;
        IFEND;
      IFEND;

      ijl_p^.job_scheduler_data.swapin_q_priority_timestamp := current_time;

    PROCEND prioritize_swapin_candidate;
?? OLDTITLE ??
?? EJECT ??

    jmp$incr_scheduler_statistic_sc (jmc$ready_task_event_count);
    jmp$clear_scheduler_event (jmc$ready_task_in_job);
    get_ready_task_list (next_job_with_ready_task);

    WHILE next_job_with_ready_task <> jmv$null_ijl_ordinal DO
      current_job := next_job_with_ready_task;
      ijl_p := jmf$ijle_p (current_job);
      next_job_with_ready_task := ijl_p^.job_scheduler_data.ready_task_link;
      ijl_p^.job_scheduler_data.ready_task_link := jmv$null_ijl_ordinal;
      IF ijl_p^.entry_status <> jmc$ies_ready_task THEN
        osp$system_error ('BAD IJL STAT ON RDY TSK IN JOB', ^status);
      IFEND;
      prioritize_swapin_candidate (ijl_p, current_time);
      jmp$jm_change_ijl_entry_status (ijl_p, jmc$ies_swapin_candidate);
      jmp$insert_swapin_candidate (current_job, current_time);
    WHILEND;

  PROCEND jmp$find_and_insert_swapin_cand;

?? TITLE := '[XDCL, #GATE] jmp$idle_advance_lw_jobs', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$idle_advance_lw_jobs;

    VAR
      dp: jmc$lowest_dispatching_priority .. jmc$highest_dispatch_priority,
      dummy_pages_flushed: mmt$page_frame_index;

    FOR dp := jmc$lowest_dispatching_priority TO jmc$highest_dispatch_priority DO
      jmv$long_wait_swap_threshold [dp] := UPPERVALUE (mmt$page_frame_index);
    FOREND;
    jsp$advance_long_wait_jobs (TRUE {flush_all_pages}, dummy_pages_flushed);

  PROCEND jmp$idle_advance_lw_jobs;

?? TITLE := '[XDCL, #GATE] jmp$idling_swapfile_update', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$idling_swapfile_update
    (    ijlo: jmt$ijl_ordinal;
     VAR status: ost$status);

{  This procedure is called for each job in the swapped out queue when the
{  system is idling.  If the swapfile is on an unavailable device the
{  condition handler will be invoked.

    TYPE
      jxcbl = record
        head: ^ost$execution_control_block,
        lock: ost$signature_lock,
      recend;

    VAR
      buffer_index: tmt$signal_buffers,
      fde_entry_p: gft$file_desc_entry_p,
      flags_updated: boolean,
      highest_page_number: mmt$page_frame_index,
      ignore_status: ost$status,
      ijl_p: ^jmt$initiated_job_list_entry,
      job_fixed: ^array [0 .. 7fffffff(16)] of cell,
      job_fixed_offset_list: ^array [0 .. * ] of integer,
      job_fixed_page_count: mmt$page_frame_index,
      job_page_count: mmt$page_frame_index,
      job_xcb_list: [XREF, oss$job_fixed] record
        head: ^ost$execution_control_block,
        lock: ost$signature_lock,
      recend,
      jxcbl_p: ^jxcbl,
      page_index: mmt$page_frame_index,
      page_number: mmt$page_frame_index,
      queue_id: mmt$job_page_queue_index,
      job_fixed_segn: ost$segment,
      sfd_p: ^jst$swap_file_descriptor,
      signal_found: boolean,
      swap_file_p: ^cell,
      swap_file_segn: ost$segment,
      system_flags: ^PACKED ARRAY [tmc$first_system_flag .. tmc$last_system_flag] OF boolean,
      write_job_fixed: boolean,
      xcb_p: ^ost$execution_control_block;


    PROCEDURE scch
      (    mf: ost$monitor_fault;
           ctc: ^ost$minimum_save_area;
       VAR continue: syt$continue_option);

      osp$set_status_abnormal ('JM', jme$condition_encountered, '', status);
      EXIT jmp$idling_swapfile_update;
    PROCEND scch;


    syp$establish_condition_handler (^scch);
    job_fixed_segn := 0;

  /file_open/
    BEGIN
      ijl_p := jmf$ijle_p (ijlo);
      mmp$open_file_by_sfid (ijl_p^.swap_data.swap_file_sfid, 3, 3, mmc$as_random, mmc$sar_write_extend,
            swap_file_segn, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      swap_file_p := #ADDRESS (1, swap_file_segn, 0);

{  Calculate job page count.  Swapped job page count from the IJL could be wrong if shared
{  pages were removed while the job was in the swapped io complete state (S2).  The job page
{  count was updated to the lower value to keep mmv$reassignable_page_frames.now correct.
{  However all pages were written out and need to be accounted for.

      job_page_count := 0;
      FOR queue_id := LOWERVALUE (mmt$job_page_queue_index) TO UPPERVALUE (mmt$job_page_queue_index) DO
        job_page_count := job_page_count + ijl_p^.swap_data.swapped_job_entry.job_page_queue_count [queue_id];
      FOREND;
      ijl_p^.swap_data.swapped_job_page_count := job_page_count;

      i#build_adaptable_array_ptr (1, swap_file_segn, job_page_count * osv$page_size,
            #SIZE (jst$swapped_page_descriptor) * (ijl_p^.swap_data.swapped_job_entry.
            swap_file_descriptor_page_count + job_page_count), 0, #SIZE (jst$swapped_page_descriptor),
            #LOC (sfd_p));

      IF sfd_p^.ijl_entry.system_supplied_name <> ijl_p^.system_supplied_name THEN
        osp$set_status_abnormal ('JM', jme$bad_swap_file_descriptor, '', status);
        EXIT /file_open/;
      IFEND;

      sfd_p^.ijl_entry := ijl_p^;
      mmp$write_modified_pages (^sfd_p^.ijl_entry, #SIZE (jmt$initiated_job_list_entry), osc$wait, status);
      IF NOT status.normal THEN
        EXIT /file_open/;
      IFEND;

{ There may be gaps in the job fixed pages.  Find the largest page number.
{ Pages of job fixed segment that are not JOB FIXED cannot be counted.

      job_fixed_page_count := ijl_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_fixed];
      highest_page_number := 0;
      FOR page_index := 0 TO job_fixed_page_count - 1 DO
        IF (sfd_p^.swapped_page_descriptors [page_index].pft_entry.queue_id = mmc$pq_job_fixed) AND
             (sfd_p^.swapped_page_descriptors [page_index].pft_entry.sva.asid =
              sfd_p^.ijl_entry.job_fixed_asid) THEN
          page_number := sfd_p^.swapped_page_descriptors [page_index].pft_entry.sva.offset DIV osv$page_size;
          IF page_number > highest_page_number THEN
            highest_page_number := page_number;
          IFEND;
        IFEND;
      FOREND;

{ Copy job fixed from the swap file to a segment so that we have a sequence of bytes that is
{ in pva order.

      PUSH job_fixed_offset_list: [0 .. highest_page_number];
      syp$order_job_fixed_pages (job_fixed_page_count, sfd_p, job_fixed_offset_list, job_fixed,
            job_fixed_segn, status);
      IF NOT status.normal THEN
        EXIT /file_open/;
      IFEND;

      jxcbl_p := ^job_fixed^ [#OFFSET (^job_xcb_list)];
      xcb_p := jxcbl_p^.head;
      write_job_fixed := FALSE;

    /follow_xcb_chain/
      WHILE xcb_p <> NIL DO
        xcb_p := ^job_fixed^ [#OFFSET (xcb_p)];
        syp$update_flags (xcb_p, tmv$ptl_p, flags_updated);
        IF flags_updated THEN
          write_job_fixed := TRUE;
          IF tmc$mainframe_linked_signals IN xcb_p^.system_flags THEN

          /find_buffer/
            FOR buffer_index := LOWERVALUE (tmt$signal_buffers) TO UPPERVALUE (tmt$signal_buffers) DO
              IF xcb_p^.signals.reserved [buffer_index] = FALSE THEN
                tmp$find_mainframe_signal (xcb_p^.global_task_id, signal_found, xcb_p^.signals.
                      buffer [buffer_index]);
                IF signal_found THEN
                  xcb_p^.signals.reserved [buffer_index] := TRUE;
                  xcb_p^.signals.present [buffer_index] := TRUE;
                ELSE
                  system_flags := #LOC (xcb_p^.system_flags);
                  system_flags^ [tmc$mainframe_linked_signals] := FALSE;
                  EXIT /find_buffer/;
                IFEND;
              IFEND;
            FOREND /find_buffer/;

          IFEND;
        IFEND;

        xcb_p := xcb_p^.link;
      WHILEND /follow_xcb_chain/;

      IF write_job_fixed THEN
        syp$write_job_fixed_pages (job_fixed_page_count, job_fixed, sfd_p, status);
      IFEND;

    END /file_open/;

    mmp$free_pages (swap_file_p, 7fffffff(16), osc$wait, ignore_status);
    mmp$close_device_file (swap_file_segn, ignore_status);
    IF job_fixed_segn <> 0 THEN
      mmp$invalidate_segment (job_fixed_segn, 1, NIL, ignore_status);
    IFEND;

  PROCEND jmp$idling_swapfile_update;

?? TITLE := '[XDCL, #GATE] jmp$increment_in_use_count', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$increment_ijl_in_use_count
    (    ijl_block_number: jmt$ijl_block_number);

{  This procedure is called by a ring 2 procedure to modify a ring 1 variable.

    jmv$ijl_p.block_p^ [ijl_block_number].in_use_count :=
          jmv$ijl_p.block_p^ [ijl_block_number].in_use_count + 1;

  PROCEND jmp$increment_ijl_in_use_count;

?? TITLE := '[XDCL, #GATE] jmp$initialize_sched_variables', EJECT ??

{  PURPOSE:
{  This procedure initializes variables used by the job scheduler.
{  This procedure is called by jmp$job_scheduler_ring_3.
{

  PROCEDURE [XDCL, #GATE] jmp$initialize_sched_variables;

    VAR
      dp: jmc$lowest_dispatching_priority .. jmc$highest_dispatch_priority,
      i: integer,
      status: ost$status;

    FOR dp := jmc$lowest_dispatching_priority TO jmc$highest_dispatch_priority DO
      jmv$long_wait_swap_threshold [dp] := mmv$min_avail_pages;
    FOREND;
    jmv$prevent_activation_of_jobs := FALSE;

    FOR i := LOWERBOUND (jmv$operator_request_list.request_list)
          TO UPPERBOUND (jmv$operator_request_list.request_list) DO
      jmv$operator_request_list.request_list [i].in_use := FALSE;
    FOREND;

  PROCEND jmp$initialize_sched_variables;

?? TITLE := '[XDCL, #GATE] jmp$initiate_job_from_scheduler', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$initiate_job_from_scheduler
    (    node: jmt$node;
         ijl_ord: jmt$ijl_ordinal;
         service_class: jmt$service_class_index;
     VAR status: ost$status);

    VAR
      create_status: ost$status,
      time: integer,
      fde_entry_p: gft$file_desc_entry_p,
      ignore_status: ost$status,
      ijl_p: ^jmt$initiated_job_list_entry,
      job_class: jmt$job_class,
      kjl_ord: jmt$kjl_index,
      local_status: ost$status,
      lock_status: ost$status,
      original_ijl_entry: jmt$initiated_job_list_entry,
      parent_fde_p: gft$file_desc_entry_p,
      parent_xcb_p: ^ost$execution_control_block,
      rb: tmt$rb_initiate_job,
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      sdtx_entry_p: ^mmt$segment_descriptor_extended,
      seg: ost$segment,
      segn: ost$segment,
      sfid: gft$system_file_identifier,
      xcb_ptr: ^ost$execution_control_block;

    status.normal := TRUE;


    ijl_p := jmf$ijle_p (ijl_ord);

    job_class := node.job_class;
    kjl_ord := jmv$candidate_queued_jobs [job_class].kjl_index;
    create_and_initialize_job_fixed (job_class, service_class, ijl_ord, xcb_ptr, create_status);
    IF NOT create_status.normal THEN
      status.normal := FALSE;
      RETURN;
    ELSE
      jmv$candidate_queued_jobs [job_class].initiated_job_list_ordinal := ijl_ord;

      IF ijl_p^.entry_status <> jmc$ies_entry_free THEN
        osp$system_error ('INIT ERR', ^ignore_status);
      IFEND;

      i#fill ($char(0), ijl_p, #SIZE (jmt$initiated_job_list_entry));

      time := #FREE_RUNNING_CLOCK (0);

      ijl_p^.ajl_ordinal := jmc$null_ajl_ordinal;
      ijl_p^.job_scheduler_data.swapout_reason := jmc$sr_null;
      ijl_p^.executing_task_count := 0;
      ijl_p^.job_scheduler_data.job_class := job_class;
      ijl_p^.job_scheduler_data.service_class := service_class;
      ijl_p^.job_scheduler_data.priority := jmv$service_classes [service_class]^.attributes.
            scheduling_priority.maximum;
      ijl_p^.job_scheduler_data.swapin_q_priority_timestamp := time;
      ijl_p^.swap_data.timestamp := time;
      ijl_p^.dispatching_control.dispatching_control_index := jmc$min_dispatching_control;
      ijl_p^.dispatching_control.dispatching_priority := jmc$priority_system_job;
      ijl_p^.scheduling_dispatching_priority := jmc$priority_system_job;
      ijl_p^.dispatching_control.service_remaining := jmc$dc_maximum_service_limit;
      ijl_p^.job_name := jmv$candidate_queued_jobs [job_class].user_supplied_name;
      ijl_p^.kjl_ordinal := jmv$candidate_queued_jobs [job_class].kjl_index;
      ijl_p^.system_supplied_name := jmv$candidate_queued_jobs [job_class].system_supplied_name;
      ijl_p^.queue_file_information.job_abort_disposition := jmc$terminate_on_abort;
      ijl_p^.queue_file_information.job_recovery_disposition := jmc$restart_on_recovery;

{ create a new job in the system.

      rb.reqcode := syc$rc_initiate_job;
      rb.xcb_p := xcb_ptr;
      rb.ijlo := ijl_ord;
      rb.ajo := jmc$null_ajl_ordinal;
      seg := #SEGMENT (xcb_ptr);

{ FDE entries must be assigned for all of the job template segments. The FDE entries will exist
{ in the new job's job fixed segment. This segment currently on exists in the system job scheduler
{ task. The FDE's must be created prior to the monitor request to initiate the job, because the job
{ could execute any time after the monitor request completes.

        mmp$get_max_sdt_sdtx_pointer (xcb_ptr, sdt_p, sdtx_p);
        FOR segn := 1 TO mmv$max_template_segment_number DO
          IF sdtx_p^.sdtx_table [segn].open_validating_ring_number <> 0 THEN
            gfp$assign_fde (gfc$tr_null_residence, seg, sfid, fde_entry_p);
            fde_entry_p^.open_count := 1;
            sdtx_p^.sdtx_table [segn].sfid := sfid;
            IF mmc$sa_stack IN sdtx_p^.sdtx_table [segn].software_attribute_set THEN
              fde_entry_p^.stack_for_ring := sdt_p^.st [segn].ste.r1;
              fde_entry_p^.last_segment_number := segn;
            IFEND;
          IFEND;
        FOREND;

    /issue_system_call/
      WHILE TRUE DO

        i#call_monitor (#LOC (rb), #SIZE (rb));
        IF rb.status.normal THEN

{ The linkage between the FDE created in the parent or the scheduler task, and
{ the AST entry for the job-fixed segment must be broken. The segment should only
{ by associated with the new job.

          pmp$find_executing_task_xcb (parent_xcb_p);
          sdtx_entry_p := mmp$get_sdtx_entry_p (parent_xcb_p, seg);
          gfp$get_fde_p (sdtx_entry_p^.sfid, parent_fde_p);
          parent_fde_p^.asti := 0;
          parent_fde_p^.open_count := 0;
          gfp$free_fde (parent_fde_p, sdtx_entry_p^.sfid);
          EXIT /issue_system_call/
        ELSEIF rb.status.condition = tme$ptl_full THEN
          osp$expand_ptl ({ unconditionally_expand } FALSE, local_status);
          IF local_status.normal THEN
            rb.status.normal := TRUE;
          IFEND;
        IFEND;

        IF NOT (rb.status.normal) THEN
          jmp$refresh_job_candidate_class (job_class, FALSE);
          i#fill ($char(0), ijl_p, #SIZE (jmt$initiated_job_list_entry));

{ It is not necessary to delete all of the FDE entries associated with this
{ job, as they only existed in the job fixed segment which is being invalidated.
{ The FDE entry in the job scheduler task must be deleted.

          mmp$invalidate_segment (seg, 1, NIL, local_status);
          IF NOT local_status.normal THEN
            osp$system_error ('SCH CANT DELETE SEG', ^local_status);
          IFEND;
          RETURN;
        IFEND;
      WHILEND /issue_system_call/;

{ set the job monitor task_id into its jobs kjl entry.

      jmv$candidate_queued_jobs [job_class].job_monitor_global_task_id := rb.jmtr_taskid;
      jmp$refresh_job_candidate_class (job_class, TRUE);

{ If the working set maximum for this class is larger than
{ jmv$max_class_working_set then update it.  This job is the only job of this
{ class initiated or it is the 2nd job of the system_job_class.  The System Job
{ is not considered in the value of jmv$max_class_working_set.

      IF (jmv$job_class_table_p^ [job_class].maximum_working_set.maximum > jmv$max_class_working_set) THEN
        jmv$max_class_working_set := jmv$job_class_table_p^ [job_class].maximum_working_set.maximum;
      IFEND;

    IFEND;
  PROCEND jmp$initiate_job_from_scheduler;

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

{ PURPOSE:
{   Insert a swapin candidate into the swapin candidate queue.
{ DESIGN:
{   The swapin candidate queue is ordered highest dispatching priority first.  Within equal dispatching
{   priorities, the queue is ordered highest guaranteed service remaining first.
{   If guaranteed service remaining is equal, the queue is ordered highest scheduling priority first.
{   The following example represents a queue with multiple dispatching priorities:
{   Key = (dispatching priority, guaranteed service remaining, scheduling priority)
{
{     (P9,0,900) --> (P6,0,700) --> (P6,0,300) --> (P4,300,500) --> (P4,200,300) --> (P4,0,700)
{
{   Linkage of the swapin queue is the swapin_candidate_queue field in an initiated_job_list (IJL)
{   entry.  The variable, JMV$SWAPIN_CANDIDATE_QUEUE, contains the ijl ordinal of the head of the queue.
{   To facilitate inserting a job into the queue, the end of each dispatching priority sub-queue is kept.
{   Before comparing scheduling priorities, the job in the queue is aged.

  PROCEDURE [XDCL] jmp$insert_swapin_candidate
    (    swap_cand_ijlo: jmt$ijl_ordinal;
           current_time: jmt$clock_time);

    VAR
      class: jmt$service_class_index,
      end_ijlo: jmt$ijl_ordinal,
      end_ijle_p: ^jmt$initiated_job_list_entry,
      next_ijle_p: ^jmt$initiated_job_list_entry,
      next_ijlo: jmt$ijl_ordinal,
      null_ijlo: jmt$ijl_ordinal,
      prev_ijle_p: ^jmt$initiated_job_list_entry,
      prev_ijlo: jmt$ijl_ordinal,
      status: ost$status,
      swap_cand_dp : jmt$dispatching_priority,
      swap_cand_ijle_p: ^jmt$initiated_job_list_entry;


    swap_cand_ijle_p := jmf$ijle_p (swap_cand_ijlo);
    swap_cand_dp := swap_cand_ijle_p^.scheduling_dispatching_priority;
    swap_cand_ijle_p^.swapin_candidate_queue_dp := swap_cand_dp;
    class := swap_cand_ijle_p^.job_scheduler_data.service_class;

  /insert/
    BEGIN
      IF jmv$swapin_candidate_queue [class].swapin_candidate_queue = jmv$null_ijl_ordinal THEN
        swap_cand_ijle_p^.swapin_candidate_queue := jmv$null_ijl_ordinal;
        jmv$swapin_candidate_queue [class].swapin_candidate_queue := swap_cand_ijlo;
        jmv$swapin_candidate_queue [class].end_of_dp_q [swap_cand_dp] := swap_cand_ijlo;
        EXIT /insert/;
      IFEND;

      end_ijlo := jmv$swapin_candidate_queue [class].end_of_dp_q [swap_cand_dp];
      IF end_ijlo = jmv$null_ijl_ordinal THEN
        find_end_of_higher_dp_q (swap_cand_dp, class, prev_ijlo, null_ijlo);
        IF prev_ijlo <> jmv$null_ijl_ordinal THEN
          prev_ijle_p := jmf$ijle_p (prev_ijlo);
          swap_cand_ijle_p^.swapin_candidate_queue := prev_ijle_p^.swapin_candidate_queue;
          prev_ijle_p^.swapin_candidate_queue := swap_cand_ijlo;
          jmv$swapin_candidate_queue [class].end_of_dp_q [swap_cand_dp] := swap_cand_ijlo;
        ELSE
          swap_cand_ijle_p^.swapin_candidate_queue := jmv$swapin_candidate_queue [class].
                swapin_candidate_queue;
          jmv$swapin_candidate_queue [class].swapin_candidate_queue := swap_cand_ijlo;
          jmv$swapin_candidate_queue [class].end_of_dp_q [swap_cand_dp] := swap_cand_ijlo;
        IFEND;
      ELSE
        end_ijle_p := jmf$ijle_p (end_ijlo);
        adjust_swapin_cand_prio (end_ijle_p, current_time);
        IF (end_ijle_p^.job_scheduler_data.guaranteed_service_remaining > swap_cand_ijle_p^.
              job_scheduler_data.guaranteed_service_remaining) OR
              ((end_ijle_p^.job_scheduler_data.guaranteed_service_remaining = swap_cand_ijle_p^.
              job_scheduler_data.guaranteed_service_remaining) AND (end_ijle_p^.
              job_scheduler_data.priority >= swap_cand_ijle_p^.job_scheduler_data.priority)) THEN
          swap_cand_ijle_p^.swapin_candidate_queue := end_ijle_p^.swapin_candidate_queue;
          end_ijle_p^.swapin_candidate_queue := swap_cand_ijlo;
          jmv$swapin_candidate_queue [class].end_of_dp_q [swap_cand_dp] := swap_cand_ijlo;
        ELSE
          find_end_of_higher_dp_q (swap_cand_dp, class, prev_ijlo, next_ijlo);

        /find_place_in_queue/
          WHILE next_ijlo <> end_ijlo DO
            next_ijle_p := jmf$ijle_p (next_ijlo);
            adjust_swapin_cand_prio (next_ijle_p, current_time);
            IF (swap_cand_ijle_p^.job_scheduler_data.guaranteed_service_remaining > next_ijle_p^.
                  job_scheduler_data.guaranteed_service_remaining) OR
                  ((swap_cand_ijle_p^.job_scheduler_data.guaranteed_service_remaining = next_ijle_p^.
                  job_scheduler_data.guaranteed_service_remaining) AND (swap_cand_ijle_p^.job_scheduler_data.
                  priority > next_ijle_p^.job_scheduler_data.priority)) THEN
              EXIT /find_place_in_queue/;
            IFEND;

            prev_ijlo := next_ijlo;
            next_ijlo := next_ijle_p^.swapin_candidate_queue;

          WHILEND /find_place_in_queue/;

{ Link the swapin candidate into the queue.

          IF prev_ijlo = jmv$null_ijl_ordinal THEN
            swap_cand_ijle_p^.swapin_candidate_queue := jmv$swapin_candidate_queue [class].
                  swapin_candidate_queue;
            jmv$swapin_candidate_queue [class].swapin_candidate_queue := swap_cand_ijlo;
          ELSE
            jmf$ijle_p (prev_ijlo) ^.swapin_candidate_queue := swap_cand_ijlo;
            swap_cand_ijle_p^.swapin_candidate_queue := next_ijlo;
          IFEND;
        IFEND;
      IFEND;
    END /insert/;

{ Increment the number of jobs in the queue.

    jmv$swapin_candidate_queue [class].number_of_jobs_in_queue :=
          jmv$swapin_candidate_queue [class].number_of_jobs_in_queue + 1;

  PROCEND jmp$insert_swapin_candidate;

?? TITLE := '[XDCL, #GATE] jmp$perform_physical_swapout', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$perform_physical_swapout
    (    node: jmt$node,
         swapout_reason: jmt$swapout_reasons,
         class: jmt$service_class_index;
         memory_needed: mmt$page_frame_index;
     VAR status: ost$status);

    VAR
      ajl_ord: jmt$ajl_ordinal,
      count: jmt$ajl_ordinal,
      ijl_ord: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      new_total_swapped_jobs: integer,
      service_table_p: ^jmt$service_class_attributes;

    status.normal := TRUE;

    ijl_ord := node.ijl_ord;
    ijle_p := jmf$ijle_p (ijl_ord);
    ajl_ord := ijle_p^.ajl_ordinal;

    IF (jmv$ajl_p^ [ajl_ord].in_use = 0) OR (ajl_ord = jmv$system_ajl_ordinal) THEN
      osp$set_status_abnormal ('JM', jme$swapping_not_allowed, 'Swapping non existent or system job', status);
      RETURN;
    IFEND;

    CASE ijle_p^.entry_status OF
    = jmc$ies_entry_free, jmc$ies_job_swapped =
      osp$set_status_abnormal ('JM', jme$job_cant_be_swapped, 'ijl entry status prevents swapout', status);
      RETURN;
    = jmc$ies_job_in_memory_non_swap =
      osp$set_status_abnormal ('JM', jme$job_status_non_swappable, 'ijl status non swappable', status);
      RETURN;
    ELSE
    CASEND;

    jsp$swap_job_out (ijl_ord, swapout_reason, memory_needed, status);
    IF NOT status.normal THEN
      IF status.condition = jse$unimplemented_subfunction THEN
        osp$system_error ('Unimplemented swapout subfunction', ^status);
      IFEND;
      RETURN;
    IFEND;

    service_table_p := ^jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes;
    ijle_p^.job_scheduler_data.unaged_swap_queue_priority := service_table_p^.scheduling_priority.minimum;
    ijle_p^.job_scheduler_data.priority := service_table_p^.scheduling_priority.minimum;
    IF service_table_p^.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 <
          service_table_p^.guaranteed_service_quantum THEN
      ijle_p^.job_scheduler_data.guaranteed_service_remaining := service_table_p^.guaranteed_service_quantum -
            ijle_p^.job_scheduler_data.service_accumulator_since_swap;
    IFEND;

    IF swapout_reason = jmc$sr_lower_priority THEN
      jmp$incr_scheduler_statistic_sc (jmc$lower_prio_swap_count);
    IFEND;

  PROCEND jmp$perform_physical_swapout;

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

{ PURPOSE:
{   This procedure scans the IJL to find jobs with the job_damaged_during_recovery field set, and
{   change their entry status so they can never swap in.
{ DESIGN:
{   If the swapout request gets back a status that the job is in a ready task state, the event is reset.
{   Scheduler's event processing loop (in ring 3) will process the ready task list, and the next time
{   through this procedure the job will no longer be in the ready task entry status.

  PROCEDURE [XDCL, #GATE] jmp$process_damaged_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,
      status: ost$status;

    jmp$clear_scheduler_event (jmc$recovery_job_damaged);

    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;
          ijle_p := jmf$ijle_p (ijl_ordinal);
          IF ijle_p^.entry_status <> jmc$ies_entry_free THEN
            IF (ijle_p^.job_damaged_during_recovery) AND (ijle_p^.entry_status <> jmc$ies_job_damaged) THEN
              jmp$special_job_swapout (ijl_ordinal, ijle_p, jmc$sr_job_damaged, status);
              IF NOT status.normal THEN
                IF status.condition = jme$job_in_ready_task_state THEN
                  jmv$job_scheduler_event [jmc$recovery_job_damaged] := TRUE;
                IFEND;
              IFEND;
            IFEND;
          IFEND;
        FOREND;
      IFEND;
    FOREND;

  PROCEND jmp$process_damaged_jobs;

?? TITLE := '[XDCL, #GATE] jmp$process_operator_requests', EJECT ??

{ PURPOSE:
{   This procedure is called by the job scheduler to process operator request events
{     in the service class table:
{       swapin_job,
{       swapout_job,
{       change_dispatching_priority.
{   If the swapout_job request gets back a status that the job is in a ready task state,
{   the job is left in the operator request list and the event is reset.  Scheduler's event
{   processing loop (in ring 3) will process the ready task list, and the next time through
{   this procedure the job will no longer be in the ready task entry status.
{   Note:  There is no need for an interlock of the jmv$operator_request_list table since
{         the 'in_use' field is cleared at the end of the processing.

  PROCEDURE [XDCL, #GATE] jmp$process_operator_requests
    (VAR status: ost$status);

    VAR
      dispatching_control_info: jmt$dispatching_control_info,
      ijle_p: ^jmt$initiated_job_list_entry,
      i: jmt$request_list_index,
      time: jmt$clock_time;

    status.normal := TRUE;

  /process_operator_requests/
    FOR i := LOWERBOUND (jmv$operator_request_list.request_list)
          TO UPPERBOUND (jmv$operator_request_list.request_list) DO
      IF jmv$operator_request_list.request_list [i].in_use THEN
        CASE jmv$operator_request_list.request_list [i].operator_request OF

        = jmc$or_swapout =
          jmp$sched_swapout_job (jmv$operator_request_list.request_list [i].ijl_ordinal,
                jmv$operator_request_list.request_list [i].system_supplied_name, status);
          IF NOT status.normal THEN
            IF status.condition = jme$job_in_ready_task_state THEN
              jmv$job_scheduler_event [jmc$process_operator_request] := TRUE;
              status.normal := TRUE;
              CYCLE /process_operator_requests/;
            IFEND;
          IFEND;

        = jmc$or_swapin =
          jmp$sched_swapin_job (jmv$operator_request_list.request_list [i].ijl_ordinal,
                jmv$operator_request_list.request_list [i].system_supplied_name, status);

        = jmc$or_change_dispatching_prio =
          dispatching_control_info.dispatching_priority := jmv$operator_request_list.request_list [i].
                dispatching_priority;
          jmp$change_dispatching_prior_r1 (tmc$cpo_operator,
                jmv$operator_request_list.request_list [i].ijl_ordinal,
                jmv$operator_request_list.request_list [i].system_supplied_name, dispatching_control_info,
                status);
          ijle_p := jmf$ijle_p (jmv$operator_request_list.request_list [i].ijl_ordinal);
          IF ijle_p^.entry_status = jmc$ies_swapin_candidate THEN
            jmp$delete_swapin_candidate (jmv$operator_request_list.request_list [i].ijl_ordinal,
                  ijle_p^.job_scheduler_data.service_class);
            time := #FREE_RUNNING_CLOCK (0);
            jmp$insert_swapin_candidate (jmv$operator_request_list.request_list [i].ijl_ordinal, time);
          IFEND;
        ELSE

        CASEND;

        jmv$operator_request_list.request_list [i].in_use := FALSE;

        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    FOREND /process_operator_requests/;

  PROCEND jmp$process_operator_requests;
?? TITLE := '[XDCL, #GATE] jmp$process_subsyst_prio_change', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to scan the swapin candidate queues to
{   find jobs that have had their scheduling dispatching priority changed
{   because of subsystem locks.
{ DESIGN:
{   Clear the event, copy the set of queues to consider, then process the queues.

  PROCEDURE [XDCL, #GATE] jmp$process_subsyst_prio_change;

    VAR
      head: jmt$ijl_ordinal,
      ijlo: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      last_ijle_p: ^jmt$initiated_job_list_entry,
      next_ijlo: jmt$ijl_ordinal,
      service_class: jmt$service_class_index,
      time: jmt$clock_time;

    jmp$incr_scheduler_statistic_sc (jmc$change_subsystem_priority);
    jmp$clear_scheduler_event (jmc$subsystem_priority_change);

    FOR service_class := jmc$system_service_class TO jmv$max_service_class_in_use DO
      IF jmv$subsystem_priority_changes [service_class] THEN
        jmv$subsystem_priority_changes [service_class] := FALSE;
        head := jmv$swapin_candidate_queue [service_class].swapin_candidate_queue;
        IF head <> jmv$null_ijl_ordinal THEN
          last_ijle_p := jmf$ijle_p (head);
          ijlo := last_ijle_p^.swapin_candidate_queue;
          WHILE ijlo <> jmv$null_ijl_ordinal DO
            ijle_p := jmf$ijle_p (ijlo);
            next_ijlo := ijle_p^.swapin_candidate_queue;
            IF ijle_p^.scheduling_dispatching_priority > last_ijle_p^.scheduling_dispatching_priority THEN
              jmp$delete_swapin_candidate (ijlo, service_class);
              time := #FREE_RUNNING_CLOCK (0);
              jmp$insert_swapin_candidate (ijlo, time);
            ELSE
              last_ijle_p := ijle_p;
            IFEND;
            ijlo := next_ijlo;
          WHILEND;
        IFEND;
      IFEND;
    FOREND;
  PROCEND jmp$process_subsyst_prio_change;

?? TITLE := '[XDCL, #GATE] jmp$process_terminated_job', EJECT ??

{ PURPOSE:
{   This procedure scans all IJL blocks with the terminated_jobs field set.  All jobs with entry status
{   of job terminating are removed from the IJL and queued files is called to cleanup the KJL entry.

  PROCEDURE [XDCL, #GATE] jmp$process_terminated_job;

    VAR
      class: jmt$service_class_index,
      job_class: jmt$job_class,
      ijl_p: ^jmt$initiated_job_list_entry,
      kjl_ord: jmt$kjl_index,
      ijl_bn: jmt$ijl_block_number,
      status: ost$status,
      ijl_bi: jmt$ijl_block_index,
      ijl_ord: jmt$ijl_ordinal;

    jmp$incr_scheduler_statistic_sc (jmc$job_terminated_event_count);
    jmp$clear_scheduler_event (jmc$job_terminated);

    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) AND (jmv$ijl_p.block_p^ [ijl_bn].terminated_job) THEN

{ Clear the terminated job indicator, and then find all terminated jobs in the block.

        jmv$ijl_p.block_p^ [ijl_bn].terminated_job := FALSE;

      /index_loop/
        FOR ijl_bi := LOWERVALUE (jmt$ijl_block_index) TO UPPERVALUE (jmt$ijl_block_index) DO

{  Check if last time through this block was deleted by jmp$delete_ijl_entry.

          IF jmv$ijl_p.block_p^ [ijl_bn].index_p <> NIL THEN
            ijl_p := ^jmv$ijl_p.block_p^ [ijl_bn].index_p^ [ijl_bi];
            IF ijl_p^.entry_status = jmc$ies_job_terminating THEN
              kjl_ord := ijl_p^.kjl_ordinal;
              class := ijl_p^.job_scheduler_data.service_class;
              job_class := ijl_p^.job_scheduler_data.job_class;

              ijl_ord.block_number := ijl_bn;
              ijl_ord.block_index := ijl_bi;
              IF (class IN jmv$classes_in_maxaj_limit_wait) THEN
                jmp$set_class_below_maxaj_limit ($jmt$service_class_set [class]);
              IFEND;

              jmp$jm_change_ijl_entry_status (ijl_p, jmc$ies_entry_free);

              IF (ijl_p^.swap_queue_link.queue_id <> jsc$isqi_null) THEN
                osp$system_error ('JOB END WITH JOB IN SWAP QUEUE', ^status);
              IFEND;

              jmp$delete_ijl_entry (ijl_ord);

              jmp$notify_queued_files_job_end (kjl_ord);

{ If there are no longer any jobs in this class and the maximum working set for
{ this class was the working set ceiling, the global maximum needs to be reset.
{ Note that since the System Job is not considered in the value of
{ jmv$max_class_working_set, the check is for an initiated_jobs count of zero
{ if the job class is NOT the system_job_class and a count of 1 if the job
{ class is the system_job_class.

              IF (jmv$job_counts.job_class_counts [job_class].initiated_jobs =
                    $INTEGER (jmc$system_job_class = job_class)) AND
                    (jmv$job_class_table_p^ [job_class].maximum_working_set.maximum =
                    jmv$max_class_working_set) THEN
                jmp$reset_max_class_working_set;
              IFEND;

              IF (jmv$job_counts.initiated_jobs = 1) AND (gtid_of_task_waiting_for_idle.index > 0) THEN
                pmp$ready_task (gtid_of_task_waiting_for_idle, status);
                gtid_of_task_waiting_for_idle.index := 0;
              IFEND;
            IFEND;
          ELSE
            EXIT /index_loop/;
          IFEND;
        FOREND /index_loop/;
      IFEND;
    FOREND;

  PROCEND jmp$process_terminated_job;

?? TITLE := '[XDCL, #GATE] jmp$recover_swapin_jobs', EJECT ??

{ PURPOSE:
{   This procedure issues a monitor request to ready recovered jobs so that they get queued to swapin.

  PROCEDURE [XDCL, #GATE] jmp$recover_swapin_jobs;

    VAR
      request_block: jmt$rb_scheduler_requests;

    jmp$incr_scheduler_statistic_sc (jmc$recovery_swapin_event_count);
    jmp$clear_scheduler_event (jmc$recovery_swapin);

    request_block.reqcode := syc$rc_job_scheduler_request;
    request_block.sub_reqcode := jmc$src_swapin_recovered_jobs;
    i#call_monitor (#LOC (request_block), #SIZE (request_block));

  PROCEND jmp$recover_swapin_jobs;

?? TITLE := '[XDCL, #GATE] jmp$queue_operator_request', EJECT ??

{ PURPOSE:
{   This procedure queues operator requests (swapin job, swapout job, change
{   dispatching control in the service class table) and sets a scheduler event.
{   Processing these requests must be synchronized by the scheduler.
{   The swapout job request can indicate that a job is not to be recovered
{   during a subsequent deadstart.

  PROCEDURE [XDCL, #GATE] jmp$queue_operator_request
    (    operator_request: jmt$operator_request;
         ijl_ordinal: jmt$ijl_ordinal;
         system_supplied_name: jmt$system_supplied_name;
         dispatching_priority: jmt$dispatching_priority;
         disable_recovery: boolean;
     VAR status: ost$status);

    VAR
      i: jmt$request_list_index,
      ijle_p: ^jmt$initiated_job_list_entry,
      service_class_name: jmt$service_class_name;

    status.normal := TRUE;

    osp$set_signature_lock (jmv$operator_request_list.lock, osc$wait, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /search_for_free_entry/

    FOR i := LOWERBOUND (jmv$operator_request_list.request_list)
          TO UPPERBOUND (jmv$operator_request_list.request_list) DO
      IF NOT jmv$operator_request_list.request_list [i].in_use THEN
        jmv$operator_request_list.request_list [i].ijl_ordinal := ijl_ordinal;
        jmv$operator_request_list.request_list [i].system_supplied_name := system_supplied_name;
        jmv$operator_request_list.request_list [i].operator_request := operator_request;
        jmv$operator_request_list.request_list [i].dispatching_priority := dispatching_priority;
        jmv$operator_request_list.request_list [i].in_use := TRUE;

{  Notify the scheduler that an operator request event has occurred.

        jmp$set_event_and_ready_sched (jmc$process_operator_request);

        ijle_p := jmf$ijle_p (ijl_ordinal);
        IF disable_recovery THEN
          ijle_p^.queue_file_information.job_abort_disposition := jmc$terminate_on_abort;
          ijle_p^.queue_file_information.job_recovery_disposition := jmc$terminate_on_recovery;
        IFEND;
        IF (operator_request = jmc$or_swapin) AND (jmv$service_classes
              [ijle_p^.job_scheduler_data.service_class]^.attributes.maximum_active_jobs = 0) THEN
          jmp$determine_serv_class_name (ijle_p^.job_scheduler_data.service_class, service_class_name,
                status);
          osp$set_status_abnormal ('JM', jme$swapin_with_maxaj_zero, service_class_name, status);
          osp$append_status_parameter (osc$status_parameter_delimiter, system_supplied_name, status);
        IFEND;
        osp$clear_signature_lock (jmv$operator_request_list.lock, status);
        RETURN;
      IFEND;
    FOREND /search_for_free_entry/;

    osp$clear_signature_lock (jmv$operator_request_list.lock, status);
    osp$set_status_abnormal ('JM', jme$swap_buffer_full, 'Request cannot be processed now', status);

  PROCEND jmp$queue_operator_request;
?? TITLE := '[XDCL, #GATE] jmp$relink_to_end_of_swapin_q', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$relink_to_end_of_swapin_q
    (    ijl_ordinal: jmt$ijl_ordinal);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      status: ost$status,
      time: jmt$clock_time;

    ijle_p := jmf$ijle_p (ijl_ordinal);

    IF ijle_p^.entry_status = jmc$ies_swapin_candidate THEN
      jmp$delete_swapin_candidate (ijl_ordinal, ijle_p^.job_scheduler_data.service_class);
      ijle_p^.job_scheduler_data.unaged_swap_queue_priority := LOWERVALUE (jmt$job_priority);
      ijle_p^.job_scheduler_data.priority := LOWERVALUE (jmt$job_priority);
      time := #FREE_RUNNING_CLOCK (0);
      jmp$insert_swapin_candidate (ijl_ordinal, time);
    ELSE
      osp$system_error ('RELINK SWAPIN CANDIDATE NOT IN QUEUE', ^status);
    IFEND;

  PROCEND jmp$relink_to_end_of_swapin_q;

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

{ PURPOSE:
{   This procedure reorders the swapin candidate queue for job classes which have had dispatching
{   controls (dispatching priority) changed by operator command.
{ DESIGN:
{   All swapin candidates for changed classes are removed from the swapin candidate queue and
{   then re-inserted.  The insert procedure places jobs in the queue in order of priority.

  PROCEDURE [XDCL] jmp$reorder_swapin_queues
    (    class_set: jmt$service_class_set);

    VAR
      class: jmt$service_class_index,
      count: integer,
      index: integer,
      swapin_candidate_ijlos_p: ^array [1 .. * ] of jmt$ijl_ordinal,
      time: jmt$clock_time;

    PUSH swapin_candidate_ijlos_p: [1 .. jmv$job_counts.initiated_jobs];

    FOR class := jmc$system_service_class TO jmv$max_service_class_in_use DO
      IF class IN class_set THEN
        count := 0;
        WHILE jmv$swapin_candidate_queue [class].swapin_candidate_queue <> jmv$null_ijl_ordinal DO
          count := count + 1;
          swapin_candidate_ijlos_p^ [count] := jmv$swapin_candidate_queue [class].swapin_candidate_queue;
          jmp$delete_swapin_candidate (jmv$swapin_candidate_queue [class].swapin_candidate_queue, class);
        WHILEND;

        time := #FREE_RUNNING_CLOCK (0);
        FOR index := 1 TO count DO
          jmp$insert_swapin_candidate (swapin_candidate_ijlos_p^ [index], time);
        FOREND;
      IFEND;
    FOREND;

  PROCEND jmp$reorder_swapin_queues;

?? TITLE := '[XDCL, #GATE] jmp$reset_activate_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$reset_activate_event;

    VAR
      any_swapped: boolean,
      any_queued: boolean,
      job_class: jmt$job_class,
      sc_ijle_p: ^jmt$initiated_job_list_entry,
      service_class: jmt$service_class_index;

    any_queued := FALSE;
    any_swapped := FALSE;

  /queued_job_count/

    FOR job_class := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
      IF (jmv$candidate_queued_jobs [job_class].candidate_available) AND
            NOT (jmv$job_class_table_p^ [job_class].initial_service_class_index IN
            jmv$classes_in_maxaj_limit_wait) AND (NOT jmv$idle_dispatching_controls.
            controls [jmv$service_classes [jmv$job_class_table_p^ [job_class].initial_service_class_index]^.
            attributes.dispatching_control [jmc$min_dispatching_control].dispatching_priority].blocked) THEN
        any_queued := TRUE;
      IFEND;
    FOREND /queued_job_count/;

  /swapped_job_count/

    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 (service_class IN jmv$classes_in_maxaj_limit_wait) THEN
        sc_ijle_p := jmf$ijle_p (jmv$swapin_candidate_queue [service_class].swapin_candidate_queue);
        IF NOT jmv$idle_dispatching_controls.controls [sc_ijle_p^.scheduling_dispatching_priority].
              blocked THEN
          any_swapped := TRUE;
        IFEND;
      IFEND;
    FOREND /swapped_job_count/;

    IF any_queued THEN
      jmv$job_scheduler_event [jmc$examine_input_queue] := TRUE;
    IFEND;

    IF any_swapped THEN
      jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
    IFEND;

  PROCEND jmp$reset_activate_event;

?? TITLE := '[XDCL, #GATE] jmp$reset_activate_events_sels', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$reset_activate_events_sels;

    jmv$job_scheduler_event [jmc$scheduler_wake_time] := FALSE;
    jmv$job_scheduler_event [jmc$needed_memory_available] := FALSE;
    jmv$job_scheduler_event [jmc$needed_ajlo_available] := FALSE;

    jmv$job_sched_events_selected [jmc$scheduler_wake_time] := FALSE;
    jmv$job_sched_events_selected [jmc$needed_memory_available] := FALSE;
    jmv$job_sched_events_selected [jmc$needed_ajlo_available] := FALSE;

    jmv$job_scheduler_event [jmc$examine_input_queue] := FALSE;
    jmv$job_scheduler_event [jmc$examine_swapin_queue] := FALSE;
    jmv$job_sched_events_selected [jmc$examine_input_queue] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_swapin_queue] := TRUE;

    jmv$classes_in_resource_wait := $jmt$service_class_set [];
    jmv$memory_needed_by_scheduler := 0;
    jmv$time_to_wake_scheduler := jmv$sched_service_calc_time;

  PROCEND jmp$reset_activate_events_sels;

?? TITLE := '[XDCL, #GATE] jmp$reset_advance_lw_swaps', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$reset_advance_lw_swaps
    (VAR memory_flushed_from_lw_queue: mmt$page_frame_index);

    VAR
      dp: jmc$lowest_dispatching_priority .. jmc$highest_dispatch_priority,
      job_class: jmt$job_class,
      next_ijle_p: ^jmt$initiated_job_list_entry,
      next_ijlo: jmt$ijl_ordinal,
      number_to_maxaj_p: ^array [1 .. * ] of integer,
      pages_needed: jmt$long_wait_swap_threshold,
      service_class: jmt$service_class_index,
      status: ost$status;

    FOR dp := jmc$lowest_dispatching_priority TO jmc$highest_dispatch_priority DO
      pages_needed [dp] := 0;
    FOREND;

    PUSH number_to_maxaj_p: [jmc$system_service_class .. jmv$max_service_class_in_use];

{ Determine the total number of pages that job mode scheduler needs to swapin or initiate all
{ swapin or initiation candidates (exclude the working sets of jobs belonging to a class that
{ has reached the maximum active job limit).

    FOR service_class := jmc$system_service_class TO jmv$max_service_class_in_use DO
      IF jmv$service_classes [service_class] <> NIL THEN
        number_to_maxaj_p^ [service_class] := jmv$service_classes [service_class]^.attributes.
              maximum_active_jobs - (jmv$job_counts.service_class_counts [service_class].
              scheduler_initiated_jobs - jmv$job_counts.service_class_counts [service_class].swapped_jobs);
        IF (jmv$swapin_candidate_queue [service_class].swapin_candidate_queue <> jmv$null_ijl_ordinal) THEN
          next_ijlo := jmv$swapin_candidate_queue [service_class].swapin_candidate_queue;
          WHILE (number_to_maxaj_p^ [service_class] > 0) AND (next_ijlo <> jmv$null_ijl_ordinal) DO
            next_ijle_p := jmf$ijle_p (next_ijlo);
            IF next_ijle_p^.scheduling_dispatching_priority > jmc$highest_dispatch_priority THEN
              dp := jmc$highest_dispatch_priority;
            ELSE
              dp := next_ijle_p^.scheduling_dispatching_priority;
            IFEND;
            pages_needed [dp] := pages_needed [dp] + next_ijle_p^.swap_data.swapped_job_page_count +
                  next_ijle_p^.memory_reserve_request.requested_page_count;
            number_to_maxaj_p^ [service_class] := number_to_maxaj_p^ [service_class] - 1;
            next_ijlo := next_ijle_p^.swapin_candidate_queue;
          WHILEND;
        IFEND;
      IFEND;
    FOREND;

    FOR job_class := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
      IF (jmv$candidate_queued_jobs [job_class].candidate_available) THEN
        service_class := jmv$job_class_table_p^ [job_class].initial_service_class_index;
        dp := jmv$service_classes [service_class]^.attributes.dispatching_control
              [jmc$min_dispatching_control].dispatching_priority;
        IF number_to_maxaj_p^ [service_class] > 0 THEN
          pages_needed [dp] := pages_needed [dp] + jmv$job_class_table_p^ [job_class].initial_working_set;
          number_to_maxaj_p^ [service_class] := number_to_maxaj_p^ [service_class] - 1;
        IFEND;
      IFEND;
    FOREND;

{ Accumulate downward through the array.  The threshold is the number of pages needed to swapin jobs of equal
{ or higher priority.  If the actual number of pages needed is less than mmv$min_avail_pages, then the
{ threshold is mmv$min_avail_pages.

    FOR dp := jmc$highest_dispatch_priority - 1 DOWNTO jmc$lowest_dispatching_priority DO
      pages_needed [dp] := pages_needed [dp + 1] + pages_needed [dp];
      IF ((pages_needed [dp + 1]) + mmv$resident_job_target) > mmv$min_avail_pages THEN
        jmv$long_wait_swap_threshold [dp +1] := pages_needed [dp + 1] + mmv$resident_job_target;
      ELSE
        jmv$long_wait_swap_threshold [dp + 1] := mmv$min_avail_pages;
      IFEND;
    FOREND;
    IF (pages_needed [jmc$lowest_dispatching_priority] + mmv$resident_job_target) > mmv$min_avail_pages THEN
      jmv$long_wait_swap_threshold [jmc$lowest_dispatching_priority] :=
            pages_needed [jmc$lowest_dispatching_priority] + mmv$resident_job_target;
    ELSE
      jmv$long_wait_swap_threshold [jmc$lowest_dispatching_priority] := mmv$min_avail_pages;
    IFEND;

    IF ((mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon) <
          pages_needed [jmc$lowest_dispatching_priority]) AND
          (mmv$reassignable_page_frames.swapout_io_not_initiated > 0) THEN
      jsp$advance_long_wait_jobs (FALSE {flush_all_pages}, memory_flushed_from_lw_queue);
      jmp$incr_scheduler_statistic_sc (jmc$called_advance_lw_job);
    IFEND;

  PROCEND jmp$reset_advance_lw_swaps;

?? TITLE := '[XDCL, #GATE] jmp$reset_ijl_search_block', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$reset_ijl_search_block
    (    ijl_block_number: jmt$ijl_block_number);

{  This procedure is called by a ring 2 procedure to modify a ring 1 variable.

    jmv$ijl_p.start_search_block := ijl_block_number;

  PROCEND jmp$reset_ijl_search_block;

?? TITLE := '[XDCL, #GATE] jmp$reset_time_to_wake_sched', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$reset_time_to_wake_sched;

    jmv$time_to_wake_scheduler := #FREE_RUNNING_CLOCK (0) + 10000000;

  PROCEND jmp$reset_time_to_wake_sched;

?? TITLE := '[XDCL, #GATE] jmp$restore_job_environment', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$restore_job_environment
    (    node: jmt$node;
     VAR status: ost$status);

    VAR
      class: jmt$service_class_index,
      ijl_p: ^jmt$initiated_job_list_entry;

    status.normal := TRUE;
    ijl_p := jmf$ijle_p (node.ijl_ord);
    class := ijl_p^.job_scheduler_data.service_class;

    IF (ijl_p^.entry_status <> jmc$ies_swapin_candidate) THEN
      osp$system_error ('SWAPIN FREE IJL ENTRY', ^status);
    IFEND;

    jsp$swap_job_in (node.ijl_ord, status);

    jmp$delete_swapin_candidate (node.ijl_ord, class);

{ If the job was swapped out with service remaining (ie, it was preempted before reaching guaranteed
{ service), it was swapped back in ahead of other jobs.  Do not zero out service_accumulator_since_swap--
{ the job must become a preemption candidate again when it uses its guaranteed service allotment.

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

  PROCEND jmp$restore_job_environment;

?? TITLE := '[XDCL, #GATE] jmp$resume_activation_of_jobs', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$resume_activation_of_jobs;

    VAR
      dp: jmc$lowest_dispatching_priority .. jmc$highest_dispatch_priority,
      status: ost$status;

    FOR dp := jmc$lowest_dispatching_priority TO jmc$highest_dispatch_priority DO
      jmv$long_wait_swap_threshold [dp] := mmv$min_avail_pages;
    FOREND;
    jmv$prevent_activation_of_jobs := FALSE;
    jmv$all_jobs_swapped_for_idling := FALSE;
    jmv$refresh_job_candidates := TRUE;
    jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
    jmv$job_scheduler_event [jmc$examine_input_queue] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_input_queue] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_swapin_queue] := TRUE;
    jmv$refresh_job_candidates := TRUE;

    tmp$ready_system_task (tmc$stid_job_scheduler, status);

  PROCEND jmp$resume_activation_of_jobs;

?? TITLE := '[XDCL, #GATE] jmp$scan_ajl_for_service', EJECT ??

{ PURPOSE:
{   This procedure scans the ajl and calls the procedure which calculates service for all active jobs.

  PROCEDURE [XDCL, #GATE] jmp$scan_ajl_for_service;

    VAR
      ajlo: jmt$ajl_ordinal,
      class: jmt$service_class_index,
      ijle_p: ^jmt$initiated_job_list_entry,
      ijlo: jmt$ijl_ordinal,
      service_used: integer;

  /search_for_job/
    FOR ajlo := LOWERBOUND (jmv$ajl_p^) TO jmv$max_ajl_ordinal_in_use DO
      IF (jmv$ajl_p^ [ajlo].in_use <> 0) AND (ajlo <> jmv$system_ajl_ordinal) THEN
        ijlo := jmv$ajl_p^ [ajlo].ijl_ordinal;
        ijle_p := jmf$ijle_p (ijlo);
        IF (ijle_p^.ajl_ordinal = ajlo) AND (ijle_p^.swap_status = jmc$iss_executing) THEN
          jmp$calculate_service (ijle_p, service_used);
          adjust_active_job_priority (ijle_p, service_used);
        IFEND;
      IFEND;
    FOREND /search_for_job/;

    jmv$last_service_calc_time := #FREE_RUNNING_CLOCK (0);

{ Clear out the set of service classes which are not being considered for activation because
{ the service class has reached the maximum active job limit; now that service accumulations
{ and priorities have changed for the active jobs, one of them may be preemptable.

    IF jmv$classes_in_maxaj_limit_wait <> $jmt$service_class_set [] THEN
      jmv$classes_in_maxaj_limit_wait := $jmt$service_class_set [];
      jmv$job_scheduler_event [jmc$examine_input_queue] := TRUE;
      jmv$job_scheduler_event [jmc$examine_swapin_queue] := TRUE;
    IFEND;

  PROCEND jmp$scan_ajl_for_service;

?? TITLE := '[XDCL, #GATE] jmp$sched_swapin_job', EJECT ??

{ PURPOSE:
{   This procedure processes swapin requests from the operator request list.
{ DESIGN:
{   Issue a monitor request to swapin the job ONLY if the entry status is operator force out.
{  *** This could be made more tolerant, so that 'lost' jobs (which would represent
{   a bug) could be swapped in with an operator request.

  PROCEDURE [XDCL, #GATE] jmp$sched_swapin_job
    (    ijl_ordinal: jmt$ijl_ordinal;
         system_supplied_name: jmt$system_supplied_name;
     VAR status: ost$status);

    VAR
      ijl_p: ^jmt$initiated_job_list_entry,
      request_block: jmt$rb_scheduler_requests,
      time: jmt$clock_time;

    status.normal := TRUE;

    IF (ijl_ordinal = jmv$system_ijl_ordinal) THEN
      osp$set_status_abnormal ('JM', jme$swapping_not_allowed, 'SWAPPING THE SYSTEM JOB NOT ALLOWED', status);
      RETURN;
    IFEND;

    ijl_p := jmf$ijle_p (ijl_ordinal);

    IF ijl_p^.system_supplied_name = system_supplied_name THEN
      CASE ijl_p^.entry_status OF
      = jmc$ies_operator_force_out =

{ Issue a monitor request to change the entry status of the job and swap the job in if it has
{ ready tasks.  This must be done in monitor to prevent timing problems checking the ready task count.

        request_block.reqcode := syc$rc_job_scheduler_request;
        request_block.sub_reqcode := jmc$src_operator_swap_in;
        request_block.ijl_ordinal := ijl_ordinal;
        i#call_monitor (#LOC (request_block), #SIZE (request_block));
      = jmc$ies_swapin_candidate =

{ If the job is already a swapin candidate then take the job out of the swapin queue, give
{ it the highest priority, and put it back in the swapin queue (highest priority will ensure
{ that it is placed at the head of the queue).  Because the job is already marked as a swapin
{ candidate, monitor mode will not try to swap the job in if a task goes ready.

        jmp$delete_swapin_candidate (ijl_ordinal, ijl_p^.job_scheduler_data.service_class);
        ijl_p^.job_scheduler_data.unaged_swap_queue_priority := UPPERVALUE (jmt$job_priority);
        ijl_p^.job_scheduler_data.priority := UPPERVALUE (jmt$job_priority);
        time := #FREE_RUNNING_CLOCK (0);
        jmp$insert_swapin_candidate (ijl_ordinal, time);

      = jmc$ies_system_force_out, jmc$ies_job_damaged =
        osp$set_status_abnormal ('JM', jme$job_dead_cannot_swap, 'JOB IS DEAD--CANNOT SWAPIN', status);

      = jmc$ies_job_swapped =
        osp$set_status_abnormal ('JM', jme$job_has_no_ready_tasks, 'JOB HAS NO READY TASKS--WILL NOT SWAPIN',
              status);

      = jmc$ies_entry_free =
        osp$system_error ('SWAPIN FREE IJL ENTRY', ^status);

      ELSE
        osp$set_status_abnormal ('JM', jme$job_in_memory_or_swapin, 'JOB IN MEMORY OR SWAPPING IN', status);
      CASEND;

    ELSE
      osp$set_status_abnormal ('JM', jme$job_not_in_swap_list, 'CANT FIND SWAPPED JOB', status);
    IFEND;

  PROCEND jmp$sched_swapin_job;

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

{ PURPOSE:
{   This procedure is executed by the JOB SCHEDULER task to process an operator swapout of a job.
{ DESIGN:
{   If the job is currently a swapin candidate, then the job scheduler task can change the job's
{   entry status here in job mode because only job mode scheduler does anything with swapin candidates.
{   If a job is non-swappable or terminating, an error status is returned.
{   All other statuses must be processed (checked and changed) in monitor with the PTL lock set to prevent
{   timing problems.

  PROCEDURE jmp$sched_swapout_job
    (    ijl_ordinal: jmt$ijl_ordinal;
         system_supplied_name: jmt$system_supplied_name;
     VAR status: ost$status);

    VAR
      class: jmt$service_class_index,
      ijl_p: ^jmt$initiated_job_list_entry;

    status.normal := TRUE;

    IF ijl_ordinal = jmv$system_ijl_ordinal THEN
      osp$set_status_abnormal ('JM', jme$swapping_not_allowed, 'SWAPPING THE SYSTEM JOB NOT ALLOWED', status);
      RETURN;
    IFEND;

    ijl_p := jmf$ijle_p (ijl_ordinal);

    IF ijl_p^.system_supplied_name = system_supplied_name THEN
      jmp$special_job_swapout (ijl_ordinal, ijl_p, jmc$sr_operator_request, status);
    ELSE
      osp$set_status_abnormal ('JM', jme$non_existent_job, 'NO MATCH FOR JOB NAME GIVEN', status);
    IFEND;

  PROCEND jmp$sched_swapout_job;

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

  PROCEDURE [XDCL, #GATE] jmp$select_job_for_thrashing
    (VAR node: jmt$node;
     VAR class: jmt$service_class_index;
     VAR ws: integer;
     VAR done: boolean);

{
{ PURPOSE:
{ To select a candidate to swap because thrashing is occurring.

{ NOTE:
{ start ajl search at one to avoid considering system job.
{ dont consider possibility of zero ws in ajls.
{

    VAR
      ajl_index: jmt$ajl_ordinal,
      first_max_prio: jmt$job_priority,
      first_pick,
      second_pick,
      third_pick: boolean,
      max_delta_serv: integer,
      delta_serv: integer,
      max_delta_ws: integer,
      delta_cand: jmt$ijl_ordinal,
      serv_cand: jmt$ijl_ordinal,
      first_cand: jmt$ijl_ordinal,
      first_ws: integer,
      local_class: jmt$service_class_index,
      guaranteed_service: integer,
      max_serv_ws: integer,
      max_ws: integer,
      ajl_p: ^jmt$active_job_list_entry,
      cp_time: integer,
      ijl_ord: jmt$ijl_ordinal,
      ijl_p: ^jmt$initiated_job_list_entry,
      cp_time_used_this_period: integer,
      candidate: jmt$ijl_ordinal,
      job_priority: jmt$job_priority,
      i: mmt$job_page_queue_index,
      working_set_size: 0 .. osc$max_page_frames,
      max_job_priority: jmt$job_priority;

    done := FALSE;
    first_pick := FALSE;
    second_pick := FALSE;
    third_pick := FALSE;
    max_delta_serv := UPPERVALUE (integer);
    max_delta_ws := 0;
    max_job_priority := UPPERVALUE (jmt$job_priority);
    max_ws := 0;
    first_max_prio := UPPERVALUE (jmt$job_priority);

  /scan_for_swapout_candidate/

    FOR ajl_index := LOWERBOUND (jmv$ajl_p^) TO jmv$max_ajl_ordinal_in_use DO

      ajl_p := ^jmv$ajl_p^ [ajl_index];
      IF (ajl_p^.in_use <> 0) AND (ajl_index <> jmv$system_ajl_ordinal) THEN

        ijl_ord := ajl_p^.ijl_ordinal;
        ijl_p := jmf$ijle_p (ijl_ord);
        IF (ijl_p^.ajl_ordinal = ajl_index) AND (ijl_p^.entry_status = jmc$ies_job_in_memory) THEN

          jmp$compute_total_memory_used (ijl_p, working_set_size);
          cp_time := (ijl_p^.statistics.cp_time.time_spent_in_job_mode +
                ijl_p^.statistics.cp_time.time_spent_in_mtr_mode);
          cp_time_used_this_period := cp_time - ijl_p^.job_scheduler_data.last_cptime;
          IF ajl_p^.job_is_good_swap_candidate OR (cp_time_used_this_period = 0) THEN
            IF ijl_p^.job_scheduler_data.priority < first_max_prio THEN
              first_cand := ijl_ord;
              first_ws := working_set_size;
              first_max_prio := ijl_p^.job_scheduler_data.priority;
              first_pick := TRUE;
            IFEND;
          ELSE
            local_class := ijl_p^.job_scheduler_data.service_class;
            guaranteed_service := jmv$service_classes [local_class]^.attributes.guaranteed_service_quantum;
            IF ijl_p^.job_scheduler_data.service_accumulator_since_swap > guaranteed_service THEN
              job_priority := ijl_p^.job_scheduler_data.priority;
              IF job_priority < max_job_priority THEN
                serv_cand := ijl_ord;
                max_job_priority := job_priority;
                max_serv_ws := working_set_size;
                second_pick := TRUE;
              IFEND;
            ELSE
              delta_serv := guaranteed_service - ijl_p^.job_scheduler_data.service_accumulator_since_swap;
              IF delta_serv <= max_delta_serv THEN
                IF working_set_size > max_delta_ws THEN
                  delta_cand := ijl_ord;
                  max_delta_serv := delta_serv;
                  max_delta_ws := working_set_size;
                  third_pick := TRUE;
                IFEND;
              IFEND;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
    FOREND /scan_for_swapout_candidate/;

    IF first_pick THEN
      candidate := first_cand;
      max_ws := first_ws;
    ELSEIF second_pick THEN
      candidate := serv_cand;
      max_ws := max_serv_ws;
    ELSEIF third_pick THEN
      candidate := delta_cand;
      max_ws := max_delta_ws;
    ELSE
      done := TRUE;
      RETURN;
    IFEND;

    ijl_p := jmf$ijle_p (candidate);
    node.qtype := active;
    node.priority := ijl_p^.job_scheduler_data.priority;
    node.ijl_ord := candidate;
    node.ws := max_ws;
    class := ijl_p^.job_scheduler_data.service_class;
    ws := max_ws;

  PROCEND jmp$select_job_for_thrashing;

?? TITLE := '[XDCL, #GATE] jmp$select_sched_memory_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$select_sched_memory_event
    (    ws: mmt$page_frame_index;
         class: jmt$service_class_index);

    VAR
      needed_memory: integer;

    needed_memory := ws + mmv$resident_job_target + jmv$sched_memory_wait_factor;

    IF (jmv$memory_needed_by_scheduler = 0) OR (jmv$memory_needed_by_scheduler > needed_memory) THEN
      jmv$memory_needed_by_scheduler := needed_memory;
    IFEND;
    jmv$job_sched_events_selected [jmc$needed_memory_available] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_input_queue] := FALSE;
    jmv$job_sched_events_selected [jmc$examine_swapin_queue] := FALSE;
    jmv$classes_in_resource_wait := jmv$classes_in_resource_wait + $jmt$service_class_set [class];

  PROCEND jmp$select_sched_memory_event;

?? TITLE := '[XDCL, #GATE] jmp$select_sched_service_wait', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$select_sched_service_wait;

    jmv$time_to_wake_scheduler := jmv$sched_service_calc_time;
    jmv$job_sched_events_selected [jmc$scheduler_wake_time] := TRUE;

  PROCEND jmp$select_sched_service_wait;

?? TITLE := '[XDCL, #GATE] jmp$select_scheduler_short_wait', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$select_scheduler_short_wait;

    jmv$time_to_wake_scheduler := #FREE_RUNNING_CLOCK (0) + 500000;
    jmv$job_sched_events_selected [jmc$scheduler_wake_time] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_input_queue] := FALSE;
    jmv$job_sched_events_selected [jmc$examine_swapin_queue] := FALSE;

  PROCEND jmp$select_scheduler_short_wait;

?? TITLE := '[XDCL, #GATE] jmp$select_scheduler_ajlo_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$select_scheduler_ajlo_event
    (    class: jmt$service_class_index);

    jmv$job_sched_events_selected [jmc$needed_ajlo_available] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_input_queue] := FALSE;
    jmv$job_sched_events_selected [jmc$examine_swapin_queue] := FALSE;
    jmv$classes_in_resource_wait := jmv$classes_in_resource_wait + $jmt$service_class_set [class];

  PROCEND jmp$select_scheduler_ajlo_event;

?? TITLE := '[XDCL, #GATE] jmp$set_all_jobs_swapped_var', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_all_jobs_swapped_var;

    jmv$all_jobs_swapped_for_idling := TRUE;

  PROCEND jmp$set_all_jobs_swapped_var;

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

  PROCEDURE [XDCL] jmp$set_class_below_maxaj_limit
    (    service_class_set: jmt$service_class_set);

    VAR
      status: ost$status;

    jmv$classes_in_maxaj_limit_wait := jmv$classes_in_maxaj_limit_wait - service_class_set;
    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$ready_system_task (tmc$stid_job_scheduler, status);

  PROCEND jmp$set_class_below_maxaj_limit;

?? TITLE := '[XDCL, #GATE] jmp$set_event_and_ready_sched', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_event_and_ready_sched
    (    event: jmt$job_scheduler_events);

    VAR
      status: ost$status;

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

  PROCEND jmp$set_event_and_ready_sched;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$set_examine_input_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_examine_input_event;
    jmv$job_scheduler_event [jmc$examine_input_queue] := TRUE;
    jmv$job_sched_events_selected [jmc$examine_input_queue] := TRUE;
    jmv$refresh_job_candidates := TRUE;
  PROCEND jmp$set_examine_input_event;

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

  PROCEDURE [XDCL] jmp$set_examine_queue_event
    (    event: jmt$job_scheduler_events;
         job_class: jmt$job_class;
         unconditional: boolean);

    VAR
      service_class: jmt$service_class_index,
      status: ost$status;

    IF unconditional THEN
      jmv$job_scheduler_event [event] := TRUE;
      jmv$job_sched_events_selected [event] := TRUE;
      tmp$ready_system_task (tmc$stid_job_scheduler, status);
    ELSE
      service_class := jmv$job_class_table_p^ [job_class].initial_service_class_index;
      IF (NOT (service_class IN jmv$classes_in_maxaj_limit_wait)) THEN
        jmv$job_scheduler_event [event] := TRUE;
        IF (NOT (service_class IN jmv$classes_in_resource_wait)) THEN
          jmv$job_sched_events_selected [event] := TRUE;
          tmp$ready_system_task (tmc$stid_job_scheduler, status);
        IFEND;
      IFEND;
    IFEND;
  PROCEND jmp$set_examine_queue_event;

?? TITLE := '[XDCL, #GATE] jmp$set_high_swapin_priority', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_high_swapin_priority
    (    ijl_ordinal: jmt$ijl_ordinal);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      status: ost$status;

    ijle_p := jmf$ijle_p (ijl_ordinal);

    IF ijle_p^.entry_status = jmc$ies_swapin_candidate THEN
      ijle_p^.job_scheduler_data.unaged_swap_queue_priority := UPPERVALUE (jmt$job_priority);
      ijle_p^.job_scheduler_data.priority := UPPERVALUE (jmt$job_priority);
    ELSE
      osp$system_error ('SET HIGH SWAPIN PRIORITY -- CANDIDATE NOT IN QUEUE', ^status);
    IFEND;

  PROCEND jmp$set_high_swapin_priority;

?? TITLE := '[XDCL, #GATE] jmp$set_idle_system_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_idle_system_event;

    VAR
      status: ost$status;

    jmv$prevent_activation_of_jobs := TRUE;
    jmp$idle_advance_lw_jobs;
    jmv$job_scheduler_event [jmc$system_is_idling] := TRUE;
    tmp$ready_system_task (tmc$stid_job_scheduler, status);

  PROCEND jmp$set_idle_system_event;

?? TITLE := '[XDCL, #GATE] jmp$set_sched_service_calc_time', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_sched_service_calc_time;

    jmv$sched_service_calc_time := #FREE_RUNNING_CLOCK (0) +
          (jmv$job_scheduler_table.service_calculation_interval * 1000000);

  PROCEND jmp$set_sched_service_calc_time;

?? TITLE := '[XDCL, #GATE] jmp$set_sched_thrashing_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_sched_thrashing_event;

    jmv$job_scheduler_event [jmc$system_is_thrashing] := TRUE;

  PROCEND jmp$set_sched_thrashing_event;

?? TITLE := '[XDCL, #GATE] jmp$set_scheduler_time_event', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_scheduler_time_event;

    jmv$job_scheduler_event [jmc$scheduler_wake_time] := TRUE;

  PROCEND jmp$set_scheduler_time_event;

?? TITLE := '[XDCL, #GATE] jmp$set_unable_to_swap_flag', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$set_unable_to_swap_flag
    (    ijlo: jmt$ijl_ordinal);

    jmf$ijle_p (ijlo) ^.unable_to_swap_idle_flag := TRUE;

  PROCEND jmp$set_unable_to_swap_flag;

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

{ PURPOSE:
{   This procedure processes operator and job damaged swapouts.
{ DESIGN:
{   Determine which status the job is currently in and swap it if necessary.  It is OK for job mode
{   scheduler to process entry statuses of swapin candidate and job damaged.  Job scheduler controls
{   setting those statuses and nothing asynchronous in monitor will change them once they are set.
{   Entry status of job damaged is checked so that an operator swapout does not change it.

  PROCEDURE jmp$special_job_swapout
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         reason: jmt$swapout_reasons;
     VAR status: ost$status);

    IF ijle_p^.entry_status = jmc$ies_swapin_candidate THEN
      IF reason = jmc$sr_operator_request THEN
        jmp$jm_change_ijl_entry_status (ijle_p, jmc$ies_operator_force_out);
      ELSE {reason = jmc$sr_job_damaged}
        jmp$jm_change_ijl_entry_status (ijle_p, jmc$ies_job_damaged);
      IFEND;
      jmp$delete_swapin_candidate (ijl_ordinal, ijle_p^.job_scheduler_data.service_class);

    ELSEIF (ijle_p^.entry_status < jmc$ies_job_in_memory) OR (ijle_p^.entry_status = jmc$ies_job_damaged) THEN
      osp$set_status_abnormal ('JM', jme$job_cant_be_swapped, 'JOB CANT BE SWAPPED', status);

    ELSE
      jsp$special_job_swapout (ijl_ordinal, reason, status);
    IFEND;

  PROCEND jmp$special_job_swapout;

?? TITLE := '[XDCL, #GATE] jmp$test_for_system_idle_r1', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$test_for_system_idle_r1
    (VAR status: ost$status);

    status.normal := TRUE;
    pmp$get_executing_task_gtid (gtid_of_task_waiting_for_idle);
    IF jmv$job_counts.initiated_jobs > 1 THEN
      osp$set_status_abnormal ('JM', jme$system_not_idle, '', status);
    IFEND;

  PROCEND jmp$test_for_system_idle_r1;

MODEND jmm$job_scheduler_ring_1;
