?? RIGHT := 110 ??
?? TITLE := 'NOS/VE: job template management' ??
MODULE sym$job_template_management;

{ PURPOSE
{  This module supports the management of multiple job templates in the system
{  core environment.

?? NEWTITLE := '  Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc gft$system_file_identifier
*copyc mmt$segment_descriptor_table
*copyc oss$job_fixed
*copyc ost$heap
*copyc ost$name
*copyc ost$segment_descriptor
*copyc ost$segment_set
*copyc pmt$virtual_memory_image_header
*copyc sye$job_template_conditions
?? POP ??
*copyc dmp$create_file_entry
*copyc dmp$destroy_file
*copyc gfp$get_locked_fde_p
*copyc gfp$unlock_fde_p
*copyc i#move
*copyc jmp$determine_job_class
*copyc jmp$get_ijle_p
*copyc mmp$close_device_file
*copyc mmp$get_sdtx_entry_p
*copyc mmp$open_file_by_sfid
*copyc osp$append_status_parameter
*copyc osp$clear_mainframe_sig_lock
*copyc osp$fatal_system_error
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc pmp$find_executing_task_xcb
*copyc syp$establish_condition_handler

*copyc jmv$ijl_p
*copyc jmv$jcb
*copyc jmv$job_class_table_p
*copyc jmv$job_counts
*copyc jmv$job_trap_handler
*copyc jmv$system_core_id
*copyc jmv$system_core_template
*copyc mmv$default_sdtx_entry
*copyc mmv$max_template_segment_number
*copyc osv$cpus_physically_configured
*copyc osv$mainframe_pageable_heap
*copyc syv$job_template_cbp
*copyc syv$job_template_ptr_array
*copyc tmv$job_debug_ring
?? TITLE := '  Global Declarations Declared by This Module', EJECT ??

  TYPE
    template_definition = record
      template_name: ost$name,
      code_base_pointer: ost$external_code_base_pointer,
      job_class_names: ^array [1 .. * ] of ost$name,
      segments: ^array [1 .. * ] of template_segment_definition,
    recend,

    template_segment_definition = record
      segment_number: ost$segment,
      segment_descriptor: ost$segment_descriptor,
      case segment_kind: (shared, job_unique, task_unique, skip) of
      = shared =
        sfid: gft$system_file_identifier,
      = job_unique, task_unique =
        location: ^array [1 .. * ] of cell,
        length: integer,
      casend,
    recend;

  VAR
    syv$job_template_lock: ost$signature_lock := [0],
    syv$highest_in_use: integer := 0,
    syv$nosve_job_template: [XDCL, #GATE, oss$job_fixed] boolean := TRUE,
    syv$job_template_name: [XDCL, #GATE, oss$job_fixed] ost$name :=
          '                               ',
    syv$job_templates: [XDCL] array [1 .. 10] of
          template_definition := [REP 10 of [osc$null_name, * , NIL, NIL]];


?? TITLE := '  PROCEDURE syp$activate_job_template' ??
?? EJECT ??

{  The purpose of this procedure is to create the mainframe scope data
{ structures that represent a job template.  Template data is copied
{ either to mainframe pageable (non-shared data) or to a DM temp global
{ file (shared code/data).  Validation is performed to insure that only
{ one template exists for any given job class.

  PROCEDURE [XDCL, #GATE] syp$activate_job_template
    (    template_file: ^SEQ ( * );
         template_name: ost$name;
         job_unique_segments,
         task_unique_segments: ost$segment_set;
         classes: ^array [1 .. * ] of ost$name;
     VAR status: ost$status);

    PROCEDURE activate_template_ch
      (    mf: ost$monitor_fault;
           msa_p: ^ost$minimum_save_area;
       VAR continue: syt$continue_option);

      osp$set_status_abnormal (syc$system_core_id, sye$template_condition, '', status);
      osp$clear_mainframe_sig_lock (syv$job_template_lock);
      EXIT syp$activate_job_template;

    PROCEND activate_template_ch;

    VAR
      syv$recovery_override: [XREF] 0 .. 0ff(16);

    VAR
      class_membership: integer,
      class_name_index: integer,
      data: ^array [1 .. * ] of cell,
      fde_p: gft$file_desc_entry_p,
      file_attributes: array [1 .. 1] of dmt$new_file_attribute,
      gfn: dmt$global_file_name,
      local_status: ost$status,
      p_file_data: ^cell,
      sdtxe_p: ^mmt$segment_descriptor_extended,
      segment_header: ^pmt$linked_segment_description,
      segment_index: integer,
      segment_number: ost$segment,
      template: ^SEQ ( * ),
      template_header: ^pmt$virtual_memory_image_header,
      template_index: integer,
      xcb: ^ost$execution_control_block;

    osp$set_mainframe_sig_lock (syv$job_template_lock);
    syp$establish_condition_handler (^activate_template_ch);
    FOR template_index := LOWERBOUND (syv$job_templates)
          TO UPPERBOUND (syv$job_templates) DO
      IF syv$job_templates [template_index].template_name <> osc$null_name THEN
        IF syv$job_templates [template_index].template_name =
              template_name THEN
          osp$set_status_abnormal (syc$system_core_id,
                sye$duplicate_template_name, template_name, status);
          osp$clear_mainframe_sig_lock (syv$job_template_lock);
          RETURN;
        IFEND;
        FOR class_membership := LOWERBOUND (syv$job_templates [template_index].
              job_class_names^) TO UPPERBOUND (syv$job_templates
              [template_index].job_class_names^) DO
          FOR class_name_index := 1 TO UPPERBOUND (classes^) DO
            IF syv$job_templates [template_index].
                  job_class_names^ [class_membership] =
                  classes^ [class_name_index] THEN
              osp$set_status_abnormal (syc$system_core_id,
                    sye$duplicate_class_definition, syv$job_templates [template_index].template_name,
                    status);
              osp$append_status_parameter (osc$status_parameter_delimiter,
                    classes^ [class_name_index], status);
              osp$clear_mainframe_sig_lock (syv$job_template_lock);
              RETURN;
            IFEND;
          FOREND;
        FOREND;
      IFEND;
    FOREND;

  /find_free_entry/
    BEGIN
      FOR template_index := LOWERBOUND (syv$job_templates)
            TO UPPERBOUND (syv$job_templates) DO
        IF syv$job_templates [template_index].template_name =
              osc$null_name THEN
          EXIT /find_free_entry/;
        IFEND;
      FOREND;
      osp$set_status_abnormal (syc$system_core_id, sye$job_template_table_full,
            '', status);
      osp$clear_mainframe_sig_lock (syv$job_template_lock);
      RETURN;
    END /find_free_entry/;
    IF template_index > syv$highest_in_use THEN
      syv$highest_in_use := template_index;
    IFEND;

    template := template_file;

    NEXT template_header IN template;

    IF (template_header^.system_core_id <> jmv$system_core_id) AND (syv$recovery_override = 0) THEN
      osp$set_status_abnormal (syc$system_core_id, sye$template_core_mismatch,
            '', status);
      osp$clear_mainframe_sig_lock (syv$job_template_lock);
      RETURN;
    IFEND;

    syv$job_templates [template_index].code_base_pointer :=
          template_header^.starting_procedure;

    ALLOCATE syv$job_templates [template_index].job_class_names:
          [1 .. UPPERBOUND (classes^)] IN osv$mainframe_pageable_heap^;

    FOR class_membership := 1 TO UPPERBOUND (classes^) DO
      syv$job_templates [template_index].job_class_names^ [class_membership] :=
            classes^ [class_membership];
    FOREND;

    ALLOCATE syv$job_templates [template_index].segments:
          [1 .. template_header^.number_of_segments] IN
          osv$mainframe_pageable_heap^;
    FOR segment_index := 1 TO template_header^.number_of_segments DO
      syv$job_templates [template_index].segments^ [segment_index].
            segment_kind := skip;
    FOREND;

    FOR segment_index := 1 TO template_header^.number_of_segments DO
      NEXT segment_header IN template;

      syv$job_templates [template_index].segments^ [segment_index].
            segment_number := segment_header^.segment_number;
      IF segment_header^.segment_number > mmv$max_template_segment_number THEN
        mmv$max_template_segment_number := segment_header^.segment_number;
      IFEND;
      syv$job_templates [template_index].segments^ [segment_index].
            segment_descriptor := segment_header^.segment_descriptor;

      IF (syv$job_templates [template_index].segments^ [segment_index].
            segment_number IN job_unique_segments) OR
            (syv$job_templates [template_index].segments^ [segment_index].
            segment_number IN task_unique_segments) THEN
        IF syv$job_templates [template_index].segments^ [segment_index].
              segment_number IN task_unique_segments THEN
          syv$job_templates [template_index].segments^ [segment_index].
                segment_kind := task_unique;
        ELSEIF syv$job_templates [template_index].segments^ [segment_index].
              segment_number IN job_unique_segments THEN
          syv$job_templates [template_index].segments^ [segment_index].
                segment_kind := job_unique;
        IFEND;
        ALLOCATE syv$job_templates [template_index].segments^ [segment_index].
              location: [1 .. segment_header^.length] IN
              osv$mainframe_pageable_heap^;
        syv$job_templates [template_index].segments^ [segment_index].length :=
              segment_header^.length;
        NEXT data: [1 .. segment_header^.length] IN template;
        i#move (data, syv$job_templates [template_index].
              segments^ [segment_index].location, segment_header^.length);
      ELSE
        syv$job_templates [template_index].segments^ [segment_index].
              segment_kind := shared;
        IF (osv$cpus_physically_configured > 1) AND
              (syv$job_templates [template_index].segments^ [segment_index].segment_descriptor.wp <>
              osc$non_writable) THEN
          syv$job_templates [template_index].segments^ [segment_index].segment_descriptor.vl :=
                osc$vl_cache_bypass;
        IFEND;
{
{   The use of file_attributes is in support of Dynamic File Space Limits
{
        file_attributes [1].keyword := dmc$owner;
        file_attributes [1].file_space_limit := sfc$no_limit;

        dmp$create_file_entry (gfc$fk_global_unnamed, -$pft$usage_selections [],
              -$pft$share_selections [], dmc$minimum_file_share_his, ^file_attributes,
              segment_header^.length, TRUE, gfn,
              syv$job_templates [template_index].segments^ [segment_index].
              sfid, status);
        IF NOT status.normal THEN
          syp$deactivate_job_template (template_name, local_status);
          osp$clear_mainframe_sig_lock (syv$job_template_lock);
          RETURN;
        IFEND;
        {Open as PF
        mmp$open_file_by_sfid (syv$job_templates [template_index].
              segments^ [segment_index].sfid, 1, 1, mmc$as_sequential,
              mmc$sar_write_extend, segment_number, status);
        IF NOT status.normal THEN
          syp$deactivate_job_template (template_name, local_status);
          osp$clear_mainframe_sig_lock (syv$job_template_lock);
          RETURN;
        IFEND;
        p_file_data := #ADDRESS (1, segment_number, 0);
        NEXT data: [1 .. segment_header^.length] IN template;
        i#move (data, p_file_data, segment_header^.length);
        {Update segment kind!!
        pmp$find_executing_task_xcb (xcb);
        sdtxe_p := mmp$get_sdtx_entry_p (xcb, segment_number);
        gfp$get_locked_fde_p (sdtxe_p^.sfid, fde_p);
        fde_p^.flags.global_template_file := TRUE;
        gfp$unlock_fde_p (fde_p);

        {No hole here - just does a purge map
        mmp$close_device_file (segment_number, status);
      IFEND;

    FOREND;

    {Update name last while still locked to prevent use until installed
    syv$job_templates [template_index].template_name := template_name;

    osp$clear_mainframe_sig_lock (syv$job_template_lock);

  PROCEND syp$activate_job_template;
?? TITLE := '  PROCEDURE syp$create_job_template' ??
?? EJECT ??

{  The purpose of this procedure is to initialize the "job monitor" task
{ of a job template "job" that is being initiated.  This routine is called
{ only by the job scheduler.  It assumes that the job fixed segment is
{ initialized with the NOS/VE system core environment.  It must add
{ segments to this environment as defined by the job template.  All
{ segments being added must have the proper MM attributes to insure
{ the correct segments are shared between tasks.

  PROCEDURE [XDCL] syp$create_job_template
    (    ijlo: jmt$ijl_ordinal;
         job_class: jmt$job_class;
         sdt_p: mmt$max_sdt_p;
         sdtx_p: mmt$max_sdtx_p;
     VAR template_created: boolean);

    VAR
      segment_index,
      template_index: integer,
      job_class_name: ost$name,
      p_boolean: ^boolean,
      p_cbp: ^ost$external_code_base_pointer,
      local_status: ost$status,
      segment: ost$segment;

    template_created := FALSE;

    job_class_name := jmv$job_class_table_p^ [job_class].name;

    find_job_class (job_class_name, template_index, local_status);
    IF NOT local_status.normal THEN
      RETURN;
    IFEND;

    FOR segment_index := LOWERBOUND (syv$job_templates [template_index].
          segments^) TO UPPERBOUND (syv$job_templates [template_index].
          segments^) DO
      segment := syv$job_templates [template_index].segments^ [segment_index].
            segment_number;
      IF sdt_p^.st [segment].ste.vl <> osc$vl_invalid_entry THEN
        RETURN;
      IFEND;

      sdt_p^.st [segment].ste := syv$job_templates [template_index].
            segments^ [segment_index].segment_descriptor;
      sdt_p^.st [segment].ste.asid := 0;
      sdtx_p^.sdtx_table [segment] := mmv$default_sdtx_entry;
      CASE syv$job_templates [template_index].segments^ [segment_index].
            segment_kind OF
      = shared =
        sdtx_p^.sdtx_table [segment].inheritance := mmc$si_share_segment;
        sdtx_p^.sdtx_table [segment].access_state := mmc$sas_allow_access;
        sdtx_p^.sdtx_table [segment].sfid := syv$job_templates [template_index].
              segments^ [segment_index].sfid;
        sdtx_p^.sdtx_table [segment].open_validating_ring_number := 0;
      = task_unique =
        sdtx_p^.sdtx_table [segment].inheritance := mmc$si_new_segment;
        sdtx_p^.sdtx_table [segment].open_validating_ring_number := 1;
      = job_unique =
        sdtx_p^.sdtx_table [segment].inheritance := mmc$si_share_segment;
        sdtx_p^.sdtx_table [segment].open_validating_ring_number := 1;
      ELSE
        RETURN;
      CASEND;
    FOREND;

    p_boolean := #ADDRESS (1, #SEGMENT (sdt_p),
          #OFFSET (^syv$nosve_job_template));
    p_boolean^ := FALSE;

    p_cbp := #ADDRESS (1, #SEGMENT (sdt_p), #OFFSET (^syv$job_template_cbp));
    p_cbp^ := syv$job_templates [template_index].code_base_pointer;

    template_created := TRUE;

  PROCEND syp$create_job_template;
?? TITLE := '  PROCEDURE syp$initialize_syscore_template' ??
?? EJECT ??

{  The purpose of this procedure is to update the [static] definition
{ of the NOS/VE system core template.  It is called from
{ osm$job_template_initialization.

  PROCEDURE [XDCL, #GATE] syp$initialize_syscore_template;

    VAR
      p_boolean: ^boolean,
      p_cbp: ^ost$external_code_base_pointer,
      p_job_template_ptr_array: ^^array [1 .. * ] of ^cell,
      segment: ost$segment,
      offset: ost$segment_offset;

    segment := #SEGMENT (jmv$system_core_template.job_fixed_template_p);
    offset := #OFFSET (jmv$system_core_template.job_fixed_template_p);

    p_cbp := #ADDRESS (1, segment, offset + #OFFSET (^syv$job_template_cbp));
    p_cbp^ := syv$job_template_cbp;
    p_job_template_ptr_array := #ADDRESS (1, segment, offset +
          #OFFSET (^syv$job_template_ptr_array));
    p_job_template_ptr_array^ := syv$job_template_ptr_array;

  PROCEND syp$initialize_syscore_template;
?? TITLE := '  PROCEDURE syp$initialize_job_template' ??
?? EJECT ??

{  The purpose of this procedure is to complete the initialization of a
{ "job template" job.  It is called by the job template early during it's
{ initialization.  This procedure will copy the non-shared template data
{ segments
{ from the template description to actual task/job segments.  This operation
{ must
{ be performed from within each job/task so that the file system (MM/DM) will
{ operate correctly.  Job unique segments are only copied on the first call to
{ this procedure.  Task unique segments are copied on every call.  The job
{ template
{ must provide a ^procedure which is used by the system core trap mechanism
{ to communicate preemptively with the job template.

  PROCEDURE [XDCL, #GATE] syp$initialize_job_template
    (    update_trap_handler: boolean;
         trap_handler: ^procedure);

    VAR
      segment_index,
      template_index: integer,
      segment: ost$segment,
      job_template_initialized: [STATIC, oss$job_fixed] boolean := FALSE,
      status: ost$status,
      job_class_name: ost$name;

    job_class_name := jmv$job_class_table_p^
          [jmv$jcb.ijle_p^.job_scheduler_data.job_class].name;

    find_job_class (job_class_name, template_index, status);
    IF NOT status.normal THEN
      osp$fatal_system_error ('Initialize job template', ^status);
    IFEND;

    FOR segment_index := LOWERBOUND (syv$job_templates [template_index].
          segments^) TO UPPERBOUND (syv$job_templates [template_index].
          segments^) DO
      segment := syv$job_templates [template_index].segments^ [segment_index].
            segment_number;

      CASE syv$job_templates [template_index].segments^ [segment_index].
            segment_kind OF
      = shared =
        ;
      = task_unique =
        i#move (syv$job_templates [template_index].segments^ [segment_index].
              location, #ADDRESS (1, segment, 0),
              syv$job_templates [template_index].segments^ [segment_index].
              length);
      = job_unique =
        IF NOT job_template_initialized THEN
          i#move (syv$job_templates [template_index].segments^ [segment_index].
                location, #ADDRESS (1, segment, 0),
                syv$job_templates [template_index].segments^ [segment_index].
                length);
          syv$job_template_name := syv$job_templates [template_index].template_name;
        IFEND;
      ELSE
        osp$fatal_system_error ('Job template confusion', NIL);
      CASEND
    FOREND;

    IF update_trap_handler THEN
      jmv$job_trap_handler := trap_handler;
    IFEND;

    job_template_initialized := TRUE;

  PROCEND syp$initialize_job_template;

?? TITLE := '  PROCEDURE find_job_class' ??
?? EJECT ??

  PROCEDURE [INLINE] find_job_class
    (    name: ost$name;
     VAR template_index: integer;
     VAR status: ost$status);

    VAR
      class_membership: integer;

    FOR template_index := LOWERBOUND (syv$job_templates)
          TO syv$highest_in_use DO
      IF syv$job_templates [template_index].template_name <> osc$null_name THEN
        FOR class_membership := LOWERBOUND (syv$job_templates [template_index].
              job_class_names^) TO UPPERBOUND (syv$job_templates
              [template_index].job_class_names^) DO
          IF syv$job_templates [template_index].
                job_class_names^ [class_membership] = name THEN
            status.normal := TRUE;
            RETURN;
          IFEND;
        FOREND;
      IFEND;
    FOREND;

    {Cannot find template
    osp$set_status_abnormal (syc$system_core_id, sye$template_not_found, name,
          status);

  PROCEND find_job_class;
?? TITLE := '  PROCEDURE find_template' ??
?? EJECT ??

  PROCEDURE [INLINE] find_template
    (    name: ost$name;
     VAR template_index: integer;
     VAR status: ost$status);

    VAR
      class_membership: integer;

    FOR template_index := LOWERBOUND (syv$job_templates)
          TO syv$highest_in_use DO
      IF syv$job_templates [template_index].template_name = name THEN
        status.normal := TRUE;
        RETURN;
      IFEND;
    FOREND;

    {Cannot find template
    osp$set_status_abnormal (syc$system_core_id, sye$template_not_found, name,
          status);

  PROCEND find_template;
?? TITLE := '  PROCEDURE syp$deactivate_job_template' ??
?? EJECT ??

{  The purpose of this procedure is to cancel the effect of an
{ activate_job_template command. Mainframe pageable data is FREE'd
{ and DM files are destroyed.  The request is allowed only if
{ all job classes using the template have no queued or initiated
{ jobs and their job class limit is zero.

  PROCEDURE [XDCL, #GATE] syp$deactivate_job_template
    (    template_name: ost$name;
     VAR status: ost$status);

    VAR
      class_membership: integer,
      job_class: jmt$job_class,
      segment_index: integer,
      template_index: integer;


    osp$set_mainframe_sig_lock (syv$job_template_lock);
    find_template (template_name, template_index, status);
    IF NOT status.normal THEN
      osp$clear_mainframe_sig_lock (syv$job_template_lock);
      RETURN;
    IFEND;

  /verify_classes/
    FOR class_membership := 1 TO UPPERBOUND (syv$job_templates
          [template_index].job_class_names^) DO
      jmp$determine_job_class (syv$job_templates [template_index].
            job_class_names^ [class_membership], job_class, status);
      IF NOT status.normal THEN
        CYCLE /verify_classes/;
      IFEND;
      IF (jmv$job_counts.job_class_counts [job_class].initiated_jobs <> 0) OR
            (jmv$job_counts.job_class_counts [job_class].queued_jobs <> 0) THEN
        osp$clear_mainframe_sig_lock (syv$job_template_lock);
        osp$set_status_abnormal (syc$system_core_id, sye$jobs_still_active, '',
              status);
        RETURN;
      IFEND;
    FOREND /verify_classes/;

    syv$job_templates [template_index].template_name := osc$null_name;

    FREE syv$job_templates [template_index].job_class_names IN
          osv$mainframe_pageable_heap^;

    FOR segment_index := 1 TO UPPERBOUND (syv$job_templates [template_index].
          segments^) DO

      CASE syv$job_templates [template_index].segments^ [segment_index].
            segment_kind OF
      = task_unique, job_unique =
        FREE syv$job_templates [template_index].segments^ [segment_index].
              location IN osv$mainframe_pageable_heap^;
      = shared =
        dmp$destroy_file (syv$job_templates [template_index]. segments^ [segment_index].sfid, sfc$no_limit,
              status);
      = skip =
        {Skip
      ELSE
        osp$fatal_system_error ('Job template confusion', NIL);
      CASEND;
    FOREND;

    FREE syv$job_templates [template_index].segments IN
          osv$mainframe_pageable_heap^;

    status.normal := TRUE;

    osp$clear_mainframe_sig_lock (syv$job_template_lock);

  PROCEND syp$deactivate_job_template;

?? TITLE := '  PROCEDURE syp$set_job_debug_ring' ??
?? EJECT ??

  PROCEDURE [XDCL, #GATE] syp$set_job_debug_ring (job_debug_ring: ost$ring);

    tmv$job_debug_ring := job_debug_ring;

  PROCEND syp$set_job_debug_ring;
?? OLDTITLE, OLDTITLE ??
MODEND sym$job_template_management;
