?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE: Job Management - system supplied name management interfaces' ??
MODULE jmm$manage_system_supplied_name;

{ Purpose: This module is responsible for the initialization and generation of
{          system supplied names assigned by NOS/VE queue file management.

{ Design: The management of system supplied names across deadstarts has one
{         requirement.  DO NOT EVER assign a name to a job or output file
{         when one currently has that name.
{
{         The current implementation defines EVER as sometime over approximately
{         a year - it is assumed that by the time the system supplied name
{         "wraps" all the way around to the point where it is back at its original
{         starting place all jobs will have completed execution.  Given the current
{         form of the system supplied name, it is estimated that we can't submit
{         enough jobs or output files in a year's time frame to cause the system
{         supplied name to "wrap".
{
{         A value of the last system supplied name (SSN) assigned by NOS/VE is kept
{         in an area in the Recovery Deadstart File (RDF).  Every X times that a
{         SSN is generated, the value in the RDF is updated; the update occurs as
{         part of the System Job's Job-Monitor Loop.  At each deadstart of NOS/VE,
{         the last SSN assigned by NOS/VE is retrieved from the RDF and X additional
{         values are generated.  This new value is then saved in the RDF.  Then net
{         result is that some values in the system supplied name will not be assigned,
{         but no duplicates will be assigned.

?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmt$system_supplied_name
*copyc jmt$system_supplied_name_mask
*copyc oss$mainframe_pageable
*copyc ost$deadstart_phase
*copyc ost$signature_lock
?? POP ??
*copyc dsp$determine_if_entry_in_rdf
*copyc dsp$get_data_from_rdf
*copyc dsp$store_data_in_rdf
*copyc osp$initialize_sig_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$clear_mainframe_sig_lock
*copyc tmp$ready_system_task1
*copyc jmv$ssn_previous_sequence
*copyc jmv$system_supplied_name
?? OLDTITLE ??
?? NEWTITLE := 'Declarations Declared in this Module', EJECT ??

{ The value of update_ssn_frequency is somewhat arbitrary.  It should be large
{ enough that we don't have to constantly update the last assigned System
{ Supplied Name.

  CONST
    update_ssn_frequency = 1000;

{ Jmv$ssn_previous_sequence and jmv$system_supplied_name are defined in jmm$job_scheduler_monitor_mode
{ so that the short form of the system supplied name may be used with the system core debugger.

  VAR
    jmv$number_of_ssns_since_update: [XDCL, STATIC, oss$mainframe_pageable] integer := 0,
    jmv$update_last_used_ssn: [XDCL, STATIC, oss$mainframe_pageable] boolean := FALSE,
    jmv$system_supplied_name_lock: [XDCL, STATIC, oss$mainframe_pageable] ost$signature_lock,
    jmv$system_job_ssn: [XDCL, #GATE, STATIC, oss$mainframe_pageable] jmt$system_supplied_name;

?? OLDTITLE ??
?? NEWTITLE := 'decrement_sequence', EJECT ??
{
{    The purpose of this request is to accept the alphabetical sequence portion of
{  the system supplied name and to decrement it to its predicessor value.  That is,
{  determine the value which, when incremented, will produce the supplied value.
{

  PROCEDURE [INLINE] decrement_sequence
    (VAR sequence: jmt$ssn_sequence_number);

    VAR
      index: 1 .. jmc$ssn_sequence_number_size;

    FOR index := jmc$ssn_sequence_number_size DOWNTO 1 DO
      IF sequence (index) = 'A' THEN
        sequence (index) := 'Z';
      ELSE
        sequence (index) := PRED (sequence (index));
        RETURN;
      IFEND;
    FOREND;
  PROCEND decrement_sequence;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$initialize_ssn', EJECT ??
*copyc jmh$initialize_ssn

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

    VAR
      entry_in_rdf_exists: boolean,
      ignore_system_supplied_name: jmt$system_supplied_name,
      frequency_count: 1 .. update_ssn_frequency,
      last_used_ssn_seq_p: ^SEQ ( * ),
      last_used_ssn: jmt$system_supplied_name,
      system_job_ssn_mask: jmt$system_supplied_name_mask;

    status.normal := TRUE;
    IF deadstart_phase = osc$installation_deadstart THEN
      jmv$system_supplied_name.system_supplied_name := jmv$system_job_ssn;

{ Update the value in the RDF area - the area exists but is empty

      last_used_ssn := jmv$system_supplied_name.system_supplied_name;
      dsp$store_data_in_rdf (dsc$rdf_system_supplied_name, dsc$rdf_production, #SEQ (last_used_ssn));

    ELSE { osc$normal_deadstart, i.e. continuation
      dsp$determine_if_entry_in_rdf (dsc$rdf_system_supplied_name, dsc$rdf_production, entry_in_rdf_exists);

{ If the entry exists, simply read it - otherwise, this is an "upgrade" deadstart

      IF entry_in_rdf_exists THEN

{ Retrieve the value from the RDF area for the last ssn assigned.
{ NOTE: this request updates the variable last_used_ssn

        last_used_ssn_seq_p := #SEQ (last_used_ssn);
        RESET last_used_ssn_seq_p;
        dsp$get_data_from_rdf (dsc$rdf_system_supplied_name, dsc$rdf_production, last_used_ssn_seq_p);
        jmv$system_supplied_name.system_supplied_name := last_used_ssn;
        system_job_ssn_mask.system_supplied_name := jmv$system_job_ssn;
        jmv$system_supplied_name.model := system_job_ssn_mask.model;
        jmv$system_supplied_name.serial_number := system_job_ssn_mask.serial_number;
      ELSE

{ Treat an upgrade deadstart the same as an installation deadstart

        jmv$system_supplied_name.system_supplied_name := jmv$system_job_ssn;

{ Update the value in the RDF area

        last_used_ssn := jmv$system_supplied_name.system_supplied_name;
        dsp$store_data_in_rdf (dsc$rdf_system_supplied_name, dsc$rdf_production, #SEQ (last_used_ssn));
      IFEND;
    IFEND;

    jmv$ssn_previous_sequence := jmv$system_supplied_name.sequence;
    decrement_sequence (jmv$ssn_previous_sequence);
    osp$initialize_sig_lock (jmv$system_supplied_name_lock);

    IF deadstart_phase <> osc$installation_deadstart THEN

{ Generate "frequency" count occurences of this - as soon as run-virtual-system gets called
{ it will "update" the last name assigned - if, for some reason, deadstart should fail before
{ then, that's okay since no multi-mainframe jobs will be submitted or processed until we make
{ it to osp$run_virtual_system.

      FOR frequency_count := 1 TO update_ssn_frequency DO
        qfp$assign_system_supplied_name (ignore_system_supplied_name);
      FOREND;
      jmv$update_last_used_ssn := TRUE;
    IFEND;
  PROCEND jmp$initialize_ssn;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$update_ssn_sequence', EJECT ??
*copyc jmh$update_ssn_sequence

  PROCEDURE [XDCL, #GATE] jmp$update_ssn_sequence
    (    system_supplied_name: jmt$system_supplied_name);

    VAR
      system_supplied_name_mask: jmt$system_supplied_name_mask;

    osp$set_mainframe_sig_lock (jmv$system_supplied_name_lock);
    system_supplied_name_mask.system_supplied_name := system_supplied_name;
    jmv$system_supplied_name.sequence := system_supplied_name_mask.sequence;
    jmv$system_supplied_name.counter := system_supplied_name_mask.counter;
    jmv$ssn_previous_sequence := jmv$system_supplied_name.sequence;
    decrement_sequence (jmv$ssn_previous_sequence);
    jmv$update_last_used_ssn := TRUE;
    jmv$number_of_ssns_since_update := 0;
    osp$clear_mainframe_sig_lock (jmv$system_supplied_name_lock);
  PROCEND jmp$update_ssn_sequence;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] qfp$assign_system_supplied_name', EJECT ??
*copyc qfh$assign_system_supplied_name

  PROCEDURE [XDCL, #GATE] qfp$assign_system_supplied_name
    (VAR system_supplied_name: jmt$system_supplied_name);

?? NEWTITLE := 'increment_counter' ??

    PROCEDURE [INLINE] increment_counter
      (VAR counter: jmt$ssn_counter;
       VAR rollover: boolean);

      VAR
        index: 1 .. jmc$ssn_counter_size;

      rollover := FALSE;
      FOR index := jmc$ssn_counter_size DOWNTO 1 DO
        IF counter (index) = '9' THEN
          counter (index) := '0';
        ELSE
          counter (index) := SUCC (counter (index));
          RETURN;
        IFEND;
      FOREND;

      rollover := TRUE;
    PROCEND increment_counter;
?? OLDTITLE ??
?? NEWTITLE := 'increment_sequence', EJECT ??

    PROCEDURE [INLINE] increment_sequence
      (VAR sequence: jmt$ssn_sequence_number;
       VAR rollover: boolean);

      VAR
        index: 1 .. jmc$ssn_sequence_number_size;

      rollover := FALSE;
      jmv$ssn_previous_sequence := sequence;
      FOR index := jmc$ssn_sequence_number_size DOWNTO 1 DO
        IF sequence (index) = 'Z' THEN
          sequence (index) := 'A';
        ELSE
          sequence (index) := SUCC (sequence (index));
          RETURN;
        IFEND;
      FOREND;

      rollover := TRUE;
    PROCEND increment_sequence;
?? OLDTITLE ??
?? EJECT ??

    VAR
      ignore_status: ost$status,
      rollover: boolean,
      ssn: jmt$system_supplied_name_mask;

    osp$set_mainframe_sig_lock (jmv$system_supplied_name_lock);
    ssn := jmv$system_supplied_name;
    increment_counter (ssn.counter, rollover);

{ If the counter rolls, must advance the sequence

    IF rollover THEN
      increment_sequence (ssn.sequence, rollover);

      { If the sequence rolls, must start over - note the recursive call
      { before procedure exit - we can't reassign the system job's ssn.

      IF rollover THEN
        ssn.system_supplied_name := jmv$system_job_ssn;
      IFEND;
    IFEND;

    jmv$system_supplied_name := ssn;

{ update the frequency count - if we have exceeded the limit, notify the system job's
{ job monitor task.

    jmv$number_of_ssns_since_update := jmv$number_of_ssns_since_update + 1;
    IF jmv$number_of_ssns_since_update >= update_ssn_frequency THEN
      jmv$update_last_used_ssn := TRUE;
      jmv$number_of_ssns_since_update := 0;
      tmp$ready_system_task (tmc$stid_job_monitor, ignore_status);
    IFEND;
    osp$clear_mainframe_sig_lock (jmv$system_supplied_name_lock);

    IF rollover THEN
      qfp$assign_system_supplied_name (ssn.system_supplied_name);
    IFEND;
    system_supplied_name := ssn.system_supplied_name;
  PROCEND qfp$assign_system_supplied_name;
?? OLDTITLE ??
?? NEWTITLE := '[XDLC, #GATE] qfp$update_last_used_ssn', EJECT ??
*copyc qfh$update_last_used_ssn

  PROCEDURE [XDCL, #GATE] qfp$update_last_used_ssn;

    VAR
      last_used_ssn: jmt$system_supplied_name;

    IF jmv$update_last_used_ssn THEN
      jmv$update_last_used_ssn := FALSE;

      { Update the value in the RDF sequence - this request should not be made too often.
      { It is currently done every update_ssn_frequency times.

      last_used_ssn := jmv$system_supplied_name.system_supplied_name;
      dsp$store_data_in_rdf (dsc$rdf_system_supplied_name, dsc$rdf_production, #SEQ (last_used_ssn));
    IFEND;
  PROCEND qfp$update_last_used_ssn;
?? OLDTITLE ??
MODEND jmm$manage_system_supplied_name;
