?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Job Management : Job Scheduler Ring 1 Utilities' ??
MODULE jmm$job_scheduler_utility;

{ PURPOSE:
{   This module contains the ring 1 interfaces for the MANAGE_ACTIVE_SCHEDULING
{   utility, job priority control, and job swapping control.
{ DESIGN:
{   This module contains interfaces which install a new scheduling profile in
{   the scheduler tables, update an existing profile in the tables, and return
{   the contents of these tables.  It also contains interfaces which manage
{   the priority of a job and prevent a job from swapping when it is terminating.
{ NOTES:
{   Applicable documents for the MANAGE_ACTIVE_SCHEDULING utility include:
{   NOS/VE Job Schedulung Phase II ERS, DCS# A7594
{   NOS/VE Job Scheduling Phase IIA Increment Plan, DCS# ARH7808

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??


?? PUSH (LISTEXT := ON) ??
*copyc clt$value
*copyc jmc$job_management_id
*copyc jmc$sched_profile_deadstart_id
*copyc jmc$special_dispatch_priorities
*copyc jmc$status_message_text
*copyc jme$job_scheduler_conditions
*copyc jme$queued_file_conditions
*copyc jmt$application_attributes
*copyc jmt$application_name
*copyc jmt$application_set
*copyc jmt$application_table
*copyc jmt$change_dispatching_list
*copyc jmt$class_kind
*copyc jmt$defined_classes
*copyc jmt$dispatching_control
*copyc jmt$dispatching_control_info
*copyc jmt$ijl_ordinal
*copyc jmt$initiated_job_list_entry
*copyc jmt$job_category_data
*copyc jmt$job_class_set
*copyc jmt$job_priority
*copyc jmt$job_sched_serv_class_stats
*copyc jmt$job_scheduler_statistics
*copyc jmt$maximum_active_jobs
*copyc jmt$node
*copyc jmt$rb_scheduler_requests
*copyc jmt$service_class_table
*copyc jmt$system_supplied_name
*copyc jmt$working_set_size
*copyc mmt$rb_ring1_segment_request
*copyc osd$integer_limits
*copyc ost$binary_unique_name
*copyc ost$global_task_id
*copyc ost$status
*copyc sft$file_space_limit_kind
*copyc tmt$change_priority_origin
*copyc tmt$rb_update_job_task_enviro
?? POP ??
*copyc dmp$allocate_file_space_r1
*copyc dmp$set_eoi
*copyc jmp$jm_change_ijl_entry_status
*copyc jmp$check_active_job_limits
*copyc jmp$clear_scheduler_event
*copyc jmp$delete_swapin_candidate
*copyc jmp$get_ijle_p
*copyc jmp$reorder_swapin_queues
*copyc jmp$reset_max_class_working_set
*copyc jmp$set_class_below_maxaj_limit
*copyc jmp$set_event_and_ready_sched
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$clear_mainframe_sig_lock
*copyc osp$generate_unique_binary_name
*copyc osp$get_locked_variable_value
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$system_error
*copyc pmp$delay
*copyc pmp$get_executing_task_gtid
*copyc pmp$zero_out_table
*copyc syp$set_status_from_mtr_status
*copyc tmp$ready_system_task1
*copyc i#call_monitor
*copyc jmv$application_table_p
*copyc jmv$candidate_queued_jobs
*copyc jmv$change_dispatching_list
*copyc jmv$classes_in_maxaj_limit_wait
*copyc jmv$default_job_class_attr
*copyc jmv$default_service_class_attr
*copyc jmv$highest_rank_job_class
*copyc jmv$ijl_entry_status_statistics
*copyc jmv$ijl_p
*copyc jmv$jcb
*copyc jmv$job_category_data
*copyc jmv$job_class_table_p
*copyc jmv$job_counts
*copyc jmv$job_scheduler_event
*copyc jmv$job_scheduler_table
*copyc jmv$maximum_job_class_in_use
*copyc jmv$max_class_working_set
*copyc jmv$max_service_class_in_use
*copyc jmv$maximum_profile_index
*copyc jmv$profile_index_to_job_class
*copyc jmv$refresh_job_candidates
*copyc jmv$sched_profile_is_loading
*copyc jmv$scheduler_tables_access
*copyc jmv$scheduling_utility_usage
*copyc jmv$service_classes
*copyc osv$mainframe_pageable_heap
*copyc osv$mainframe_wired_heap
*copyc pmv$quantum
*copyc tmv$dual_state_dispatch_prior
*copyc tmv$null_global_task_id
*copyc tmv$ptl_p

?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    jmv$job_scheduler_statistics: [XDCL, #GATE, oss$mainframe_pageable] jmt$job_scheduler_statistics :=
          [REP 42 of 0],

    jmv$job_sched_serv_class_stats: [XDCL, #GATE, oss$mainframe_pageable] jmt$job_sched_serv_class_stats :=
          [REP jmc$maximum_service_classes of [0, 0]],

    jmv$leveler_profile_loading: [XDCL, #GATE, oss$mainframe_pageable] boolean := FALSE;

?? TITLE := 'delete_classes_from_tables', EJECT ??

{ PURPOSE:
{   The purpose of this request is to delete job classes, service classes, and
{   applications from the scheduler tables used by the job scheduler.
{ DESIGN:
{   Job classes and applications are deleted first since they may reference
{   service classes.  Then service classes are deleted.  Job classes being
{   deleted are first removed from the thread of ranked job classes.  For
{   coding convenience, the index of the highest rank job class is stored in
{   the next_rank_class field of the UNASSIGNED job class since this is never
{   a ranked job class.  IF a service class being deleted is still referenced
{   by a job class, another service class, or application , these references
{   are replaced by the UNASSIGNED service class, the parent service class,
{   and the null service class, respectively.  These references will be
{   resolved when the new profile is installed in the tables.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE delete_classes_from_tables
    (    deleted_job_classes: jmt$job_class_set;
         deleted_service_classes: jmt$service_class_set;
         deleted_applications: jmt$application_set);

    VAR
      application_index: jmt$application_index,
      current_rank_class: jmt$job_class,
      job_class_index: jmt$job_class,
      next_rank_class: jmt$job_class,
      service_class_index: jmt$service_class_index;

{ Delete the job classes.

    IF deleted_job_classes <> $jmt$job_class_set [] THEN

{ Remove job classes which are to be deleted from the thread of ranked job classes.

      current_rank_class := jmc$unassigned_job_class;
      WHILE current_rank_class <> jmc$null_job_class DO
        next_rank_class := jmv$job_class_table_p^ [current_rank_class].next_rank_class;
        WHILE (next_rank_class <> jmc$null_job_class) AND (next_rank_class IN deleted_job_classes) DO
          next_rank_class := jmv$job_class_table_p^ [next_rank_class].next_rank_class;
          jmv$job_class_table_p^ [current_rank_class].next_rank_class := next_rank_class;
        WHILEND;
        current_rank_class := next_rank_class;
      WHILEND;
      jmv$highest_rank_job_class := jmv$job_class_table_p^ [jmc$unassigned_job_class].next_rank_class;

{ Now delete the job classes.

      FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
        IF job_class_index IN deleted_job_classes THEN
          jmv$job_class_table_p^ [job_class_index].defined := FALSE;
          IF jmv$job_class_table_p^ [job_class_index].prolog_p <> NIL THEN
            FREE jmv$job_class_table_p^ [job_class_index].prolog_p IN osv$mainframe_pageable_heap^;
          IFEND;
          IF jmv$job_class_table_p^ [job_class_index].epilog_p <> NIL THEN
            FREE jmv$job_class_table_p^ [job_class_index].epilog_p IN osv$mainframe_pageable_heap^;
          IFEND;

          jmv$job_counts.job_class_counts [job_class_index].completed_jobs := 0;

{         pmp$zero_out_table (^jmv$job_class_table_p^ [job_class_index],
{               #SIZE (jmv$job_class_table_p^ [job_class_index]));

          IF job_class_index = jmv$maximum_job_class_in_use THEN
            WHILE NOT jmv$job_class_table_p^ [jmv$maximum_job_class_in_use].defined DO
              jmv$maximum_job_class_in_use := jmv$maximum_job_class_in_use - 1;
            WHILEND;
          IFEND;
        IFEND;
      FOREND;

    IFEND;

{ Delete the applications.

    IF deleted_applications <> $jmt$application_set [] THEN
      IF jmv$application_table_p <> NIL THEN
        FOR application_index := 1 TO UPPERBOUND (jmv$application_table_p^) DO
          IF application_index IN deleted_applications THEN
            jmv$application_table_p^ [application_index].defined := FALSE;
          IFEND;
        FOREND;
      IFEND;
    IFEND;

{ Delete the service classes.

    IF deleted_service_classes <> $jmt$service_class_set [] THEN

{ Remove references by job classes to service classes which are to be deleted.

      FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
        IF jmv$job_class_table_p^ [job_class_index].defined THEN
          IF jmv$job_class_table_p^ [job_class_index].initial_service_class_index IN
                deleted_service_classes THEN
            jmv$job_class_table_p^ [job_class_index].initial_service_class_index :=
                  jmc$unassigned_service_class;
          IFEND;
        IFEND;
      FOREND;

{ Remove references by service classes to service classes which are to be deleted.

      FOR service_class_index := jmc$system_service_class TO jmv$max_service_class_in_use DO
        IF (jmv$service_classes [service_class_index] <> NIL) AND
              jmv$service_classes [service_class_index]^.attributes.defined THEN
          IF jmv$service_classes [service_class_index]^.attributes.next_service_class_index IN
                deleted_service_classes THEN
            jmv$service_classes [service_class_index]^.attributes.next_service_class_index :=
                  service_class_index;
          IFEND;
        IFEND;
      FOREND;

{ Remove references by applications to service classes which are to be deleted.

      IF jmv$application_table_p <> NIL THEN
        FOR application_index := 1 TO UPPERBOUND (jmv$application_table_p^) DO
          IF jmv$application_table_p^ [application_index].defined THEN
            IF jmv$application_table_p^ [application_index].service_class_index IN
                  deleted_service_classes THEN
              jmv$application_table_p^ [application_index].service_class_index :=
                    jmc$unspecified_service_class;
            IFEND;
          IFEND;
        FOREND;
      IFEND;

{ Now delete the service classes.

      FOR service_class_index := jmc$system_service_class TO jmv$max_service_class_in_use DO
        IF service_class_index IN deleted_service_classes THEN
          jmv$service_classes [service_class_index]^.attributes.defined := FALSE;

          pmp$zero_out_table (^jmv$job_sched_serv_class_stats [service_class_index],
                #SIZE (jmv$job_sched_serv_class_stats [service_class_index]));
          pmp$zero_out_table (^jmv$service_classes [service_class_index]^.
                statistics, #SIZE (jmv$service_classes [service_class_index]^.statistics));

        IFEND;
      FOREND;
      WHILE (jmv$service_classes [jmv$max_service_class_in_use] = NIL) OR
            NOT jmv$service_classes [jmv$max_service_class_in_use]^.attributes.defined DO
        jmv$max_service_class_in_use := jmv$max_service_class_in_use - 1;
      WHILEND;

    IFEND;

  PROCEND delete_classes_from_tables;
?? TITLE := 'install_application_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to install a given list of applications
{   in the application table for application scheduling.
{ DESIGN:
{   The current application table is completely replaced with the given list of
{   applications.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE install_application_table
    (    application_entries_p: ^jmt$application_table);

    VAR
      application_index: jmt$application_index;

    IF jmv$application_table_p <> NIL THEN
      FREE jmv$application_table_p IN osv$mainframe_pageable_heap^;
    IFEND;
    IF application_entries_p <> NIL THEN
      ALLOCATE jmv$application_table_p: [1 .. UPPERBOUND (application_entries_p^)] IN
            osv$mainframe_pageable_heap^;
      jmv$application_table_p^ := application_entries_p^;
      FOR application_index := 1 TO UPPERBOUND (application_entries_p^) DO
        jmv$application_table_p^ [application_index].defined := TRUE;
      FOREND;
    IFEND;

  PROCEND install_application_table;
?? TITLE := 'install_job_category_data', EJECT ??

{ PURPOSE:
{   The purpose of this request is to install a given list of job category
{   data for job submission and scheduling.
{ DESIGN:
{   The current job category data is completely replaced with the given list of
{   data.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE install_job_category_data
    (    category_data: jmt$job_category_data);

    IF jmv$job_category_data.item_list <> NIL THEN
      FREE jmv$job_category_data.item_list IN osv$mainframe_pageable_heap^;
    IFEND;

    jmv$job_category_data := category_data;
    IF category_data.item_list <> NIL THEN
      ALLOCATE jmv$job_category_data.item_list: [[REP #SIZE (category_data.item_list^) OF cell]] IN
            osv$mainframe_pageable_heap^;
      jmv$job_category_data.item_list^ := category_data.item_list^;
    IFEND;
    IF category_data.category_names <> NIL THEN
      ALLOCATE jmv$job_category_data.category_names: [0 .. UPPERBOUND (category_data.category_names^)] IN
            osv$mainframe_pageable_heap^;
      jmv$job_category_data.category_names^ := category_data.category_names^;
    IFEND;

  PROCEND install_job_category_data;
?? TITLE := 'install_job_class_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to install a given list of job classes
{   in the job class table.  The order in which the job classes are given
{   defines the rank, highest to lowest, of the classes which are to be used
{   for automatic class selection.
{ DESIGN:
{   The current job class table is completely replaced with the given list of
{   job class entries.  Job classes which are to be used for automatic class
{   selection are threaded by rank from highest to lowest.  The predefined
{   job classes, SYSTEM, MAINTENANCE, AND UNASSIGNED are never used for
{   automatic class selection.  For coding convenience, the index of the
{   highest rank job class is stored in the next_rank_class field of the
{   UNASSIGNED job class.
{   If this is the first profile installed since the system was deadstarted,
{   initiation of jobs for all job classes except SYSTEM and MAINTENANCE is
{   prevented by setting the initiation_level counts to zero.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.
{   Job candidates for initiation are not refreshed here as a result of new
{   job class table information since this is done unconditionally when
{   the installation of a profile is complete.

  PROCEDURE install_job_class_table
    (    job_class_entries_p: ^jmt$job_class_table;
         current_profile_id: ost$name);

    VAR
      entry: ost$non_negative_integers,
      job_class_index: jmt$job_class,
      ignore_refresh_job_candidates: boolean,
      profile_index: jmt$job_class;

    jmv$highest_rank_job_class := jmc$null_job_class;
    jmv$job_class_table_p^ [jmc$unassigned_job_class].next_rank_class := jmc$null_job_class;

    jmv$maximum_profile_index := 0;
    FOR entry := UPPERBOUND (job_class_entries_p^) DOWNTO 1 DO
      job_class_index := job_class_entries_p^ [entry].index;
      WHILE job_class_index > jmv$maximum_job_class_in_use DO
        jmv$maximum_job_class_in_use := jmv$maximum_job_class_in_use + 1;
        jmv$job_class_table_p^ [jmv$maximum_job_class_in_use] := jmv$default_job_class_attr;
      WHILEND;
      IF NOT jmv$job_class_table_p^ [job_class_index].defined THEN
        jmv$job_class_table_p^ [job_class_index] := job_class_entries_p^ [entry];
        IF (current_profile_id = jmc$sched_profile_deadstart_id) AND
              (job_class_index > jmc$maintenance_job_class) THEN
          jmv$job_class_table_p^ [job_class_index].initiation_level.preferred := 0;
          jmv$job_class_table_p^ [job_class_index].initiation_level.maximum_increment := 0;
        IFEND;
        IF job_class_entries_p^ [entry].prolog_p <> NIL THEN
          ALLOCATE jmv$job_class_table_p^ [job_class_index].prolog_p:
                [STRLENGTH (job_class_entries_p^ [entry].prolog_p^)] IN osv$mainframe_pageable_heap^;
          jmv$job_class_table_p^ [job_class_index].prolog_p^ := job_class_entries_p^ [entry].prolog_p^;
        IFEND;
        IF job_class_entries_p^ [entry].epilog_p <> NIL THEN
          ALLOCATE jmv$job_class_table_p^ [job_class_index].epilog_p:
                [STRLENGTH (job_class_entries_p^ [entry].epilog_p^)] IN osv$mainframe_pageable_heap^;
          jmv$job_class_table_p^ [job_class_index].epilog_p^ := job_class_entries_p^ [entry].epilog_p^;
        IFEND;
        jmv$job_class_table_p^ [job_class_index].defined := TRUE;
      ELSE
        update_job_class_table (job_class_entries_p^ [entry], {install_profile} TRUE, current_profile_id,
              ignore_refresh_job_candidates);
      IFEND;
      IF job_class_entries_p^ [entry].automatic_class_selection THEN
        jmv$job_class_table_p^ [job_class_index].next_rank_class := jmv$highest_rank_job_class;
        jmv$highest_rank_job_class := job_class_index;
      IFEND;
      profile_index := jmv$job_class_table_p^ [job_class_index].profile_index;
      jmv$profile_index_to_job_class [profile_index] := job_class_index;
      IF profile_index > jmv$maximum_profile_index THEN
        jmv$maximum_profile_index := profile_index;
      IFEND;
    FOREND;

    jmv$job_class_table_p^ [jmc$unassigned_job_class].next_rank_class := jmv$highest_rank_job_class;

  PROCEND install_job_class_table;
?? TITLE := 'install_job_scheduler_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to install a given list of job scheduler
{   controls in the job scheduler table.
{ DESIGN:
{   The current job scheduler table is completely replaced with the given list of
{   job scheduler controls.
{   If there are different dispatching allocation values, a monitor request must be
{   issued to change tables used by dispatcher.  The scheduler table must be updated
{   before the monitor request is issued.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE install_job_scheduler_table
    (    controls_entry: jmt$job_scheduler_table);

    VAR
      dp: 1 .. 15,
      local_controls: jmt$job_scheduler_table,
      rb: jmt$rb_scheduler_requests;

    IF jmv$job_scheduler_table.validation_categories_p <> NIL THEN
      FREE jmv$job_scheduler_table.validation_categories_p IN osv$mainframe_pageable_heap^;
    IFEND;

    jmv$job_scheduler_table := controls_entry;

    IF controls_entry.validation_categories_p <> NIL THEN
      ALLOCATE jmv$job_scheduler_table.validation_categories_p:
            [1 .. UPPERBOUND (controls_entry.validation_categories_p^)] IN osv$mainframe_pageable_heap^;
      jmv$job_scheduler_table.validation_categories_p^ := controls_entry.validation_categories_p^;
    IFEND;

{ Issue a monitor request to check and change dispatching allocation controls.

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

{ Update the dual-state dispatching priority table. This is the table that
{ the dispatcher reads during task selection. It does not have to be updated in
{ monitor mode. The table will be wrong (because not updated in MM) for a maximum
{ a one timeslice. The table has the priorities and subpriorities for NOS/VE priorities
{ P1 through P14. Only the values P1 through P10 can be modified by the scheduling utilities.
{ Priorities P11 through P14 are hard-coded.

    FOR dp := jmc$priority_p1 TO jmc$priority_p10 DO
      tmv$dual_state_dispatch_prior [dp].dual_state_priority :=
            jmv$job_scheduler_table.dual_state_priority_control [dp].priority;
      tmv$dual_state_dispatch_prior [dp].subpriority := jmv$job_scheduler_table.
            dual_state_priority_control [dp].subpriority;
    FOREND;

  PROCEND install_job_scheduler_table;
?? TITLE := 'install_service_class_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to install a given list of service classes
{   in the service class table.
{ DESIGN:
{   The current service class table is completely replaced with the given list of
{   service class entries.  New classes are defined first and then existing
{   classes are updated since existing classes may reference new classes.
{   Job scheduler events are issued once after the entire table has been installed to
{   process changes due to new service class table information.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE install_service_class_table
    (    service_class_entries_p: ^jmt$service_class_table);

    VAR
      decreased_maxaj: boolean,
      decreased_maxaj_classes: jmt$service_class_set,
      dispatching_control_change: boolean,
      dispatching_control_changes: boolean,
      entry: ost$non_negative_integers,
      existing_classes: jmt$service_class_set,
      increased_maxaj: boolean,
      increased_maxaj_classes: jmt$service_class_set,
      service_class_index: jmt$service_class_index;

    dispatching_control_changes := FALSE;
    existing_classes := $jmt$service_class_set [];
    decreased_maxaj_classes := $jmt$service_class_set [];
    increased_maxaj_classes := $jmt$service_class_set [];

{ Define new service classes.

    FOR entry := 1 TO UPPERBOUND (service_class_entries_p^) DO
      service_class_index := service_class_entries_p^ [entry].index;
      IF jmv$service_classes [service_class_index] = NIL THEN
        ALLOCATE jmv$service_classes [service_class_index] IN osv$mainframe_wired_heap^;
        pmp$zero_out_table (^jmv$service_classes [service_class_index]^.
              statistics, #SIZE (jmv$service_classes [service_class_index]^.statistics));
        jmv$service_classes [service_class_index]^.attributes := jmv$default_service_class_attr;
      IFEND;
      IF NOT jmv$service_classes [service_class_index]^.attributes.defined THEN
        jmv$service_classes [service_class_index]^.attributes := service_class_entries_p^ [entry];
        IF service_class_entries_p^ [entry].next_service_class_index = jmc$null_service_class THEN
          jmv$service_classes [service_class_index]^.attributes.next_service_class_index :=
                service_class_index;
        IFEND;
        jmv$service_classes [service_class_index]^.attributes.defined := TRUE;
        IF service_class_index > jmv$max_service_class_in_use THEN
          jmv$max_service_class_in_use := service_class_index;
        IFEND;
      ELSE
        existing_classes := existing_classes + $jmt$service_class_set [service_class_index];
      IFEND;
    FOREND;

{ Update existing service classes.

    IF existing_classes <> $jmt$service_class_set [] THEN
      FOR entry := 1 TO UPPERBOUND (service_class_entries_p^) DO
        service_class_index := service_class_entries_p^ [entry].index;
        IF service_class_index IN existing_classes THEN
          update_service_class_table (service_class_entries_p^ [entry], dispatching_control_change,
                decreased_maxaj, increased_maxaj);
          dispatching_control_changes := dispatching_control_changes OR dispatching_control_change;
          IF decreased_maxaj THEN
            decreased_maxaj_classes := decreased_maxaj_classes + $jmt$service_class_set [service_class_index];
          IFEND;
          IF increased_maxaj THEN
            increased_maxaj_classes := increased_maxaj_classes + $jmt$service_class_set [service_class_index];
          IFEND;
        IFEND;
      FOREND;
    IFEND;

{ Issue the job scheduler events to process the service class table changes.

    IF decreased_maxaj_classes <> $jmt$service_class_set [] THEN
      jmp$check_active_job_limits (decreased_maxaj_classes);
    IFEND;

    IF increased_maxaj_classes <> $jmt$service_class_set [] THEN
      jmp$set_class_below_maxaj_limit (increased_maxaj_classes);
    IFEND;

    IF dispatching_control_changes THEN
      jmp$set_event_and_ready_sched (jmc$change_dispatching_controls);
    IFEND;

  PROCEND install_service_class_table;
?? TITLE := '[XDCL] jmp$cleanup_unrecovered_job', EJECT ??

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

    VAR
      rb: jmt$rb_scheduler_requests;

    rb.reqcode := syc$rc_job_scheduler_request;
    rb.sub_reqcode := jmc$src_cleanup_unrecovered_job;
    rb.ijl_ordinal := ijl_ordinal;
    i#call_monitor (#LOC (rb), #SIZE (rb));

  PROCEND jmp$cleanup_unrecovered_job;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$clear_leveler_profile_flag', EJECT ??
*copy jmh$clear_leveler_profile_flag

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

    status.normal := TRUE;

    jmp$verify_utility_access_id (access_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    jmv$leveler_profile_loading := FALSE;
  PROCEND jmp$clear_leveler_profile_flag;
?? TITLE := '[XDCL, #GATE] jmp$clear_utility_active_flag', EJECT ??
*copy jmh$clear_utility_active_flag

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

    VAR
      global_task_id: ost$global_task_id;

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);

    IF NOT jmv$scheduling_utility_usage.active THEN
      osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);
      osp$set_status_condition (jme$no_utility_is_active, status);
      RETURN;
    IFEND;
    pmp$get_executing_task_gtid (global_task_id);
    IF (access_id <> jmv$scheduling_utility_usage.access_id) OR
          (global_task_id <> jmv$scheduling_utility_usage.global_task_id) THEN
      osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);
      osp$set_status_condition (jme$access_id_mismatch, status);
      RETURN;
    IFEND;
    jmv$scheduling_utility_usage.active := FALSE;
    jmv$leveler_profile_loading := FALSE;

    osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);

  PROCEND jmp$clear_utility_active_flag;

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

*copyc jmh$get_job_name_via_gtid

  PROCEDURE [XDCL] jmp$get_job_name_via_gtid
    (    global_task_id: ost$global_task_id;
     VAR system_supplied_name: jmt$system_supplied_name;
     VAR job_exists: boolean);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry;

    job_exists := TRUE;

    IF (global_task_id = tmv$null_global_task_id) OR (global_task_id.index > UPPERBOUND (tmv$ptl_p^)) OR
          (tmv$ptl_p^ [global_task_id.index].sequence_number <> global_task_id.seqno) OR
          (tmv$ptl_p^ [global_task_id.index].status = tmc$ts_null) THEN
      job_exists := FALSE;
    ELSE
      jmp$get_ijle_p (tmv$ptl_p^ [global_task_id.index].ijl_ordinal, ijle_p);
      system_supplied_name := ijle_p^.system_supplied_name;
      IF (tmv$ptl_p^ [global_task_id.index].status = tmc$ts_null) OR
            (tmv$ptl_p^ [global_task_id.index].sequence_number <> global_task_id.seqno) THEN
        job_exists := FALSE;
      IFEND;
    IFEND;

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

  PROCEDURE [XDCL, #GATE] jmp$incr_scheduler_statistics
    (    scheduler_statistic: jmt$sched_statistic_elements);

    jmv$job_scheduler_statistics [scheduler_statistic] :=
          jmv$job_scheduler_statistics [scheduler_statistic] + 1;

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

{ PURPOSE:
{   The purpose of this procedure is to increment the scheduler statistics and the service class
{   statistics.  Service class statistics are a subset of scheduler statistics.

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

    jmv$job_scheduler_statistics [scheduler_statistic] :=
          jmv$job_scheduler_statistics [scheduler_statistic] + 1;

    IF scheduler_statistic = jmc$memory_wait_no_preempt THEN
      jmv$job_sched_serv_class_stats [class].memory_wait := jmv$job_sched_serv_class_stats [class].
            memory_wait + 1;
    ELSEIF scheduler_statistic = jmc$ajlo_wait_no_preempt THEN
      jmv$job_sched_serv_class_stats [class].ajl_wait := jmv$job_sched_serv_class_stats [class].ajl_wait + 1;
    IFEND;

  PROCEND jmp$incr_sched_serv_statistics;
?? TITLE := '[XDCL, #GATE] jmp$install_profile_in_tables', EJECT ??
*copy jmh$install_profile_in_tables

{ DESIGN:
{   Access to the scheduler tables is interlocked while a profile is installed.
{   Job classes, service classes, and applications to be deleted are deleted
{   first.  Then the new scheduler tables are installed in the order of
{   service classes, applications, job scheduler controls, job classes, and
{   job category data.  Service classes are installed first since job classes
{   and applications reference them.
{   If this is the first profile installed since the system was deadstarted,
{   initiation of jobs is prevented for all job classes except SYSTEM and
{   MAINTENANCE.
{ NOTES:
{   Data validity checks are made prior to calling this procedure.

  PROCEDURE [XDCL, #GATE] jmp$install_profile_in_tables
    (    access_id: ost$binary_unique_name;
         job_class_entries_p: ^jmt$job_class_table;
         service_class_entries_p: ^jmt$service_class_table;
         application_entries_p: ^jmt$application_table;
         controls_entry: jmt$job_scheduler_table;
         category_data: jmt$job_category_data;
         deleted_job_classes: jmt$job_class_set;
         deleted_service_classes: jmt$service_class_set;
         deleted_applications: jmt$application_set;
     VAR status: ost$status);

    VAR
      actual_value: integer,
      current_profile_id: ost$name,
      ignore_status: ost$status;

    status.normal := TRUE;

    jmp$verify_utility_access_id (access_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);
    osp$get_locked_variable_value (jmv$scheduler_tables_access.count, {expected_value} 0, actual_value);
    WHILE actual_value <> 0 DO
      pmp$delay (500, ignore_status);
      osp$get_locked_variable_value (jmv$scheduler_tables_access.count, {expected_value} 0, actual_value);
    WHILEND;

    current_profile_id := jmv$job_scheduler_table.profile_identification;
    delete_classes_from_tables (deleted_job_classes, deleted_service_classes, deleted_applications);
    install_service_class_table (service_class_entries_p);
    install_application_table (application_entries_p);
    install_job_scheduler_table (controls_entry);
    install_job_class_table (job_class_entries_p, current_profile_id);
    install_job_category_data (category_data);

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

  PROCEND jmp$install_profile_in_tables;
?? TITLE := '[XDCL, #GATE] jmp$read_application_record', EJECT ??
*copy jmh$read_application_record

  PROCEDURE [XDCL, #GATE] jmp$read_application_record
    (    application_name: jmt$application_name;
     VAR application_index: {input, output} jmt$application_index;
     VAR application_record: jmt$application_attributes;
     VAR status: ost$status);

    VAR
      temp: integer,
      high_index: jmt$application_index,
      low_index: jmt$application_index,
      middle_index: jmt$application_index;

    status.normal := TRUE;
    low_index := 1;
    high_index := 0;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    IF jmv$application_table_p <> NIL THEN
      high_index := UPPERBOUND (jmv$application_table_p^);
      IF (application_index > 0) AND (application_index <= high_index) THEN
        middle_index := application_index;
      ELSE
        temp := low_index + high_index;
        middle_index := temp DIV 2;
      IFEND;

    /binary_search_for_application/
      REPEAT
        IF application_name < jmv$application_table_p^ [middle_index].name THEN
          high_index := middle_index - 1;
        ELSEIF application_name > jmv$application_table_p^ [middle_index].name THEN
          low_index := middle_index + 1;
        ELSEIF jmv$application_table_p^ [middle_index].defined THEN
          application_record := jmv$application_table_p^ [middle_index];
          application_index := middle_index;
          EXIT /binary_search_for_application/;
        ELSE
          low_index := high_index + 1;
        IFEND;
        temp := low_index + high_index;
        middle_index := temp DIV 2;
      UNTIL low_index > high_index;
    IFEND;

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    IF low_index > high_index THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$class_or_appl_not_defined, jmc$smt_application,
            status);
      osp$append_status_parameter (osc$status_parameter_delimiter, application_name, status);
    IFEND;

  PROCEND jmp$read_application_record;
?? TITLE := '[XDCL, #GATE] jmp$read_category_data', EJECT ??
*copy jmh$read_category_data

  PROCEDURE [XDCL, #GATE] jmp$read_category_data
    (VAR category_data: jmt$job_category_data;
     VAR data_p: ^SEQ ( * );
     VAR status: ost$status);

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    category_data := jmv$job_category_data;
    RESET data_p;
    IF jmv$job_category_data.item_list <> NIL THEN
      NEXT category_data.item_list: [[REP #SIZE (jmv$job_category_data.item_list^) OF cell]] IN data_p;
      IF category_data.item_list = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
      ELSE
        category_data.item_list^ := jmv$job_category_data.item_list^;
      IFEND;
    IFEND;
    IF jmv$job_category_data.category_names <> NIL THEN
      NEXT category_data.category_names: [0 .. UPPERBOUND (jmv$job_category_data.category_names^)] IN data_p;
      IF category_data.category_names = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
      ELSE
        category_data.category_names^ := jmv$job_category_data.category_names^;
      IFEND;
    IFEND;

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

  PROCEND jmp$read_category_data;
?? TITLE := '[XDCL, #GATE] jmp$read_defined_classes', EJECT ??
*copy jmh$read_defined_classes

  PROCEDURE [XDCL, #GATE] jmp$read_defined_classes
    (    class_kind: jmt$class_kind;
     VAR defined_classes: jmt$defined_classes;
     VAR number_of_classes: ost$non_negative_integers;
     VAR status: ost$status);

    VAR
      application_index: jmt$application_index,
      job_class_index: jmt$job_class,
      ranking_error: boolean,
      ranked_class_set: jmt$job_class_set,
      result_size: ost$non_negative_integers,
      service_class_index: jmt$service_class_index,
      size_error: boolean;

    status.normal := TRUE;
    number_of_classes := 0;
    result_size := UPPERBOUND (defined_classes);
    ranking_error := FALSE;
    size_error := FALSE;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    CASE class_kind OF

    = jmc$job_class =
      job_class_index := jmv$highest_rank_job_class;
      ranked_class_set := $jmt$job_class_set [];

    /get_ranked_job_classes/
      WHILE job_class_index <> jmc$null_job_class DO
        IF (jmv$job_class_table_p^ [job_class_index].defined) AND
              (jmv$job_class_table_p^ [job_class_index].automatic_class_selection) THEN
          IF number_of_classes < result_size THEN
            number_of_classes := number_of_classes + 1;
          ELSE
            size_error := TRUE;
            EXIT /get_ranked_job_classes/;
          IFEND;
          defined_classes [number_of_classes].name := jmv$job_class_table_p^ [job_class_index].name;
          defined_classes [number_of_classes].index := jmv$job_class_table_p^ [job_class_index].index;
          ranked_class_set := ranked_class_set + $jmt$job_class_set [job_class_index];
          job_class_index := jmv$job_class_table_p^ [job_class_index].next_rank_class;
        ELSE
          ranking_error := TRUE;
          EXIT /get_ranked_job_classes/;
        IFEND;
      WHILEND /get_ranked_job_classes/;

      IF ranking_error THEN
        number_of_classes := 0;
      IFEND;

    /get_non_ranked_job_classes/
      FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
        IF jmv$job_class_table_p^ [job_class_index].defined THEN
          IF (NOT jmv$job_class_table_p^ [job_class_index].automatic_class_selection) OR (ranking_error) THEN
            IF number_of_classes < result_size THEN
              number_of_classes := number_of_classes + 1;
            ELSE
              size_error := TRUE;
              EXIT /get_non_ranked_job_classes/;
            IFEND;
            defined_classes [number_of_classes].name := jmv$job_class_table_p^ [job_class_index].name;
            defined_classes [number_of_classes].index := jmv$job_class_table_p^ [job_class_index].index;
          ELSE
            IF NOT (job_class_index IN ranked_class_set) THEN
              IF NOT size_error THEN
                ranking_error := TRUE;
              IFEND;
            IFEND;
          IFEND;
        IFEND;
      FOREND /get_non_ranked_job_classes/;

    = jmc$service_class =

    /get_service_classes/
      FOR service_class_index := jmc$system_service_class TO jmv$max_service_class_in_use DO
        IF (jmv$service_classes [service_class_index] <> NIL) AND
              jmv$service_classes [service_class_index]^.attributes.defined THEN
          IF number_of_classes < result_size THEN
            number_of_classes := number_of_classes + 1;
          ELSE
            size_error := TRUE;
            EXIT /get_service_classes/;
          IFEND;
          defined_classes [number_of_classes].name := jmv$service_classes [service_class_index]^.attributes.
                name;
          defined_classes [number_of_classes].index := jmv$service_classes [service_class_index]^.attributes.
                index;
        IFEND;
      FOREND /get_service_classes/;

    = jmc$application =
      IF jmv$application_table_p <> NIL THEN

      /get_applications/
        FOR application_index := 1 TO UPPERBOUND (jmv$application_table_p^) DO
          IF jmv$application_table_p^ [application_index].defined THEN
            IF number_of_classes < result_size THEN
              number_of_classes := number_of_classes + 1;
            ELSE
              size_error := TRUE;
              EXIT /get_applications/;
            IFEND;
            defined_classes [number_of_classes].name := jmv$application_table_p^ [application_index].name;
            defined_classes [number_of_classes].index := application_index;
          IFEND;
        FOREND /get_applications/;
      IFEND;

    ELSE
    CASEND;

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    IF ranking_error THEN
      osp$set_status_condition (jme$error_in_job_class_ranking, status);
    ELSEIF size_error THEN
      osp$set_status_condition (jme$result_array_too_small, status);
    IFEND;

  PROCEND jmp$read_defined_classes;
?? TITLE := '[XDCL, #GATE] jmp$read_job_class_record', EJECT ??
*copy jmh$read_job_class_record

  PROCEDURE [XDCL, #GATE] jmp$read_job_class_record
    (    job_class_index: jmt$job_class;
     VAR job_class_record: jmt$job_class_attributes;
     VAR data_p: ^SEQ ( * );
     VAR status: ost$status);

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    IF (job_class_index = 0) OR (NOT jmv$job_class_table_p^ [job_class_index].defined) THEN
      osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);
      osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_job_class, status);
      osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
      RETURN;
    IFEND;

    job_class_record := jmv$job_class_table_p^ [job_class_index];
    RESET data_p;
    IF job_class_record.prolog_p <> NIL THEN
      NEXT job_class_record.prolog_p: [STRLENGTH (jmv$job_class_table_p^ [job_class_index].prolog_p^)] IN
            data_p;
      IF job_class_record.prolog_p = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
      ELSE
        job_class_record.prolog_p^ := jmv$job_class_table_p^ [job_class_index].prolog_p^;
      IFEND;
    IFEND;
    IF job_class_record.epilog_p <> NIL THEN
      NEXT job_class_record.epilog_p: [STRLENGTH (jmv$job_class_table_p^ [job_class_index].epilog_p^)] IN
            data_p;
      IF job_class_record.epilog_p = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
      ELSE
        job_class_record.epilog_p^ := jmv$job_class_table_p^ [job_class_index].epilog_p^;
      IFEND;
    IFEND;

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

  PROCEND jmp$read_job_class_record;
?? TITLE := '[XDCL, #GATE] jmp$read_scheduler_table', EJECT ??
*copy jmh$read_scheduler_table

  PROCEDURE [XDCL, #GATE] jmp$read_scheduler_table
    (VAR scheduler_table: jmt$job_scheduler_table;
     VAR data_p: ^SEQ ( * );
     VAR status: ost$status);

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    scheduler_table := jmv$job_scheduler_table;
    RESET data_p;
    IF jmv$job_scheduler_table.validation_categories_p <> NIL THEN
      NEXT scheduler_table.validation_categories_p: [1 .. UPPERBOUND (jmv$job_scheduler_table.
            validation_categories_p^)] IN data_p;
      IF scheduler_table.validation_categories_p = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
      ELSE
        scheduler_table.validation_categories_p^ := jmv$job_scheduler_table.validation_categories_p^;
      IFEND;
    IFEND;

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

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

{ PURPOSE:
{   This procedure is executed during job initiation to do the following:
{   --store the sfid of the job's swap file
{   --allocate the swap file for interactive jobs
{   --issue a monitor request to change the DM and MM tables for the swap file to indicate
{     that the swap file is a shared file.  The job has the swap file attached exclusively,
{     but the tables need to indicate shared because the system will open and reference the
{     swap file during IDLE_SYSTEM and TERMINATE_SYSTEM.
{   --change the entry status of the job so that it is now swappable
{   --change the dispatching priority of the job from system priority to the value for the service class

  PROCEDURE [XDCL, #GATE] jmp$save_sfid_of_swap_file
    (    sfid: gft$system_file_identifier;
     VAR status: ost$status);

    CONST
      initial_swap_file_size = 393216;

    VAR
      dispatching_control_info: jmt$dispatching_control_info,
      rb: mmt$rb_ring1_segment_request;

    status.normal := TRUE;
    jmv$jcb.ijle_p^.swap_data.swap_file_sfid := sfid;
    IF jmv$jcb.ijle_p^.job_mode <> jmc$batch THEN
      dmp$allocate_file_space_r1 (sfid, initial_swap_file_size, 0, 0, osc$wait, sfc$no_limit, status);
    IFEND;

    rb.reqcode := syc$rc_ring1_segment_request;
    rb.request := mmc$sr1_change_swap_file_queue;
    rb.sfid := sfid;
    i#call_monitor (#LOC (rb), #SIZE (rb));

    jmp$jm_change_ijl_entry_status (jmv$jcb.ijle_p, jmc$ies_job_in_memory);

    dispatching_control_info.dispatching_priority := jmv$service_classes
          [jmv$jcb.ijle_p^.job_scheduler_data.service_class]^.
          attributes.dispatching_control [jmc$min_dispatching_control].dispatching_priority;
    jmp$change_dispatching_prior_r1 (tmc$cpo_save_swap_file_sfid, jmv$jcb.ijl_ordinal, jmv$jcb.system_name,
          dispatching_control_info, status);

  PROCEND jmp$save_sfid_of_swap_file;
?? OLDTITLE, NEWTITLE := '[XDCL, #GATE] jmp$select_reset_disp_pr', EJECT ??
*copy jmh$select_reset_disp_pr

  PROCEDURE [XDCL, #GATE] jmp$select_reset_disp_pr;

    VAR
      ignore_status: ost$status,
      null_dispatching_info:  jmt$dispatching_control_info;

{ The following code is a duplicate of the ring 2 code. It is required here also to
{ satisfy DESKTOP's use of the interface. DESKTOP bypasses ring 2.

    IF jmv$jcb.ijle_p^.interactive_task_gtid <> tmv$null_global_task_id THEN
      jmp$change_dispatching_prior_r1 (tmc$cpo_interactive_command, jmv$jcb.ijl_ordinal, jmv$jcb.system_name,
        null_dispatching_info, ignore_status);
    ELSE
      pmp$get_executing_task_gtid (jmv$jcb.ijle_p^.interactive_task_gtid);
    IFEND;

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

{ PURPOSE:
{   This procedure is executed during job termination to set a job non-swappable.
{   This MUST happen to insure that a job is not swapping when it terminates.

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

    VAR
      dispatching_control_info: jmt$dispatching_control_info,
      rb: tmt$rb_update_job_task_enviro;

    status.normal := TRUE;
    dispatching_control_info.dispatching_priority := jmc$priority_system_job;
    jmp$change_dispatching_prior_r1 (tmc$cpo_set_job_unswappable, jmv$jcb.ijl_ordinal, jmv$jcb.system_name,
          dispatching_control_info, status);

    rb.reqcode := syc$rc_update_job_task_enviro;
    rb.subcode := tmc$ujte_set_non_swappable;
    i#call_monitor (#LOC (rb), #SIZE (rb));

{ Set swapfile eoi to 0 in case the system terminates before the swapfile gets purged.

    dmp$set_eoi (jmv$jcb.ijle_p^.swap_data.swap_file_sfid, 0, status);

  PROCEND jmp$set_job_unswappable;
?? TITLE := '[XDCL, #GATE] jmp$set_profile_loading_flag', EJECT ??
*copy jmh$set_profile_loading_flag

  PROCEDURE [XDCL, #GATE] jmp$set_profile_loading_flag
    (    profile_is_loading: boolean;
         new_profile_id: ost$name;
     VAR status: ost$status);

    VAR
      ignore_status: ost$status,
      job_class: jmt$job_class,
      rb: jmt$rb_scheduler_requests;

    status.normal := TRUE;

{ If a profile is being installed, issue a monitor request to set the
{ profile loading flag.  Wait for scheduler to refresh the job candidates
{ for initiation before returning.

    IF profile_is_loading THEN
      jmv$leveler_profile_loading := TRUE;

      rb.reqcode := syc$rc_job_scheduler_request;
      rb.sub_reqcode := jmc$src_sched_profile_loading;

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

      jmv$job_class_table_p^ [jmc$unassigned_job_class].enable_class_initiation := FALSE;
      jmv$refresh_job_candidates := TRUE;
      jmp$set_event_and_ready_sched (jmc$examine_input_queue);
      WHILE jmv$refresh_job_candidates DO
        pmp$delay (500, ignore_status);
      WHILEND;
      FOR job_class := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
        WHILE jmv$candidate_queued_jobs [job_class].candidate_available DO
          pmp$delay (100, ignore_status);
        WHILEND;
      FOREND;
    ELSE
      jmv$sched_profile_is_loading := FALSE;
      jmv$refresh_job_candidates := TRUE;
      jmp$set_event_and_ready_sched (jmc$examine_input_queue);
    IFEND;

  PROCEND jmp$set_profile_loading_flag;
?? TITLE := '[XDCL, #GATE] jmp$set_utility_active_flag', EJECT ??
*copy jmh$set_utility_active_flag

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

    status.normal := TRUE;
    IF jmv$scheduling_utility_usage.active THEN
      osp$set_status_condition (jme$another_utility_is_active, status);
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);

    IF jmv$scheduling_utility_usage.active THEN
      osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);
      osp$set_status_condition (jme$another_utility_is_active, status);
      RETURN;
    IFEND;
    pmp$get_executing_task_gtid (jmv$scheduling_utility_usage.global_task_id);
    osp$generate_unique_binary_name (jmv$scheduling_utility_usage.access_id, status);
    IF NOT status.normal THEN
      osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);
      RETURN;
    IFEND;
    jmv$scheduling_utility_usage.active := TRUE;

    osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);

    access_id := jmv$scheduling_utility_usage.access_id;

  PROCEND jmp$set_utility_active_flag;
?? TITLE := '[XDCL, #GATE] jmp$update_profile_in_tables', EJECT ??
*copy jmh$update_profile_in_tables

{ DESIGN:
{   Access to the scheduler tables is interlocked while a profile is updated.
{   Job category data is never updated since this type of change would
{   constitute a structure change in the scheduling profile.  Job scheduler
{   events are issued once after all the tables are updated to process changes
{   due to new information in the tables.
{ NOTES:
{   Data validity checks are made prior to calling this procedure.

  PROCEDURE [XDCL, #GATE] jmp$update_profile_in_tables
    (    access_id: ost$binary_unique_name;
         changed_job_classes_p: ^jmt$job_class_table;
         changed_service_classes_p: ^jmt$service_class_table;
         changed_applications_p: ^jmt$application_table;
         controls_p: ^jmt$job_scheduler_table;
     VAR status: ost$status);

    VAR
      actual_value: integer,
      current_profile_id: ost$name,
      decreased_maxaj: boolean,
      decreased_maxaj_classes: jmt$service_class_set,
      dispatching_control_change: boolean,
      dispatching_control_changes: boolean,
      entry: ost$non_negative_integers,
      ignore_status: ost$status,
      increased_maxaj: boolean,
      increased_maxaj_classes: jmt$service_class_set,
      refresh_candidates: boolean,
      refresh_job_candidates: boolean;

    status.normal := TRUE;
    dispatching_control_changes := FALSE;
    decreased_maxaj_classes := $jmt$service_class_set [];
    increased_maxaj_classes := $jmt$service_class_set [];
    refresh_candidates := FALSE;

    jmp$verify_utility_access_id (access_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);
    osp$get_locked_variable_value (jmv$scheduler_tables_access.count, {expected_value} 0, actual_value);
    WHILE actual_value <> 0 DO
      pmp$delay (500, ignore_status);
      osp$get_locked_variable_value (jmv$scheduler_tables_access.count, {expected_value} 0, actual_value);
    WHILEND;

    IF changed_job_classes_p <> NIL THEN
      current_profile_id := jmv$job_scheduler_table.profile_identification;
      FOR entry := 1 TO UPPERBOUND (changed_job_classes_p^) DO
        update_job_class_table (changed_job_classes_p^ [entry], {install_profile} FALSE, current_profile_id,
              refresh_job_candidates);
        refresh_candidates := refresh_candidates OR refresh_job_candidates;
      FOREND;
    IFEND;

    IF changed_service_classes_p <> NIL THEN
      FOR entry := 1 TO UPPERBOUND (changed_service_classes_p^) DO
        update_service_class_table (changed_service_classes_p^ [entry], dispatching_control_change,
              decreased_maxaj, increased_maxaj);
        dispatching_control_changes := dispatching_control_changes OR dispatching_control_change;
        IF decreased_maxaj THEN
          decreased_maxaj_classes := decreased_maxaj_classes +
                $jmt$service_class_set [changed_service_classes_p^ [entry].index];
        IFEND;
        IF increased_maxaj THEN
          increased_maxaj_classes := increased_maxaj_classes +
                $jmt$service_class_set [changed_service_classes_p^ [entry].index];
        IFEND;
      FOREND;
    IFEND;

    update_application_table (changed_applications_p);

    update_job_scheduler_table (controls_p, refresh_job_candidates);
    refresh_candidates := refresh_candidates OR refresh_job_candidates;

    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

{ Issue the job scheduler events to process the service class table changes.

    IF decreased_maxaj_classes <> $jmt$service_class_set [] THEN
      jmp$check_active_job_limits (decreased_maxaj_classes);
    IFEND;

    IF increased_maxaj_classes <> $jmt$service_class_set [] THEN
      jmp$set_class_below_maxaj_limit (increased_maxaj_classes);
    IFEND;

    IF dispatching_control_changes THEN
      jmp$set_event_and_ready_sched (jmc$change_dispatching_controls);
    IFEND;

{ Issue the job scheduler event to refresh the job candidates for initiation.

    IF refresh_candidates THEN
      jmv$refresh_job_candidates := TRUE;
      jmp$set_event_and_ready_sched (jmc$examine_input_queue);
    IFEND;

  PROCEND jmp$update_profile_in_tables;
?? TITLE := '[XDCL, #GATE] jmp$verify_utility_access_id', EJECT ??
*copy jmh$verify_utility_access_id

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

    VAR
      global_task_id: ost$global_task_id;

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);

    IF NOT jmv$scheduling_utility_usage.active THEN
      osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);
      osp$set_status_condition (jme$no_utility_is_active, status);
      RETURN;
    IFEND;
    pmp$get_executing_task_gtid (global_task_id);
    IF (access_id <> jmv$scheduling_utility_usage.access_id) OR
          (global_task_id <> jmv$scheduling_utility_usage.global_task_id) THEN
      osp$set_status_condition (jme$access_id_mismatch, status);
    IFEND;

    osp$clear_mainframe_sig_lock (jmv$scheduling_utility_usage.lock);

  PROCEND jmp$verify_utility_access_id;

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

{ PURPOSE:
{   This procedure processes the scheduler event to change the dispatching control
{   information in the service class table.
{ DESIGN:
{   The list of changes to be made must be locked so that another change service table
{   command cannot add to the list during processing.  A monitor request must be made
{   to change the dispatching control field in the service class table.
{   After the dispatching controls have been changed, the swapin candidate queues (which
{   are ordered by dispatching priority) must be re-ordered.

  PROCEDURE [XDCL, #GATE] jmp$process_change_dispatching;

    VAR
      changed_classes: jmt$service_class_set,
      dispatching_ctrl_p_to_free: ^jmt$dispatching_control_changes,
      rb: jmt$rb_scheduler_requests;

{inline of jmp$incr_scheduler_statistics (jmc$change_dispatching);
    jmv$job_scheduler_statistics [jmc$change_dispatching] :=
          jmv$job_scheduler_statistics [jmc$change_dispatching] + 1;

    osp$set_mainframe_sig_lock (jmv$change_dispatching_list.lock);

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

{ Free the space used by the change dispatching control list and determine the set of classes
{ that have been changed.

    changed_classes := $jmt$service_class_set [];
    WHILE jmv$change_dispatching_list.dispatching_control_changes_p <> NIL DO
      dispatching_ctrl_p_to_free := jmv$change_dispatching_list.dispatching_control_changes_p;
      changed_classes := changed_classes + $jmt$service_class_set
            [jmv$change_dispatching_list.dispatching_control_changes_p^.change_service_class];
      jmv$change_dispatching_list.dispatching_control_changes_p :=
            jmv$change_dispatching_list.dispatching_control_changes_p^.dispatching_control_changes_p;
      FREE dispatching_ctrl_p_to_free IN osv$mainframe_wired_heap^;
    WHILEND;

    jmp$clear_scheduler_event (jmc$change_dispatching_controls);

    osp$clear_mainframe_sig_lock (jmv$change_dispatching_list.lock);

    jmp$reorder_swapin_queues (changed_classes);
    jmp$set_event_and_ready_sched (jmc$examine_swapin_queue);
    jmp$set_event_and_ready_sched (jmc$examine_input_queue);

  PROCEND jmp$process_change_dispatching;

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

{ PURPOSE:
{   This procedure processes requests to change a job's dispatching priority.
{ DESIGN:
{   A monitor request must be issued to change the job's dispatching priority, because
{   the change must be synchronized with dispatcher and swapper in monitor.

  PROCEDURE [XDCL, #GATE] jmp$change_dispatching_prior_r1
    (    request_origin: tmt$change_priority_origin;
         ijl_ordinal: jmt$ijl_ordinal;
         system_supplied_name: jmt$system_supplied_name;
         dispatching_control_info: jmt$dispatching_control_info;
     VAR status: ost$status);

    VAR
      rb: tmt$rb_update_job_task_enviro;

    rb.reqcode := syc$rc_update_job_task_enviro;
    rb.subcode := tmc$ujte_dispatching_priority;
    rb.request_origin := request_origin;
    rb.ijl_ordinal := ijl_ordinal;
    rb.system_supplied_name := system_supplied_name;
    rb.dispatching_control_info := dispatching_control_info;
    i#call_monitor (#LOC (rb), #SIZE (rb));
    syp$set_status_from_mtr_status (rb.status, status);

  PROCEND jmp$change_dispatching_prior_r1;
?? TITLE := 'update_application_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to update the current application table with
{   the given list of existing applications whose attributes have changed.
{ DESIGN:
{   The application name and profile identification are never updated since
{   changing them would constitute a change in the structure of the scheduling profile.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE update_application_table
    (    changed_applications_p: ^jmt$application_table);

    VAR
      temp: integer,
      application_name: jmt$application_name,
      high_index: jmt$application_index,
      i: integer,
      low_index: jmt$application_index,
      middle_index: jmt$application_index;

    IF (changed_applications_p = NIL) OR (jmv$application_table_p = NIL) THEN
      RETURN;
    IFEND;

    FOR i := 1 TO UPPERBOUND (changed_applications_p^) DO
      low_index := 1;
      high_index := UPPERBOUND (jmv$application_table_p^);
      application_name := changed_applications_p^ [i].name;

    /binary_search_for_application/
      REPEAT
        temp := low_index + high_index;
        middle_index := temp DIV 2;
        IF application_name < jmv$application_table_p^ [middle_index].name THEN
          high_index := middle_index - 1;
        ELSEIF application_name > jmv$application_table_p^ [middle_index].name THEN
          low_index := middle_index + 1;
        ELSE { Application name matches }

{ Update the Definition group attributes.

          jmv$application_table_p^ [middle_index].enable_application_scheduling :=
                changed_applications_p^ [i].enable_application_scheduling;

{ Update the Control group attributes.

          jmv$application_table_p^ [middle_index].cyclic_aging_interval :=
                changed_applications_p^ [i].cyclic_aging_interval;
          jmv$application_table_p^ [middle_index].maximum_working_set :=
                changed_applications_p^ [i].maximum_working_set;
          jmv$application_table_p^ [middle_index].minimum_working_set :=
                changed_applications_p^ [i].minimum_working_set;
          jmv$application_table_p^ [middle_index].page_aging_interval :=
                changed_applications_p^ [i].page_aging_interval;
          jmv$application_table_p^ [middle_index].service_class_index :=
                changed_applications_p^ [i].service_class_index;
          EXIT /binary_search_for_application/;
        IFEND;
      UNTIL low_index > high_index;
    FOREND;

  PROCEND update_application_table;
?? TITLE := 'update_job_class_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to update the current job class table with
{   the given existing job class whose attributes have changed.
{ DESIGN:
{   The job class name and index are never updated.  The profile identification,
{   abbreviation, and all Membership group attributes are only updated when
{   a profile is being installed since these changes constitute a structure
{   change in the scheduling profile.  The enable class initiation attribute is
{   not updated for the UNASSIGNED job class when a profile is installed.
{   If this is the first profile installed since the system was deadstarted,
{   initiation of jobs is prevented for all classes except SYSTEM and
{   MAINTENANCE by setting the initiation_level to zero.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE update_job_class_table
    (    changed_job_class: jmt$job_class_attributes;
         install_profile: boolean;
         current_profile_id: ost$name;
     VAR refresh_job_candidates: boolean);

    VAR
      job_class_index: jmt$job_class,
      local_job_class: jmt$job_class_attributes,
      previous_maxws_maximum: jmt$working_set_size;

    refresh_job_candidates := FALSE;

    job_class_index := changed_job_class.index;
    local_job_class := changed_job_class;

{ Retain the elements of the job class table which must not be updated.

    local_job_class.defined := jmv$job_class_table_p^ [job_class_index].defined;
    local_job_class.next_rank_class := jmv$job_class_table_p^ [job_class_index].next_rank_class;
    IF install_profile AND (job_class_index = jmc$unassigned_job_class) THEN
      local_job_class.enable_class_initiation := jmv$job_class_table_p^ [job_class_index].
            enable_class_initiation;
    IFEND;
    IF install_profile AND (current_profile_id = jmc$sched_profile_deadstart_id) AND
          (job_class_index > jmc$maintenance_job_class) THEN
      local_job_class.initiation_level.preferred := 0;
      local_job_class.initiation_level.maximum_increment := 0;
    IFEND;

{ Retain the elements of the job class table which need special processing to
{ be updated.

    local_job_class.prolog_p := jmv$job_class_table_p^ [job_class_index].prolog_p;
    local_job_class.epilog_p := jmv$job_class_table_p^ [job_class_index].epilog_p;
    previous_maxws_maximum := jmv$job_class_table_p^ [job_class_index].maximum_working_set.maximum;

{ Check for attribute changes which require the job candidates for execution to
{ be refreshed.

    IF (jmv$job_class_table_p^ [job_class_index].enable_class_initiation <>
          local_job_class.enable_class_initiation) OR (jmv$job_class_table_p^ [job_class_index].
          initial_service_class_index <> local_job_class.initial_service_class_index) OR
          (jmv$job_class_table_p^ [job_class_index].initiation_level <> local_job_class.initiation_level) THEN
      refresh_job_candidates := TRUE;
    IFEND;

    jmv$job_class_table_p^ [job_class_index] := local_job_class;

{ Process any changes in maximum working set, prolog, and epilog after the job
{ class table has been updated.  jmv$max_class_working_set only needs to be examined if there are jobs
{ of this class initiated.  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 greater than zero if the job class is
{ NOT the system_job_class and a count greater than 1 if the job class is the system_job_class.

    IF (jmv$job_counts.job_class_counts [job_class_index].
          initiated_jobs > $INTEGER (jmc$system_job_class = job_class_index)) THEN
      IF jmv$job_class_table_p^ [job_class_index].maximum_working_set.maximum > jmv$max_class_working_set THEN
        jmv$max_class_working_set := jmv$job_class_table_p^ [job_class_index].maximum_working_set.maximum;
      ELSEIF (previous_maxws_maximum = jmv$max_class_working_set) THEN
        jmp$reset_max_class_working_set;
      IFEND;
    IFEND;


    IF jmv$job_class_table_p^ [job_class_index].prolog_p <> NIL THEN
      IF changed_job_class.prolog_p <> NIL THEN
        IF jmv$job_class_table_p^ [job_class_index].prolog_p^ <> changed_job_class.prolog_p^ THEN
          FREE jmv$job_class_table_p^ [job_class_index].prolog_p IN osv$mainframe_pageable_heap^;
          ALLOCATE jmv$job_class_table_p^ [job_class_index].prolog_p:
                [STRLENGTH (changed_job_class.prolog_p^)] IN osv$mainframe_pageable_heap^;
          jmv$job_class_table_p^ [job_class_index].prolog_p^ := changed_job_class.prolog_p^;
        IFEND;
      ELSE
        FREE jmv$job_class_table_p^ [job_class_index].prolog_p IN osv$mainframe_pageable_heap^;
      IFEND;
    ELSEIF changed_job_class.prolog_p <> NIL THEN
      ALLOCATE jmv$job_class_table_p^ [job_class_index].prolog_p: [STRLENGTH (changed_job_class.prolog_p^)] IN
            osv$mainframe_pageable_heap^;
      jmv$job_class_table_p^ [job_class_index].prolog_p^ := changed_job_class.prolog_p^;
    IFEND;

    IF jmv$job_class_table_p^ [job_class_index].epilog_p <> NIL THEN
      IF changed_job_class.epilog_p <> NIL THEN
        IF jmv$job_class_table_p^ [job_class_index].epilog_p^ <> changed_job_class.epilog_p^ THEN
          FREE jmv$job_class_table_p^ [job_class_index].epilog_p IN osv$mainframe_pageable_heap^;
          ALLOCATE jmv$job_class_table_p^ [job_class_index].epilog_p:
                [STRLENGTH (changed_job_class.epilog_p^)] IN osv$mainframe_pageable_heap^;
          jmv$job_class_table_p^ [job_class_index].epilog_p^ := changed_job_class.epilog_p^;
        IFEND;
      ELSE
        FREE jmv$job_class_table_p^ [job_class_index].epilog_p IN osv$mainframe_pageable_heap^;
      IFEND;
    ELSEIF changed_job_class.epilog_p <> NIL THEN
      ALLOCATE jmv$job_class_table_p^ [job_class_index].epilog_p: [STRLENGTH (changed_job_class.epilog_p^)] IN
            osv$mainframe_pageable_heap^;
      jmv$job_class_table_p^ [job_class_index].epilog_p^ := changed_job_class.epilog_p^;
    IFEND;

  PROCEND update_job_class_table;
?? TITLE := 'update_job_scheduler_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to update the current job scheduler table with
{   the given job scheduler controls which have changed.
{ DESIGN:
{   The profile identification and all Membership group attributes are never
{   updated since changing these would constitute a change in the structure of
{   the scheduling profile.
{   If dispatching allocation is being changed, monitor request must be issued to
{   change tables used by dispatcher.  The scheduler table must be updated before
{   the monitor request is issued.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE update_job_scheduler_table
    (    controls_p: ^jmt$job_scheduler_table;
     VAR refresh_job_candidates: boolean);

    VAR
      dp: 1 .. 15,
      local_controls: jmt$job_scheduler_table,
      rb: jmt$rb_scheduler_requests;

    refresh_job_candidates := FALSE;

    IF controls_p = NIL THEN
      RETURN;
    IFEND;

    local_controls := controls_p^;

{ Retain the current Membership group attributes.

    local_controls.validation_categories_p := jmv$job_scheduler_table.validation_categories_p;

{ Check for attribute changes which require the job candidates for execution to be refreshed.

    IF (jmv$job_scheduler_table.initiation_excluded_categories <>
          local_controls.initiation_excluded_categories) OR (jmv$job_scheduler_table.
          initiation_required_categories <> local_controls.initiation_required_categories) THEN
      refresh_job_candidates := TRUE;
    IFEND;

    jmv$job_scheduler_table := local_controls;

{ Issue a monitor request to check and change the dispatching allocation controls.

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

{ Update the dual-state dispatching priority table. This is the table that
{ the dispatcher reads during task selection. It does not have to be updated in
{ monitor mode. The table will be wrong (because not updated in MM) for a maximum
{ a one timeslice. The table has the priorities and subpriorities for NOS/VE priorities
{ P1 through P14. Only the values P1 through P10 can be modified by the scheduling utilities.
{ Priorities P11 through P14 are hard-coded.

    FOR dp := jmc$priority_p1 TO jmc$priority_p10 DO
      tmv$dual_state_dispatch_prior [dp].dual_state_priority :=
            jmv$job_scheduler_table.dual_state_priority_control [dp].priority;
      tmv$dual_state_dispatch_prior [dp].subpriority := jmv$job_scheduler_table.
            dual_state_priority_control [dp].subpriority;
    FOREND;

  PROCEND update_job_scheduler_table;
?? TITLE := 'update_service_class_table', EJECT ??

{ PURPOSE:
{   The purpose of this request is to update the current service class table with
{   the given existing service class whose attributes have changed.
{ DESIGN:
{   The service class name and index are never updated.  The profile
{   identification and abbreviation are only updated when a profile is being
{   installed since these changes constitute a structure change in the
{   scheduling profile.
{   Because dispatcher is using the dispatching control field in monitor mode
{   during task switch, the dispatching control field of the service class
{   table cannot be changed in job mode.  Dispatching control changes must be
{   synchronized by setting a scheduler event to have scheduler issue a monitor
{   request to change the dispatching control field.
{ NOTES:
{   Data validity checks are made and access to the scheduler tables is
{   interlocked prior to calling this procedure.

  PROCEDURE update_service_class_table
    (    changed_service_class: jmt$service_class_attributes;
     VAR dispatching_control_change: boolean;
     VAR decreased_maxaj: boolean;
     VAR increased_maxaj: boolean);

    VAR
      dispatching_control_p: ^jmt$dispatching_control_changes,
      entry: jmt$dispatching_control_index,
      local_service_class: jmt$service_class_attributes,
      new_dispatching_control_p: ^jmt$dispatching_control_changes,
      next_dispatching_control_p: ^jmt$dispatching_control_changes,
      old_value_maxaj: jmt$maximum_active_jobs,
      service_class_index: jmt$service_class_index,
      service_class_p: ^jmt$service_class_attributes;

    dispatching_control_change := FALSE;
    decreased_maxaj := FALSE;
    increased_maxaj := FALSE;

    service_class_index := changed_service_class.index;
    local_service_class := changed_service_class;
    service_class_p := ^jmv$service_classes [service_class_index]^.attributes;

{ Retain the elements of the service class table which must not be updated.

    local_service_class.defined := service_class_p^.defined;
    local_service_class.dispatching_control := service_class_p^.dispatching_control;

{ Check for changes in attributes that require special processing.

    old_value_maxaj := service_class_p^.maximum_active_jobs;
    IF old_value_maxaj > changed_service_class.maximum_active_jobs THEN
      decreased_maxaj := TRUE;
    ELSEIF old_value_maxaj < changed_service_class.maximum_active_jobs THEN
      IF (service_class_index IN jmv$classes_in_maxaj_limit_wait) THEN
        increased_maxaj := TRUE;
      IFEND;
    IFEND;

    IF changed_service_class.next_service_class_index = jmc$null_service_class THEN
      local_service_class.next_service_class_index := service_class_index;
    IFEND;

    service_class_p^ := local_service_class;

{ Process any changes in dispatching control after the service class table has been updated.

    FOR entry := jmc$min_dispatching_control TO jmc$max_dispatching_control DO
      IF local_service_class.dispatching_control [entry] <>
            changed_service_class.dispatching_control [entry] THEN
        dispatching_control_change := TRUE;
      IFEND;
    FOREND;

    IF dispatching_control_change THEN

      osp$set_mainframe_sig_lock (jmv$change_dispatching_list.lock);

      ALLOCATE new_dispatching_control_p IN osv$mainframe_wired_heap^;
      new_dispatching_control_p^.change_service_class := service_class_index;
      new_dispatching_control_p^.dispatching_control_info := changed_service_class.dispatching_control;
      new_dispatching_control_p^.dispatching_control_changes_p := NIL;

{ Link the new set of dispatching control changes into the list for monitor.

      IF jmv$change_dispatching_list.dispatching_control_changes_p = NIL THEN
        jmv$change_dispatching_list.dispatching_control_changes_p := new_dispatching_control_p;
      ELSE
        next_dispatching_control_p := jmv$change_dispatching_list.dispatching_control_changes_p;
        WHILE next_dispatching_control_p <> NIL DO
          dispatching_control_p := next_dispatching_control_p;
          next_dispatching_control_p := dispatching_control_p^.dispatching_control_changes_p;
        WHILEND;
        dispatching_control_p^.dispatching_control_changes_p := new_dispatching_control_p;
      IFEND;

      osp$clear_mainframe_sig_lock (jmv$change_dispatching_list.lock);

    IFEND;

  PROCEND update_service_class_table;

MODEND jmm$job_scheduler_utility;
