?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Statistics : Emit Periodic Statistics' ??
MODULE osm$emit_os_statistics_r1;

{ PURPOSE:
{   This module manages the ring 1 data structures that control the emission
{   of the periodic statistics.
{ DESIGN:
{   This module contains interfaces to read and write the emission sets used
{   by the manage_periodic_statistics utility.  The procedures in this module
{   are called by procedures in the module osm$emit_os_statistics.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nac$statistics_codes
*copyc lge$condition_codes
*copyc osc$processor_defined_registers
*copyc osc$statistics
*copyc ofe$error_codes
*copyc oss$mainframe_pageable
*copyc ost$data_id
*copyc ost$emission_sets
*copyc ost$execution_control_block
*copyc ost$heap
*copyc ost$signature_lock_status
*copyc syt$monitor_status
?? POP ??
*copyc osp$clear_mainframe_sig_lock
*copyc osp$initialize_sig_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_condition
*copyc osp$test_sig_lock
*copyc osv$mainframe_pageable_heap
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    osv$time_to_emit_statistics: [XDCL, #GATE, oss$mainframe_pageable] integer := osc$max_emit_time,
    osv$emission_sets_p: [oss$mainframe_pageable] ^array [ost$emission_set_names]
          of ost$emission_set := NIL,
    osv$emission_sets_lock: [oss$mainframe_pageable] ost$signature_lock := [0],
    osv$manps_user_lock: [oss$mainframe_pageable] ost$signature_lock := [0];

?? TITLE := 'initialize_emission_sets', EJECT ??

{ PURPOSE:
{   The purpose of this request is to allocate and initialize the data
{   structures for the os statistics emission_sets. Space is allocated in the
{   oss$mainframe_pageable_heap for the emission sets and the timing variables
{   in each of the emission sets are initialized.

  PROCEDURE initialize_emission_sets;

    VAR
      emission_set: ost$emission_set_names;

    ALLOCATE osv$emission_sets_p IN osv$mainframe_pageable_heap^;

    FOR emission_set := LOWERVALUE (ost$emission_set_names) TO UPPERVALUE (ost$emission_set_names) DO
      osv$emission_sets_p^ [emission_set].enabled := FALSE;
      osv$emission_sets_p^ [emission_set].period.hour := 23;
      osv$emission_sets_p^ [emission_set].period.minute := 59;
      osv$emission_sets_p^ [emission_set].period.second := 59;
      osv$emission_sets_p^ [emission_set].microsecond_period := (23 * 3600 + 59 * 60 + 59) * 100000;
      osv$emission_sets_p^ [emission_set].next_emit_time := osc$max_emit_time;
      osv$emission_sets_p^ [emission_set].stat_count := 0;
    FOREND;

  PROCEND initialize_emission_sets;
?? TITLE := '[XDCL, #GATE] osp$emit_os_statistics_r1', EJECT ??

{ PURPOSE:
{   The purpose of this request is to supply the caller with a copy of the
{   emission sets.  The emission sets are then checked to determine the next
{   emit time for each set and the next emit time for the sets.
{
{       OSP$EMIT_OS_STATISTICS_R1 (CURRENT_TIME, EMISSION_SETS_COPY)
{
{ CURRENT_TIME: (input)  This parameter specifies the current time.  It is used
{       to compute the next emit times.  This value is passed from the ring 3
{       code to insure that the two procedures use exactly the same value for
{       the current time.
{
{ EMISSION_SETS_COPY: (output)  This parameter specifies a copy of the emission
{       sets.

  PROCEDURE [XDCL, #GATE] osp$emit_os_statistics_r1
    (    current_time: integer;
     VAR emission_sets_copy: array [ost$emission_set_names] of ost$emission_set);

    VAR
      emission_set: ost$emission_set_names,
      new_emit_time: integer;

    osp$set_mainframe_sig_lock (osv$emission_sets_lock);

{ Copy the emission sets.

    emission_sets_copy := osv$emission_sets_p^;

{ Set the next time to emit statistics to max for now.

    osv$time_to_emit_statistics := osc$max_emit_time;

{ Update the next time to emit in each of the enabled sets.

    FOR emission_set := LOWERVALUE (ost$emission_set_names) TO UPPERVALUE (ost$emission_set_names) DO
      IF (osv$emission_sets_p^ [emission_set].enabled) THEN
        IF (osv$emission_sets_p^ [emission_set].next_emit_time <= current_time) THEN
          IF emission_set = osc$immediate_emission_set THEN
            osv$emission_sets_p^ [emission_set].enabled := FALSE;
            osv$emission_sets_p^ [emission_set].next_emit_time := osc$max_emit_time;
          ELSE
            new_emit_time := osv$emission_sets_p^ [emission_set].
                  next_emit_time + osv$emission_sets_p^ [emission_set].microsecond_period;
            IF new_emit_time > osc$max_emit_time THEN
              osv$emission_sets_p^ [emission_set].next_emit_time := osc$max_emit_time;
            ELSE
              osv$emission_sets_p^ [emission_set].next_emit_time := new_emit_time;
            IFEND;
          IFEND;
        IFEND;

{ Compute the next time to emit the periodic statistics.

        IF (osv$emission_sets_p^ [emission_set].next_emit_time < osv$time_to_emit_statistics) THEN
          osv$time_to_emit_statistics := osv$emission_sets_p^ [emission_set].next_emit_time;
        IFEND;
      IFEND;
    FOREND;

    osp$clear_mainframe_sig_lock (osv$emission_sets_lock);

  PROCEND osp$emit_os_statistics_r1;
?? TITLE := '[XDCL, #GATE] osp$read_emission_sets_r1', EJECT ??

{ PURPOSE:
{   The purpose of this request is to return a copy of the emission sets data.
{
{       OSP$READ_EMISSION_SETS_R1 (EMISSION_SETS_COPY, STATUS)
{
{ EMISSION_SETS_COPY: (output)  This parameter specifies a copy of the emission
{       sets.
{
{ STATUS: (output)  This parameter specifies the request status.

  PROCEDURE [XDCL, #GATE] osp$read_emission_sets_r1
    (VAR emission_sets_copy: array [ost$emission_set_names] of ost$emission_set;
     VAR status: ost$status);

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (osv$emission_sets_lock);
    IF osv$emission_sets_p = NIL THEN
      initialize_emission_sets;
    IFEND;
    emission_sets_copy := osv$emission_sets_p^;
    osp$clear_mainframe_sig_lock (osv$emission_sets_lock);

  PROCEND osp$read_emission_sets_r1;
?? TITLE := '[XDCL, #GATE] osp$release_manps_lock_r1', EJECT ??

{ PURPOSE:
{   The purpose of this request is to release the osv$manps_user_lock if
{   it is held by the calling task.
{
{       OSP$RELEASE_MANPS_LOCK_R1
{

  PROCEDURE [XDCL, #GATE] osp$release_manps_lock_r1;

    VAR
      lock_status: ost$signature_lock_status;

    osp$test_sig_lock (osv$manps_user_lock, lock_status);
    IF (lock_status = osc$sls_locked_by_current_task) THEN
      osp$clear_mainframe_sig_lock (osv$manps_user_lock);
    IFEND;

  PROCEND osp$release_manps_lock_r1;
?? TITLE := '[XDCL, #GATE] osp$reserve_manps_lock_r1', EJECT ??

{ PURPOSE:
{   The purpose of this request is to reserve the osv$manps_user_lock for
{   the calling task.
{
{       OSP$RESERVE_MANPS_LOCK_R1 (STATUS)
{
{ STATUS: (output)  This parameter specifies the request status.

  PROCEDURE [XDCL, #GATE] osp$reserve_manps_lock_r1
    (VAR status: ost$status);

    VAR
      lock_set_for_this_task: boolean,
      lock_status: ost$signature_lock_status;

    status.normal := TRUE;
    lock_set_for_this_task := FALSE;

    REPEAT
      osp$test_sig_lock (osv$manps_user_lock, lock_status);
      IF (lock_status = osc$sls_locked_by_current_task) THEN
        lock_set_for_this_task := TRUE;
      ELSEIF (lock_status = osc$sls_not_locked) THEN
        osp$set_mainframe_sig_lock (osv$manps_user_lock);
      ELSE                {(lock_status = osc$sls_locked_by_another_task)
        osp$set_status_condition (lge$write_privilege_reserved, status);
        RETURN;
      IFEND;
    UNTIL lock_set_for_this_task;

  PROCEND osp$reserve_manps_lock_r1;
?? TITLE := '[XDCL, #GATE] osp$write_emission_sets_r1', EJECT ??

{ PURPOSE:
{   This procedure updates the emission sets and the emission set emit times.
{   The new value for osv$time_to_emit_statistics indicates the next time
{   when osp$emit_os_statistics should be called.
{
{       OSP$WRITE_EMISSION_SETS_R1 (EMISSION_SETS, STATUS)
{
{ EMISSION_SETS: (input)  This parameter specifies the new value for the
{       emission sets.
{
{ STATUS: (output)  This parameter specifies the request status.

  PROCEDURE [XDCL, #GATE] osp$write_emission_sets_r1
    (VAR emission_sets: array [ost$emission_set_names] of ost$emission_set;
     VAR status: ost$status);

    VAR
      current_time: integer,
      local_time_to_emit_statistics: integer,
      lock_status: ost$signature_lock_status,
      new_emit_time: integer,
      period_has_changed: boolean,
      reset_emission_times: boolean,
      state_has_changed: boolean,
      test_emission_set: ost$emission_set_names;

    status.normal := TRUE;

    osp$test_sig_lock (osv$manps_user_lock, lock_status);
    IF NOT (lock_status = osc$sls_locked_by_current_task) THEN
      osp$set_status_condition (lge$write_priv_not_reserved, status);
      RETURN;
    IFEND;
    osp$set_mainframe_sig_lock (osv$emission_sets_lock);
    IF osv$emission_sets_p = NIL THEN
      initialize_emission_sets;
    IFEND;

    osv$time_to_emit_statistics := osc$max_emit_time;
    local_time_to_emit_statistics := osc$max_emit_time;
    current_time := #FREE_RUNNING_CLOCK (0);

{  The following FOR loop resets the next_time_to_emit for each enabled emission set
{  and keeps track of the minimum value to use as the next emission time.
{  If one of the two conditions occur the next_time_to_emit will be recalculated:
{  1)  the state has been changed from disabled to enabled.
{  2)  the period has been changed AND the next_time_to_emit is greater than the
{      current time
{  Otherwise one of two conditions could occur and the current value for
{  next_time_to_emit will be used.
{  1)  the period has not changed.
{  2)  the period has changed but the current value for next_time_to_emit is
{      less than or equal to the current time.  This means that an emission was about to occur.

    FOR test_emission_set := LOWERVALUE (ost$periodic_emission_sets)
          TO UPPERVALUE (ost$periodic_emission_sets) DO
      IF (emission_sets [test_emission_set].enabled) THEN
        period_has_changed := osv$emission_sets_p^ [test_emission_set].microsecond_period <>
              emission_sets [test_emission_set].microsecond_period;
        state_has_changed := osv$emission_sets_p^ [test_emission_set].enabled <>
              emission_sets [test_emission_set].enabled;
        IF state_has_changed OR
          (period_has_changed AND (osv$emission_sets_p^ [test_emission_set].next_emit_time > current_time))
          THEN
          new_emit_time := current_time + emission_sets [test_emission_set].microsecond_period;
          IF new_emit_time > osc$max_emit_time THEN
            emission_sets [test_emission_set].next_emit_time := osc$max_emit_time;
          ELSE
            emission_sets [test_emission_set].next_emit_time := new_emit_time;
          IFEND;
        ELSE
          emission_sets [test_emission_set].next_emit_time :=
                osv$emission_sets_p^ [test_emission_set].next_emit_time;
        IFEND;
        IF emission_sets [test_emission_set].next_emit_time < local_time_to_emit_statistics THEN
          local_time_to_emit_statistics := emission_sets [test_emission_set].next_emit_time;
        IFEND;
      IFEND;
    FOREND;

    IF emission_sets [osc$immediate_emission_set].enabled THEN
      emission_sets [osc$immediate_emission_set].next_emit_time := current_time;
      local_time_to_emit_statistics := current_time;
    IFEND;

    osv$emission_sets_p^ := emission_sets;
    osv$time_to_emit_statistics := local_time_to_emit_statistics;
    osp$clear_mainframe_sig_lock (osv$emission_sets_lock);

  PROCEND osp$write_emission_sets_r1;
?? OLDTITLE  ??
?? OLDTITLE  ??
MODEND osm$emit_os_statistics_r1;
