?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access : Timer Monitor Task' ??
MODULE nlm$timer_monitor;

{    PURPOSE:
{      The purpose of this module/task is to call all connection layer's connection timer
{      evaluator for active connections and the service access point timer evaluator for
{      all layers on a connection layer path.
{
{      This module shares the knowledge of the connection list with the connection layer
{      connection manager.
{
{    DESIGN:
{      This module is designed to be contained in the OSF$JOB_TEMPLATE_23D library and to execute
{      in the network job environemnt.
{
?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nlt$cl_connection
*copyc nlt$cl_connections
*copyc nlt$timer
*copyc oss$network_paged
*copyc oss$mainframe_wired
*copyc ost$status
*copyc pmt$program_parameters
?? POP ??
*copyc nlp$cl_zero_terminated_connects
*copyc nlp$cl_get_nonexclusive_to_root
*copyc nlp$cl_release_nonexclu_to_root
*copyc nlp$cl_get_connection_access
*copyc nlp$cl_release_exclusive_access
*copyc nlp$cl_release_connection
*copyc nlp$cl_get_conn_timer_evaluator
*copyc nlp$cl_get_sap_timer_evaluator
*copyc nlp$cl_layer_on_path
*copyc nlp$select_timer
*copyc nlp$timer_expired
*copyc osp$free_heap_pages
*copyc pmp$get_executing_task_gtid
*copyc pmp$wait
*copyc jmv$executing_within_system_job
*copyc nav$network_wired_heap
*copyc nlv$bm_buffers_freed
*copyc nlv$cl_connections
*copyc nlv$timer_monitor_task
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$monitor_timers', EJECT ??

  PROCEDURE [XDCL, #GATE] nlp$monitor_timers (parameters: pmt$program_parameters;
    VAR status: ost$status);

    CONST
      free_page_timer_duration = 90 * 1000 * 1000;

    VAR
      free_page_timer: nlt$timer,
      last_time,
      time_to_monitor,
      current_time: integer;

    status.normal := TRUE;
    IF NOT jmv$executing_within_system_job THEN
      RETURN;
    IFEND;
    pmp$get_executing_task_gtid (nlv$timer_monitor_task);
    nlp$select_timer (free_page_timer_duration, 0, free_page_timer);
    last_time := #free_running_clock (0);
    WHILE TRUE DO
      current_time := #free_running_clock (0);
      IF ((current_time - last_time) > (nlc$sap_timer_duration DIV 2)) THEN
        IF ((nlv$cl_connections.list <> NIL) AND (nlv$cl_connections.active > 0)) THEN
          last_time := current_time;
          evaluate_connection_timers (current_time);
          evaluate_sap_timers (current_time);
        ELSE
          IF ((current_time - last_time) > (nlc$sap_timer_duration - 100000)) THEN
            last_time := current_time;
            evaluate_sap_timers (current_time);
          IFEND;
        IFEND;
      ELSE
        IF ((nlv$cl_connections.list <> NIL) AND (nlv$cl_connections.active > 0)) THEN
          release_terminated_connections;
        IFEND;
      IFEND;

      IF nlp$timer_expired (current_time, free_page_timer) THEN
        IF nlv$bm_buffers_freed THEN
          nlv$bm_buffers_freed := FALSE;
          osp$free_heap_pages (nav$network_wired_heap);
        IFEND;
        nlp$select_timer (free_page_timer_duration, 0, free_page_timer);
      IFEND;

      time_to_monitor := (#free_running_clock (0) - last_time);
      IF (time_to_monitor < (nlc$sap_timer_duration - 100000)) THEN
        pmp$wait (((nlc$sap_timer_duration - time_to_monitor) DIV 1000), (nlc$sap_timer_duration DIV
              1000));
      IFEND;
    WHILEND;
  PROCEND nlp$monitor_timers;
?? OLDTITLE ??
?? NEWTITLE := 'evaluate_connection_timers', EJECT ??

  PROCEDURE evaluate_connection_timers (current_time: ost$free_running_clock);

    VAR
      root: nlt$cl_reference_number,
      connection: ^nlt$cl_connection,
      next_connection: ^nlt$cl_connection,
      access_gained: boolean,
      layer: nlt$cl_layer_name,
      timer_evaluator: nlt$cl_evaluat_connection_timer,
      layer_connections: ^nlt$cl_layer_connections,
      inactive_connection_found: boolean,
      inactive_connection_entry,
      last_inactive_connection_entry: nlt$cl_reference_number,
      inactive_connection_roots: ^array [0 .. * ] of nlt$cl_reference_number;

    inactive_connection_found := FALSE;

  /scan_roots/
    FOR root := LOWERBOUND (nlv$cl_connections.list^) TO UPPERBOUND (nlv$cl_connections.list^) DO
      IF nlv$cl_connections.list^ [root].first <> NIL THEN
      nlp$cl_get_nonexclusive_to_root (root);
      connection := nlv$cl_connections.list^ [root].first;

    /scan_stem/
      WHILE (connection <> NIL) DO
        next_connection := connection^.nextt;
        nlp$cl_get_connection_access (connection, access_gained);
        IF access_gained THEN
          FOR layer := UPPERVALUE (nlt$cl_layer_name) DOWNTO connection^.application_layer DO
            IF (nlp$cl_layer_on_path (connection^.application_layer, layer) AND (layer IN connection^.
                  layers_active)) THEN
              nlp$cl_get_conn_timer_evaluator (connection^.application_layer, layer, timer_evaluator);
              IF (timer_evaluator <> NIL) THEN
                timer_evaluator^ (current_time, connection);
              IFEND;
            IFEND;
          FOREND;
          IF (connection^.layers_active = $nlt$cl_layers []) THEN
            IF inactive_connection_found THEN
              IF (root <> inactive_connection_roots^ [last_inactive_connection_entry]) THEN
                last_inactive_connection_entry := last_inactive_connection_entry + 1;
                inactive_connection_roots^ [last_inactive_connection_entry] := root;
              IFEND;
            ELSE
              PUSH inactive_connection_roots: [LOWERBOUND (nlv$cl_connections.list^) .. UPPERBOUND
                    (nlv$cl_connections.list^)];
              last_inactive_connection_entry := LOWERBOUND (nlv$cl_connections.list^);
              inactive_connection_roots^ [last_inactive_connection_entry] := root;
              inactive_connection_found := TRUE;
            IFEND;
          IFEND;
          nlp$cl_release_exclusive_access (connection);
        IFEND;
        connection := next_connection;
      WHILEND /scan_stem/;
      nlp$cl_release_nonexclu_to_root (root);
      IFEND;
    FOREND /scan_roots/;

    IF inactive_connection_found THEN
      nlp$cl_zero_terminated_connects;

    /scan_roots_for_inactive/
      FOR inactive_connection_entry := LOWERBOUND (nlv$cl_connections.list^) TO last_inactive_connection_entry
            DO
        root := inactive_connection_roots^ [inactive_connection_entry];
        nlp$cl_get_nonexclusive_to_root (root);
        connection := nlv$cl_connections.list^ [root].first;

      /scan_stem_for_inactive/
        WHILE (connection <> NIL) DO
          IF (connection^.layers_active = $nlt$cl_layers []) THEN
            next_connection := connection^.nextt;
            nlp$cl_release_nonexclu_to_root (root);
            nlp$cl_release_connection (connection^.identifier);
            nlp$cl_get_nonexclusive_to_root (root);

{ The assumption is that an inactive connection is released from the root only by the timer task.

            connection := next_connection;
          ELSE
            connection := connection^.nextt;
          IFEND;
        WHILEND /scan_stem_for_inactive/;
        nlp$cl_release_nonexclu_to_root (root);
      FOREND /scan_roots_for_inactive/;
    IFEND;
  PROCEND evaluate_connection_timers;
?? OLDTITLE ??
?? NEWTITLE := 'evaluate_sap_timers', EJECT ??

  PROCEDURE evaluate_sap_timers (current_time: ost$free_running_clock);

    VAR
      layer: nlt$cl_layer_name,
      application_layer: nlt$cl_application_layer,
      timer_evaluator: nlt$cl_evaluate_sap_timer;

    FOR application_layer := LOWERVALUE (nlt$cl_application_layer) TO UPPERVALUE (nlt$cl_application_layer) DO
      FOR layer := LOWERVALUE (nlt$cl_layer_name) TO UPPERVALUE (nlt$cl_layer_name) DO
        IF nlp$cl_layer_on_path (application_layer, layer) THEN
          nlp$cl_get_sap_timer_evaluator (application_layer, layer, timer_evaluator);
          IF (timer_evaluator <> NIL) THEN
            timer_evaluator^ (current_time);
          IFEND;
        IFEND;
      FOREND;
    FOREND;
  PROCEND evaluate_sap_timers;
?? OLDTITLE ??
?? NEWTITLE := 'release_terminated_connections', EJECT ??

  PROCEDURE release_terminated_connections;

    VAR
      root: nlt$cl_reference_number,
      connection: ^nlt$cl_connection,
      next_connection: ^nlt$cl_connection;

    nlp$cl_zero_terminated_connects;

  /scan_roots_for_inactive/
    FOR root := LOWERBOUND (nlv$cl_connections.list^) TO UPPERBOUND (nlv$cl_connections.list^) DO
      IF nlv$cl_connections.list^ [root].first <> NIL THEN
        nlp$cl_get_nonexclusive_to_root (root);
        connection := nlv$cl_connections.list^ [root].first;

      /scan_stem_for_inactive/
        WHILE (connection <> NIL) DO
          IF (connection^.layers_active = $nlt$cl_layers []) THEN
            next_connection := connection^.nextt;
            nlp$cl_release_nonexclu_to_root (root);
            nlp$cl_release_connection (connection^.identifier);
            nlp$cl_get_nonexclusive_to_root (root);

{ The assumption is that an inactive connection is released from the root only by the timer task.

            connection := next_connection;
          ELSE
            connection := connection^.nextt;
          IFEND;
        WHILEND /scan_stem_for_inactive/;
        nlp$cl_release_nonexclu_to_root (root);
      IFEND;
    FOREND /scan_roots_for_inactive/;
  PROCEND release_terminated_connections;
?? OLDTITLE ??
MODEND nlm$timer_monitor;
