MODULE pfm$catalog_alarm_manager;
?? RIGHT := 110 ??

{ PURPUSE:
{   This module manages keeping track of what catalogs have been
{   marked as alarmed on a system wide basis.
{   A catalog may be marked as alarmed by another job,
{   and these routines provide a mechanism for determining current status
{   of a catalog.  These routines also maintain statistics on the
{   number and duration of catalog alarms.
{   This module does NOT decide what an alarmed
{   catalog is NOR does it decide when to no longer include a catalog
{   as alarmed.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc osd$integer_limits
*copyc pfe$error_condition_codes
*copyc pfe$internal_error_conditions
*copyc pft$catalog_alarm_table_index
*copyc pft$p_catalog_alarm_table
*copyc pft$p_table_info
*copyc pft$record_id
*copyc pft$table_name
?? POP ??
*copyc osp$clear_mainframe_sig_lock
*copyc osp$clear_signature_lock
*copyc osp$initialize_signature_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$format_compact_time
*copyc pmp$get_compact_date_time
*copyc osv$mainframe_pageable_heap
*copyc tmv$null_global_task_id
*copyc tmv$ptl_p
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module', EJECT ??

  VAR
    pfv$catalog_alarm_table_lock: [XDCL] ost$signature_lock := [0],

    pfv$number_of_alarm_sets: [XDCL] ost$non_negative_integers := 0,
    { This variable is a counter of the number of alarms set
    { since the last recovery.

    pfv$p_catalog_alarm_table: [XDCL] ^pft$catalog_alarm_table := NIL,

    highest_valid_entry: 0 .. pfc$highest_alarm_table_index := 0;

?? OLDTITLE ??
?? NEWTITLE := ' pfp$check_catalog_alarm', EJECT ??

  PROCEDURE [XDCL, #GATE] pfp$check_catalog_alarm
    (    global_file_name: ost$binary_unique_name;
     VAR catalog_alarm_set: boolean;
     VAR destroy_on_last_detach: boolean);

    VAR
      catalog_alarm_table_index: pft$catalog_alarm_table_index;

    osp$set_mainframe_sig_lock (pfv$catalog_alarm_table_lock);

    search_catalog_alarm_table (global_file_name, catalog_alarm_set, catalog_alarm_table_index);
    destroy_on_last_detach := FALSE;
    IF catalog_alarm_set THEN
      pfv$p_catalog_alarm_table^ [catalog_alarm_table_index].
            search_count := pfv$p_catalog_alarm_table^ [catalog_alarm_table_index].search_count + 1;
      destroy_on_last_detach := pfv$p_catalog_alarm_table^ [catalog_alarm_table_index].destroy_on_last_detach;
    IFEND;

    osp$clear_mainframe_sig_lock (pfv$catalog_alarm_table_lock);

  PROCEND pfp$check_catalog_alarm;
?? OLDTITLE ??
?? NEWTITLE := ' pfp$clear_catalog_alarm', EJECT ??

  PROCEDURE [XDCL, #GATE] pfp$clear_catalog_alarm
    (    global_file_name: ost$binary_unique_name);

    VAR
      catalog_alarm_table_index: pft$catalog_alarm_table_index,
      catalog_found: boolean;

    osp$set_mainframe_sig_lock (pfv$catalog_alarm_table_lock);

    search_catalog_alarm_table (global_file_name, catalog_found, catalog_alarm_table_index);
    IF catalog_found THEN
      pfv$p_catalog_alarm_table^ [catalog_alarm_table_index].external_catalog_name := 'free';
      pfv$p_catalog_alarm_table^ [catalog_alarm_table_index].entry_type := pfc$catalog_alarm_entry_free;
      IF catalog_alarm_table_index = highest_valid_entry THEN

      /set_new_highest_valid_entry/
        FOR highest_valid_entry := (catalog_alarm_table_index - 1) DOWNTO 1 DO
          IF (pfv$p_catalog_alarm_table^ [highest_valid_entry].entry_type =
                pfc$catalog_alarm_entry_valid) THEN
            EXIT /set_new_highest_valid_entry/; {----->
          IFEND;
        FOREND /set_new_highest_valid_entry/;

        IF (highest_valid_entry = 1) AND (pfv$p_catalog_alarm_table^ [highest_valid_entry].entry_type =
              pfc$catalog_alarm_entry_free) THEN
          highest_valid_entry := 0;
        IFEND;
      IFEND;
    IFEND;

    osp$clear_mainframe_sig_lock (pfv$catalog_alarm_table_lock);

  PROCEND pfp$clear_catalog_alarm;
?? OLDTITLE ??
?? NEWTITLE := ' pfp$r1_get_catalog_alarm_table', EJECT ??

  PROCEDURE [XDCL, #GATE] pfp$r1_get_catalog_alarm_table
    (VAR p_table_info: pft$p_table_info;
     VAR status: ost$status);

    VAR
      p_number_of_alarms: ^integer,
      p_record_id: ^pft$record_id,
      p_table: pft$p_catalog_alarm_table,
      p_table_lock: ^ost$signature_lock,
      p_table_name: ^pft$table_name,
      p_table_size: ^integer;

    status.normal := TRUE;
    NEXT p_table_name IN p_table_info;
    IF p_table_name = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table name', status);
      RETURN; {----->
    IFEND;
    p_table_name^ := 'CATALOG_ALARM_TABLE';

    NEXT p_record_id IN p_table_info;
    IF p_record_id = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
      RETURN; {----->
    IFEND;
    p_record_id^ := 'TABLLOCK';

    NEXT p_table_lock IN p_table_info;
    IF p_table_lock = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table lock', status);
      RETURN; {----->
    IFEND;
    p_table_lock^ := pfv$catalog_alarm_table_lock;

    NEXT p_record_id IN p_table_info;
    IF p_record_id = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
      RETURN; {----->
    IFEND;
    p_record_id^ := 'HIGHENTR';

    NEXT p_number_of_alarms IN p_table_info;
    IF p_number_of_alarms = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'high entry', status);
      RETURN; {----->
    IFEND;
    p_number_of_alarms^ := highest_valid_entry;

    NEXT p_record_id IN p_table_info;
    IF p_record_id = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
      RETURN; {----->
    IFEND;
    p_record_id^ := 'ALARMSET';

    NEXT p_number_of_alarms IN p_table_info;
    IF p_number_of_alarms = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'alarms set', status);
      RETURN; {----->
    IFEND;
    p_number_of_alarms^ := pfv$number_of_alarm_sets;

    NEXT p_record_id IN p_table_info;
    IF p_record_id = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
      RETURN; {----->
    IFEND;
    p_record_id^ := 'TABLSIZE';

    NEXT p_table_size IN p_table_info;
    IF p_table_size = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table size', status);
      RETURN; {----->
    IFEND;
    IF pfv$p_catalog_alarm_table = NIL THEN
      p_table_size^ := 0;
    ELSE
      p_table_size^ := UPPERBOUND (pfv$p_catalog_alarm_table^);

      NEXT p_record_id IN p_table_info;
      IF p_record_id = NIL THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
        RETURN; {----->
      IFEND;
      p_record_id^ := 'ALARMTBL';

      NEXT p_table: [1 .. p_table_size^] IN p_table_info;
      IF p_table = NIL THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table', status);
        RETURN; {----->
      IFEND;
      p_table^ := pfv$p_catalog_alarm_table^;
    IFEND;

  PROCEND pfp$r1_get_catalog_alarm_table;
?? OLDTITLE ??
?? NEWTITLE := ' pfp$set_catalog_alarm', EJECT ??

  PROCEDURE [XDCL, #GATE] pfp$set_catalog_alarm
    (    global_file_name: ost$binary_unique_name;
         internal_catalog_name: pft$internal_catalog_name;
         external_catalog_name: pft$name;
         destroy_on_last_detach: boolean);

    VAR
      alarm_table_index: pft$catalog_alarm_table_index,
      catalog_already_alarmed: boolean,
      entry_p: ^pft$catalog_alarm_entry,
      ignored_status: ost$status,
      time: ost$time;

    osp$set_mainframe_sig_lock (pfv$catalog_alarm_table_lock);

    pfv$number_of_alarm_sets := pfv$number_of_alarm_sets + 1;

    search_catalog_alarm_table (global_file_name, catalog_already_alarmed, alarm_table_index);
    IF catalog_already_alarmed THEN
      entry_p := ^pfv$p_catalog_alarm_table^ [alarm_table_index];
      entry_p^.alarm_count := entry_p^.alarm_count + 1;
      entry_p^.destroy_on_last_detach := entry_p^.destroy_on_last_detach OR destroy_on_last_detach;
    ELSE
      get_free_alarm_table_entry (alarm_table_index);
      entry_p := ^pfv$p_catalog_alarm_table^ [alarm_table_index];
      entry_p^.entry_type := pfc$catalog_alarm_entry_valid;
      entry_p^.internal_catalog_name := internal_catalog_name;
      entry_p^.global_file_name := global_file_name;
      entry_p^.destroy_on_last_detach := destroy_on_last_detach;

      pmp$get_compact_date_time (entry_p^.time_of_alarm_setting, ignored_status);
      IF ignored_status.normal THEN
        pmp$format_compact_time (entry_p^.time_of_alarm_setting, osc$hms_time, time, ignored_status);
        entry_p^.displayable_time := time.hms;
      IFEND;
      entry_p^.external_catalog_name := external_catalog_name;
      entry_p^.alarm_count := 1;
      entry_p^.search_count := 0;

      IF alarm_table_index > highest_valid_entry THEN
        highest_valid_entry := alarm_table_index;
      IFEND;
    IFEND;

    osp$clear_mainframe_sig_lock (pfv$catalog_alarm_table_lock);

  PROCEND pfp$set_catalog_alarm;
?? OLDTITLE ??
?? NEWTITLE := ' get_free_alarm_table_entry', EJECT ??

  PROCEDURE get_free_alarm_table_entry
    (VAR catalog_alarm_table_index: pft$catalog_alarm_table_index);

{  PURPOSE:
{    The purpose of this procedure is
{    to get a free entry in the catalog alarm table; this includes initially
{    creating the table, expanding the table if necessary, and
{    searching for an free entry.
{    The only possible error condition is if the mainframe pageable heap
{    is full, and the table can not be expanded.


    CONST
      pfc$expand_alarm_table_amount = 8,
      pfc$initial_alarm_table_size = 8;

    VAR
      p_new_catalog_alarm_table: pft$p_catalog_alarm_table,
      p_old_catalog_alarm_table: pft$p_catalog_alarm_table,
      space_found: boolean,
      status: ost$status;

?? OLDTITLE ??
?? NEWTITLE := ' find_free_alarm_table_entry', EJECT ??

{  PURPOSE:
{    Searches the catalog alarm table looking for a free table entry. The free index number is returned.

    PROCEDURE [INLINE] find_free_alarm_table_entry
      (    p_catalog_alarm_table: pft$p_catalog_alarm_table;
       VAR catalog_alarm_table_index: pft$catalog_alarm_table_index;
       VAR free_found: boolean);

      free_found := FALSE;
      IF p_catalog_alarm_table <> NIL THEN
        FOR catalog_alarm_table_index := 1 TO UPPERBOUND (p_catalog_alarm_table^) DO
          IF p_catalog_alarm_table^ [catalog_alarm_table_index].entry_type = pfc$catalog_alarm_entry_free THEN
            free_found := TRUE;
            RETURN; {----->
          IFEND;
        FOREND;
      IFEND;

    PROCEND find_free_alarm_table_entry;
?? OLDTITLE ??
?? NEWTITLE := ' initialize_alarm_table', EJECT ??

{  PURPOSE:
{    This procedure initializes a catalog alarm  table to indicate that all entries are free.

    PROCEDURE initialize_alarm_table
      (    p_catalog_alarm_table: pft$p_catalog_alarm_table);

      VAR
        catalog_alarm_table_index: pft$catalog_alarm_table_index;

      IF p_catalog_alarm_table <> NIL THEN
        FOR catalog_alarm_table_index := LOWERBOUND (p_catalog_alarm_table^)
              TO UPPERBOUND (p_catalog_alarm_table^) DO
          p_catalog_alarm_table^ [catalog_alarm_table_index].external_catalog_name := 'free';
          p_catalog_alarm_table^ [catalog_alarm_table_index].entry_type := pfc$catalog_alarm_entry_free;
        FOREND;
      IFEND;

    PROCEND initialize_alarm_table;
?? OLDTITLE ??
?? NEWTITLE := ' transfer_old_to_new_alarm_table', EJECT ??

    PROCEDURE transfer_old_to_new_alarm_table
      (    p_old_catalog_alarm_table: pft$p_catalog_alarm_table;
           p_new_catalog_alarm_table: pft$p_catalog_alarm_table);

{  PURPOSE:
{    This procedure transfers an old catalog alarm table to a new one.  The
{    new table must be equal to or larger than the old table.  If the new table
{    is larger, additional entries are initialized to indicate they are free.

      VAR
        catalog_alarm_table_index: pft$catalog_alarm_table_index;

    /copy_existing_entries/
      FOR catalog_alarm_table_index := LOWERBOUND (p_old_catalog_alarm_table^)
            TO UPPERBOUND (p_old_catalog_alarm_table^) DO
        p_new_catalog_alarm_table^ [catalog_alarm_table_index] :=
              p_old_catalog_alarm_table^ [catalog_alarm_table_index];
      FOREND /copy_existing_entries/;

    /initialize_new_entries/
      FOR catalog_alarm_table_index := (UPPERBOUND (p_old_catalog_alarm_table^) + 1)
            TO UPPERBOUND (p_new_catalog_alarm_table^) DO
        p_new_catalog_alarm_table^ [catalog_alarm_table_index].external_catalog_name := 'free';
        p_new_catalog_alarm_table^ [catalog_alarm_table_index].entry_type := pfc$catalog_alarm_entry_free;
      FOREND /initialize_new_entries/;

    PROCEND transfer_old_to_new_alarm_table;
?? OLDTITLE ??
?? EJECT ??

    space_found := FALSE;
    IF pfv$p_catalog_alarm_table = NIL THEN
      ALLOCATE pfv$p_catalog_alarm_table: [1 .. pfc$initial_alarm_table_size] IN osv$mainframe_pageable_heap^;
      IF pfv$p_catalog_alarm_table = NIL THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$pf_system_error,
              'full mainframe pageable', status);
        osp$system_error (' PF - FULL MAINFRAME HEAP', ^status);
      ELSE
        initialize_alarm_table (pfv$p_catalog_alarm_table);
        find_free_alarm_table_entry (pfv$p_catalog_alarm_table, catalog_alarm_table_index, space_found);
      IFEND;
    ELSE
      find_free_alarm_table_entry (pfv$p_catalog_alarm_table, catalog_alarm_table_index, space_found);
      IF NOT space_found THEN {expand the old table }
        ALLOCATE p_new_catalog_alarm_table: [1 .. (UPPERBOUND (pfv$p_catalog_alarm_table^) +
              pfc$expand_alarm_table_amount)] IN osv$mainframe_pageable_heap^;
        IF p_new_catalog_alarm_table = NIL THEN
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$pf_system_error,
                'full mainframe pageable', status);
          osp$system_error (' PF - FULL MAINFRAME HEAP', ^status);
        ELSE
          transfer_old_to_new_alarm_table (pfv$p_catalog_alarm_table, p_new_catalog_alarm_table);
          p_old_catalog_alarm_table := pfv$p_catalog_alarm_table;
          pfv$p_catalog_alarm_table := p_new_catalog_alarm_table;
          FREE p_old_catalog_alarm_table IN osv$mainframe_pageable_heap^;
          find_free_alarm_table_entry (pfv$p_catalog_alarm_table, catalog_alarm_table_index, space_found);
        IFEND;
      IFEND;
    IFEND;

  PROCEND get_free_alarm_table_entry;
?? OLDTITLE ??
?? NEWTITLE := ' search_catalog_alarm_table', EJECT ??

  PROCEDURE [INLINE] search_catalog_alarm_table
    (    global_file_name: ost$binary_unique_name;
     VAR catalog_found: boolean;
     VAR catalog_alarm_table_index: pft$catalog_alarm_table_index);

    VAR
      entry_p: ^pft$catalog_alarm_entry;

    catalog_found := FALSE;
    IF pfv$p_catalog_alarm_table = NIL THEN
      RETURN; {----->
    IFEND;

    FOR catalog_alarm_table_index := 1 TO highest_valid_entry DO
      entry_p := ^pfv$p_catalog_alarm_table^ [catalog_alarm_table_index];
      IF (entry_p^.entry_type = pfc$catalog_alarm_entry_valid) AND
            (entry_p^.global_file_name = global_file_name) THEN
        catalog_found := TRUE;
        RETURN; {----->
      IFEND;
    FOREND;

  PROCEND search_catalog_alarm_table;
?? OLDTITLE ??
MODEND pfm$catalog_alarm_manager;
