?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access: Internal TCP Portion Of The Socket Layer' ??
MODULE nlm$sk_tcp_socket_layer;
?? RIGHT := 110 ??

{ PURPOSE:
{   This module contains procedures neccesary to support the TCP portion of the
{   socket layer.
{ DESIGN:
{   These procedures are called by the socket layer external interface code and in turn
{   interface to the TCP Access Agent.
{   The XDCL'd procedures have been grouped in alphabetical order
{   followed by the internal procedures. The internal procedures are also in alphabetical
{   order.
{   This module contains code that executes in ring 3. It resides on OSF$JOB_TEMPLATE_23D.
{
{ NOTES:
{   The following abbreviations have been used in this module:
{          TCP - Transmission Control Protocol

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nac$null_connection_id
*copyc nac$sk_all_ip_addresses
*copyc nae$sk_socket_layer
*copyc nat$application_name
*copyc nat$connection_id
*copyc nat$data_fragments
*copyc nat$sk_interface_mode
*copyc nat$sk_listen_queue_limit
*copyc nat$sk_socket_identifier
*copyc nat$sk_traffic_pattern
*copyc nat$wait_time
*copyc nlc$udp_null_global_socket_id
*copyc nlt$bm_message_id
*copyc nlt$cl_connection
*copyc nlt$cl_layer_name
*copyc nlt$device_count
*copyc nlt$device_identifier
*copyc nlt$tcp_listen_socket
*copyc nlt$tcp_listen_sockets
*copyc nlt$tcp_sender_task
*copyc nlt$tcp_received_data
*copyc nlt$tcp_received_socket
*copyc nlt$tcp_receiver_task
*copyc nlt$tcp_socket_layer
*copyc nlt$tcp_wait_for_socket
*copyc nlt$tcpaa_event
*copyc nlt$tm_device_address_list
*copyc ost$free_running_clock
*copyc ost$global_task_id
*copyc ost$signature_lock_status
*copyc ost$status
?? POP ??
*copyc nap$condition_handler_trace
*copyc nap$namve_system_error
*copyc nlp$bm_concatenate_messages
*copyc nlp$bm_create_message
*copyc nlp$bm_deliver_message
*copyc nlp$bm_get_message_resources
*copyc nlp$bm_release_message
*copyc nlp$cc_receive_data
*copyc nlp$cc_report_undelivered_data
*copyc nlp$cl_activate_layer
*copyc nlp$cl_activate_receiver
*copyc nlp$cl_activate_sender
*copyc nlp$cl_create_connection
*copyc nlp$cl_deactivate_layer
*copyc nlp$cl_deactivate_receiver
*copyc nlp$cl_deactivate_sender
*copyc nlp$cl_get_exclusive_via_cid
*copyc nlp$cl_get_layer_connection
*copyc nlp$cl_initialize_template
*copyc nlp$cl_release_exclusive_access
*copyc nlp$get_exclusive_access
*copyc nlp$get_nonexclusive_access
*copyc nlp$osi_get_outbound_capacity
*copyc nlp$release_exclusive_access
*copyc nlp$release_nonexclusive_access
*copyc nlp$sk_fragment_data
*copyc nlp$sk_tcp_deactivate_layer
*copyc nlp$sk_tcp_get_rec_task_entry
*copyc nlp$sk_tcp_get_send_task_entry
*copyc nlp$sk_tcp_ret_rec_data_entry
*copyc nlp$sk_tcp_ret_rec_task_entry
*copyc nlp$sk_tcp_ret_send_task_entry
*copyc nlp$tcp_accept_socket
*copyc nlp$tcp_flush_release_socket
*copyc nlp$tcp_initialize
*copyc nlp$tcp_listen_socket
*copyc nlp$tcp_release_socket
*copyc nlp$tcp_send_data_fragments
*copyc nlp$tcp_set_socket_options
*copyc nlp$tcpip_decrement_appl_access
*copyc nlp$tcpip_increment_appl_access
*copyc nlp$tcpip_set_socket_assigned
*copyc nlp$tm_get_local_tcp_devices
*copyc nlp$tm_select_by_local_tcp_addr
*copyc osp$append_status_integer
*copyc osp$clear_job_signature_lock
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$establish_condition_handler
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$set_status_from_condition
*copyc pmp$continue_to_cause
*copyc pmp$get_executing_task_gtid
*copyc pmp$ready_task
*copyc pmp$wait
*copyc syp$cycle

*copyc nav$network_paged_heap
*copyc nlv$bm_null_message_id
*copyc nlv$configured_network_devices
*copyc nlv$tcp_listen_sockets
*copyc nav$network_paged_heap
*copyc oss$job_paged_literal

  VAR
    unexpected_state: [STATIC, READ, oss$job_paged_literal] string (17) := 'Unexpected state.';

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_accept_socket', EJECT ??
*copy nlh$sk_tcp_accept_socket

  PROCEDURE [XDCL] nlp$sk_tcp_accept_socket
    (    port: nat$sk_port_number;
         bound_address: nat$sk_ip_address;
         graceful_close: boolean;
         traffic_pattern: nat$sk_traffic_pattern;
         wait_time: nat$wait_time;
     VAR connection_id: nat$connection_id;
     VAR source_socket: nat$sk_socket_address;
     VAR local_ip_address: nat$sk_ip_address;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_accept_socket', EJECT ??

    PROCEDURE terminate_accept_socket
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

{ Dequeue the task from the wait list.

      nap$condition_handler_trace (condition, save_area);
      nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
      listen_socket := nlv$tcp_listen_sockets.list;
      WHILE (listen_socket <> NIL) AND ((listen_socket^.port <> port) OR
            (listen_socket^.bound_address <> bound_address)) DO
        listen_socket := listen_socket^.next_entry;
      WHILEND;
      IF listen_socket <> NIL THEN
        nlp$get_exclusive_access (listen_socket^.access_control);
        previous_wait_for_socket := ^listen_socket^.wait_for_socket_list;
        WHILE (previous_wait_for_socket^ <> NIL) AND (previous_wait_for_socket^^.task_id <> current_task_id)
              DO
          previous_wait_for_socket := ^previous_wait_for_socket^^.next_entry;
        WHILEND;
        IF previous_wait_for_socket^ <> NIL THEN
          wait_for_socket := previous_wait_for_socket^;
          previous_wait_for_socket^ := wait_for_socket^.next_entry;
          FREE wait_for_socket IN nav$network_paged_heap^;
        IFEND;
        nlp$release_exclusive_access (listen_socket^.access_control);
      IFEND;
      nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

    PROCEND terminate_accept_socket;
?? OLDTITLE, EJECT ??

    VAR
      application: nat$application_name,
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      current_task_id: ost$global_task_id,
      current_time: ost$free_running_clock,
      end_time: integer,
      layer_active: boolean,
      listen_socket: ^nlt$tcp_listen_socket,
      previous_wait_for_socket: ^^nlt$tcp_wait_for_socket,
      received_socket: ^nlt$tcp_received_socket,
      remaining_time: integer,
      tcp_connection: ^nlt$tcp_socket_layer,
      wait_for_socket: ^nlt$tcp_wait_for_socket;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (current_task_id);
    remaining_time := wait_time;
    end_time := #FREE_RUNNING_CLOCK (0) + remaining_time * 1000;
    osp$establish_block_exit_hndlr (^terminate_accept_socket);

  /accept_socket/
    REPEAT
      nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

{ Find the listen socket corresponding to the given listen socket id.

      listen_socket := nlv$tcp_listen_sockets.list;
      WHILE (listen_socket <> NIL) AND ((listen_socket^.port <> port) OR
            (listen_socket^.bound_address <> bound_address)) DO
        listen_socket := listen_socket^.next_entry;
      WHILEND;
      IF listen_socket <> NIL THEN
        nlp$get_exclusive_access (listen_socket^.access_control);
        IF listen_socket^.received_sockets <> NIL THEN
          received_socket := listen_socket^.received_sockets;

          IF received_socket^.connected OR (received_socket^.release_reason = nlc$tcpaa_ri_user_termination)
                THEN
            application := listen_socket^.application;
            connection_id := received_socket^.connection_id;
            source_socket := received_socket^.source_socket;
            local_ip_address := received_socket^.destination_socket.ip_address;
            listen_socket^.received_sockets := received_socket^.next_entry;
            FREE received_socket IN nav$network_paged_heap^;

{ Dequeue the task from the wait for socket list.

            previous_wait_for_socket := ^listen_socket^.wait_for_socket_list;
            WHILE (previous_wait_for_socket^ <> NIL) AND (previous_wait_for_socket^^.task_id <>
                  current_task_id) DO
              previous_wait_for_socket := ^previous_wait_for_socket^^.next_entry;
            WHILEND;
            IF previous_wait_for_socket^ <> NIL THEN
              wait_for_socket := previous_wait_for_socket^;
              previous_wait_for_socket^ := wait_for_socket^.next_entry;
              FREE wait_for_socket IN nav$network_paged_heap^;
            IFEND;
            nlp$release_exclusive_access (listen_socket^.access_control);
            nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

{ Send an accept socket request to the peer.

            cl_connection := NIL;
            layer_active := FALSE;
            IF connection_id <> nac$null_connection_id THEN
              nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
              IF cl_connection <> NIL THEN
                nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
              IFEND;
            IFEND;
            IF (cl_connection <> NIL) AND (layer_active) THEN
              IF tcp_connection^.state = nlc$tcp_conn_await_accept THEN
                nlp$tcp_accept_socket (cl_connection, graceful_close, traffic_pattern, nlc$cc_normal_class,
                      {ignore} status);
                status.normal := TRUE;
                tcp_connection^.state := nlc$tcp_conn_open;
                nlp$cl_release_exclusive_access (cl_connection);

{ Request application management to update the socket status.

                nlp$tcpip_set_socket_assigned (application, connection_id, {ignore} status);
                status.normal := TRUE;
                EXIT /accept_socket/; {----->
              ELSE { Connection closed
                nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
                nlp$cl_release_exclusive_access (cl_connection);
                nlp$tcpip_decrement_appl_access (application, nlc$udp_null_global_socket_id, connection_id,
                      {ignore} status);

{ Return the disconnected connection.

                connection_id := nac$null_connection_id;
                EXIT /accept_socket/; {----->
              IFEND;
            ELSE { cl_connection = NIL or NOT layer_active

{ Return the disconnected connection.

              connection_id := nac$null_connection_id;
              IF cl_connection <> NIL THEN
                nlp$cl_release_exclusive_access (cl_connection);
              IFEND;
              EXIT /accept_socket/; {----->
            IFEND;

          ELSE { Network disconnect

{ On network disconnects (assumed be anything except 'user termination') the received socket is discarded
{ with no notification to the calling procedure.

            listen_socket^.received_sockets := received_socket^.next_entry;
            FREE received_socket IN nav$network_paged_heap^;
            nlp$release_exclusive_access (listen_socket^.access_control);
            nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
            CYCLE /accept_socket/; {----->
          IFEND;
        ELSE { No received sockets

{ Check if task already queued in the wait for socket list.

          previous_wait_for_socket := ^listen_socket^.wait_for_socket_list;
          WHILE (previous_wait_for_socket^ <> NIL) AND (previous_wait_for_socket^^.task_id <> current_task_id)
                DO
            previous_wait_for_socket := ^previous_wait_for_socket^^.next_entry;
          WHILEND;
          IF remaining_time > 0 THEN
            IF previous_wait_for_socket^ = NIL THEN

{ Queue the task.

              REPEAT
                ALLOCATE wait_for_socket IN nav$network_paged_heap^;
                IF wait_for_socket = NIL THEN
                  syp$cycle;
                IFEND;
              UNTIL wait_for_socket <> NIL;
              wait_for_socket^.next_entry := NIL;
              wait_for_socket^.task_id := current_task_id;
              previous_wait_for_socket^ := wait_for_socket;
            IFEND;

            nlp$release_exclusive_access (listen_socket^.access_control);
            nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
            pmp$wait (remaining_time, remaining_time);
            current_time := #FREE_RUNNING_CLOCK (0);
            IF end_time > current_time THEN
              remaining_time := (end_time - current_time) DIV 1000;
            ELSE
              remaining_time := 0;
            IFEND;
            CYCLE /accept_socket/; {----->
          ELSE { wait_time = 0
            IF previous_wait_for_socket^ <> NIL THEN

{ Dequeue the task.

              wait_for_socket := previous_wait_for_socket^;
              previous_wait_for_socket^ := wait_for_socket^.next_entry;
              FREE wait_for_socket IN nav$network_paged_heap^;
            IFEND;
            osp$set_status_condition (nae$sk_no_accept_socket, status);
            nlp$release_exclusive_access (listen_socket^.access_control);
            nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
          IFEND;
        IFEND;
      ELSE { listen_socket = NIL
        nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
        osp$set_status_condition (nae$sk_socket_terminated, status);
      IFEND;
    UNTIL NOT status.normal;

    osp$disestablish_cond_handler;

  PROCEND nlp$sk_tcp_accept_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_activate_listen', EJECT ??
*copy nlh$sk_tcp_activate_listen

  PROCEDURE [XDCL] nlp$sk_tcp_activate_listen
    (    socket_id: nat$sk_socket_identifier;
         application: nat$application_name;
         port: nat$sk_port_number;
         bound_address: nat$sk_ip_address;
         queue_limit: nat$sk_listen_queue_limit;
         selection_criteria: nat$sk_socket_address;
     VAR status: ost$status);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      count: nlt$device_count,
      device_id: nlt$device_identifier,
      i: integer,
      ignore_layer_active: boolean,
      layer_active: boolean,
      listen_socket: ^nlt$tcp_listen_socket,
      tcp_connection: ^nlt$tcp_socket_layer,
      tcp_devices: ^nlt$tm_device_address_list;

    status.normal := TRUE;
    count := 0;
    PUSH tcp_devices: [1 .. nlv$configured_network_devices.network_device_count];
    IF bound_address <> nac$sk_all_ip_addresses THEN
      nlp$tm_select_by_local_tcp_addr (bound_address, tcp_devices^ [1].device_id, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      tcp_devices^ [1].address := bound_address;
      count := 1;

    ELSE

{ This procedure will return the list of TCP devices and the associated IP addresses.
      nlp$tm_get_local_tcp_devices (tcp_devices^, count);
      IF count = 0 THEN
        osp$set_status_abnormal (nac$status_id, nae$sk_no_device_configured, 'TCP', status);
        RETURN; {----->
      IFEND;
    IFEND;

    REPEAT
      ALLOCATE listen_socket: [1 .. nlv$configured_network_devices.network_device_count] IN
            nav$network_paged_heap^;
      IF listen_socket = NIL THEN
        syp$cycle;
      IFEND;
    UNTIL listen_socket <> NIL;

    listen_socket^.access_control.nonexclusive_accessors := 0;
    listen_socket^.access_control.exclusive := FALSE;
    listen_socket^.access_control.fill := 0;
    listen_socket^.identifier := socket_id;
    listen_socket^.application := application;
    listen_socket^.port := port;
    listen_socket^.bound_address := bound_address;
    listen_socket^.selection_criteria := selection_criteria;
    listen_socket^.queue_limit := queue_limit;
    listen_socket^.received_sockets := NIL;
    listen_socket^.wait_for_socket_list := NIL;

{ Initialize the device list.

    FOR i := 1 TO UPPERBOUND (listen_socket^.device_list) DO
      listen_socket^.device_list [i].device_id := 0;
      listen_socket^.device_list [i].ip_address := 0;
      listen_socket^.device_list [i].connection_id := nac$null_connection_id;
      listen_socket^.device_list [i].status := nlc$tcp_device_closed;
    FOREND;

{ Add the listen socket to the list.

    nlp$get_exclusive_access (nlv$tcp_listen_sockets.access_control);
    listen_socket^.next_entry := nlv$tcp_listen_sockets.list;
    nlv$tcp_listen_sockets.list := listen_socket;
    nlp$release_exclusive_access (nlv$tcp_listen_sockets.access_control);
    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
    listen_socket := nlv$tcp_listen_sockets.list;

{ The listen socket can be terminated via application management while the lock
{ is being changed from exclusive to nonexclusive.

    WHILE (listen_socket <> NIL) AND ((listen_socket^.port <> port) OR
          (listen_socket^.bound_address <> bound_address)) DO
      listen_socket := listen_socket^.next_entry;
    WHILEND;

{ The listen socket should be there in the list.

    IF listen_socket <> NIL THEN
      nlp$get_exclusive_access (listen_socket^.access_control);

    /open_connections/
      FOR i := 1 TO count DO
        device_id := tcp_devices^ [i].device_id;
        listen_socket^.device_list [device_id].device_id := device_id;
        listen_socket^.device_list [device_id].ip_address := tcp_devices^ [i].address;
        nlp$cl_create_connection (nlc$tcp_interface, cl_connection);
        IF cl_connection <> NIL THEN
          nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, {ignore} layer_active,
                tcp_connection);
          tcp_connection^.device_id := device_id;
          tcp_connection^.disconnect_reason := 0;
          tcp_connection^.user_initiated_close := FALSE;
          tcp_connection^.socket_id := socket_id;
          tcp_connection^.socket_type := nlc$tcp_listen_socket;
          tcp_connection^.inventory_report := 0;
          tcp_connection^.send_queue := NIL;
          tcp_connection^.receive_queue := NIL;
          tcp_connection^.received_data := NIL;
          tcp_connection^.available_sender_pool := NIL;
          tcp_connection^.available_receiver_pool := NIL;
          tcp_connection^.available_data_pool := NIL;
          tcp_connection^.source_socket.port := port;
          tcp_connection^.source_socket.ip_address := tcp_devices^ [i].address;
          tcp_connection^.destination_socket.port := 0;
          tcp_connection^.destination_socket.ip_address := 0;
          tcp_connection^.waiting_task_id.index := 0;
          nlp$tcp_listen_socket (cl_connection, port, queue_limit, selection_criteria, device_id,
                nlc$cc_normal_class, status);
          IF status.normal THEN
            nlp$cl_activate_layer (nlc$tcp_interface, cl_connection);
            tcp_connection^.state := nlc$tcp_conn_await_confirm;
            listen_socket^.device_list [device_id].connection_id := cl_connection^.identifier;
            listen_socket^.device_list [device_id].status := nlc$tcp_device_await_confirm;
          ELSE { Resource constraint
            listen_socket^.device_list [device_id].status := nlc$tcp_device_res_constraint;
          IFEND;
          nlp$cl_release_exclusive_access (cl_connection);
        ELSE { cl_connection = NIL
          osp$set_status_condition (nae$sk_insufficient_resources, status);
          EXIT /open_connections/; {----->
        IFEND;
      FOREND /open_connections/;
      nlp$release_exclusive_access (listen_socket^.access_control);
    ELSE { listen socket has been terminated
      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;
    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

{ **** NOTE - This procedure does not wait for the responses from the devices. (???)

  PROCEND nlp$sk_tcp_activate_listen;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_await_clear_to_send', EJECT ??
*copy nlh$sk_tcp_await_clear_to_send

  PROCEDURE [XDCL] nlp$sk_tcp_await_clear_to_send
    (    connection_id: nat$connection_id;
         wait: boolean;
     VAR activity_complete: boolean);

    VAR
      capacity: nat$data_length,
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      current_task_id: ost$global_task_id,
      ignore_status: ost$status,
      layer_active: boolean,
      previous_sender_task: ^^nlt$tcp_sender_task,
      sender_task: ^nlt$tcp_sender_task,
      tcp_connection: ^nlt$tcp_socket_layer;

    activity_complete := FALSE;
    pmp$get_executing_task_gtid (current_task_id);
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active AND (tcp_connection^.state = nlc$tcp_conn_open) THEN
        IF tcp_connection^.send_queue = NIL THEN
          nlp$osi_get_outbound_capacity (cl_connection, capacity);
          IF capacity <= 0 THEN
            nlp$cc_receive_data (cl_connection);
            nlp$osi_get_outbound_capacity (cl_connection, capacity);
          IFEND;
          IF (capacity > 0) OR (tcp_connection^.state <> nlc$tcp_conn_open) THEN
            activity_complete := TRUE;
          ELSEIF wait THEN

{ Queue the task on the send queue.

            nlp$sk_tcp_get_send_task_entry (tcp_connection, sender_task);
            sender_task^.next_entry := NIL;
            sender_task^.task_id := current_task_id;
            sender_task^.send_type := nlc$tcp_await_clear_to_send;
            tcp_connection^.send_queue := sender_task;
            nlp$cl_activate_sender (cl_connection);
          IFEND;
        ELSE { tcp_connection^.send_queue <> NIL
          sender_task := tcp_connection^.send_queue;
          IF sender_task^.task_id = current_task_id THEN

{ Current task is at the head of the queue.

            nlp$osi_get_outbound_capacity (cl_connection, capacity);
            IF capacity <= 0 THEN
              nlp$cc_receive_data (cl_connection);

{ A release event may be received, which would result in the connection being closed.

              nlp$osi_get_outbound_capacity (cl_connection, capacity);
            IFEND;
            IF (capacity > 0) OR (tcp_connection^.state <> nlc$tcp_conn_open) THEN
              activity_complete := TRUE;
              IF tcp_connection^.state = nlc$tcp_conn_open THEN
                tcp_connection^.send_queue := sender_task^.next_entry;
                nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
                IF tcp_connection^.send_queue <> NIL THEN
                  pmp$ready_task (tcp_connection^.send_queue^.task_id, ignore_status);
                IFEND;
              IFEND;
            ELSEIF NOT wait THEN

{ Dequeue the sender task.

              tcp_connection^.send_queue := sender_task^.next_entry;
              nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
              nlp$cl_deactivate_sender (cl_connection);
              IF tcp_connection^.send_queue <> NIL THEN
                pmp$ready_task (tcp_connection^.send_queue^.task_id, ignore_status);
              IFEND;
            ELSE { wait
              nlp$cl_activate_sender (cl_connection);
            IFEND;
          ELSE { current task not at the head of the queue

{ Queue the task at the end of the send queue if not already queued.

            previous_sender_task := ^tcp_connection^.send_queue;
            WHILE (previous_sender_task^ <> NIL) AND (previous_sender_task^^.task_id <> current_task_id) DO
              previous_sender_task := ^previous_sender_task^^.next_entry;
            WHILEND;
            IF wait THEN
              IF previous_sender_task^ = NIL THEN
                nlp$sk_tcp_get_send_task_entry (tcp_connection, sender_task);
                sender_task^.next_entry := NIL;
                sender_task^.task_id := current_task_id;
                sender_task^.send_type := nlc$tcp_await_clear_to_send;
                previous_sender_task^ := sender_task;
              IFEND;
            ELSE { NOT wait
              IF previous_sender_task^ <> NIL THEN
                sender_task := previous_sender_task^;
                previous_sender_task^ := sender_task^.next_entry;
                nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
              IFEND;
            IFEND;
          IFEND;
        IFEND;
      ELSE { Layer inactive or tcp_connection^.state <> nlc$tcp_conn_open
        activity_complete := TRUE;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL
      activity_complete := TRUE;
    IFEND;

  PROCEND nlp$sk_tcp_await_clear_to_send;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_await_data_available', EJECT ??
*copy nlh$sk_tcp_await_data_available

  PROCEDURE [XDCL] nlp$sk_tcp_await_data_available
    (    connection_id: nat$connection_id;
         wait: boolean;
     VAR activity_complete: boolean);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      current_task_id: ost$global_task_id,
      ignore_status: ost$status,
      layer_active: boolean,
      previous_receiver_task: ^^nlt$tcp_receiver_task,
      receiver_active: boolean,
      receiver_task: ^nlt$tcp_receiver_task,
      tcp_connection: ^nlt$tcp_socket_layer;

    activity_complete := FALSE;
    pmp$get_executing_task_gtid (current_task_id);
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active AND (tcp_connection^.state = nlc$tcp_conn_open) THEN
        IF tcp_connection^.receive_queue = NIL THEN
          IF tcp_connection^.received_data <> NIL THEN
            activity_complete := TRUE;
          ELSE
            nlp$cc_receive_data (cl_connection);
            activity_complete := (tcp_connection^.received_data <> NIL) OR
                  (tcp_connection^.state <> nlc$tcp_conn_open);
            IF (NOT activity_complete) AND wait THEN

{ Queue the task on the receive queue.

              nlp$sk_tcp_get_rec_task_entry (tcp_connection, receiver_task);
              receiver_task^.next_entry := NIL;
              receiver_task^.task_id := current_task_id;
              receiver_task^.receive_type := nlc$tcp_await_data_available;
              tcp_connection^.receive_queue := receiver_task;
              nlp$cl_activate_receiver (cl_connection);
              receiver_task^.receiver_active := TRUE;
            IFEND;
          IFEND;
        ELSE { tcp_connection^.receive_queue <> NIL
          receiver_task := tcp_connection^.receive_queue;
          IF receiver_task^.task_id = current_task_id THEN

{ Current task is at the head of the queue.

            activity_complete := tcp_connection^.received_data <> NIL;
            receiver_active := receiver_task^.receiver_active;
            IF NOT activity_complete THEN
              nlp$cc_receive_data (cl_connection);

{ A release event may be received, which would result in the connection being closed.

              activity_complete := (tcp_connection^.received_data <> NIL) OR
                    (tcp_connection^.state <> nlc$tcp_conn_open);
            IFEND;
            IF activity_complete OR NOT wait THEN
              IF receiver_active THEN
                nlp$cl_deactivate_receiver (cl_connection);
              IFEND;
              IF tcp_connection^.state = nlc$tcp_conn_open THEN
                tcp_connection^.receive_queue := receiver_task^.next_entry;
                nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
                IF tcp_connection^.receive_queue <> NIL THEN
                  pmp$ready_task (tcp_connection^.receive_queue^.task_id, ignore_status);
                IFEND;
              IFEND;
            ELSEIF NOT receiver_task^.receiver_active THEN
              nlp$cl_activate_receiver (cl_connection);
              receiver_task^.receiver_active := TRUE;
            IFEND;
          ELSE { current task is not at the head of the receive queue
            previous_receiver_task := ^tcp_connection^.receive_queue;
            WHILE (previous_receiver_task^ <> NIL) AND (previous_receiver_task^^.task_id <> current_task_id)
                  DO
              previous_receiver_task := ^previous_receiver_task^^.next_entry;
            WHILEND;
            IF (previous_receiver_task^ = NIL) AND wait THEN

{ Queue the task at the end of the receive queue.

              nlp$sk_tcp_get_rec_task_entry (tcp_connection, receiver_task);
              receiver_task^.next_entry := NIL;
              receiver_task^.task_id := current_task_id;
              receiver_task^.receive_type := nlc$tcp_await_data_available;
              receiver_task^.receiver_active := FALSE;
              previous_receiver_task^ := receiver_task;
            ELSEIF NOT wait THEN
              IF previous_receiver_task^ <> NIL THEN

{ Dequeue the task from the receive queue.

                receiver_task := previous_receiver_task^;
                previous_receiver_task^ := receiver_task^.next_entry;
                nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
              IFEND;
            IFEND;
          IFEND;
        IFEND;
      ELSE { Layer inactive or tcp_connection^.state <> nlc$tcp_conn_open
        activity_complete := TRUE;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL
      activity_complete := TRUE;
    IFEND;

  PROCEND nlp$sk_tcp_await_data_available;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_cancel_socket_offer', EJECT ??
*copy nlh$sk_tcp_cancel_socket_offer

  PROCEDURE [XDCL] nlp$sk_tcp_cancel_socket_offer
    (    connection_id: nat$connection_id;
     VAR status: ost$status);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      layer_active: boolean,
      tcp_connection: ^nlt$tcp_socket_layer;

    status.normal := TRUE;
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active THEN
        IF tcp_connection^.state = nlc$tcp_conn_offered THEN
          tcp_connection^.state := nlc$tcp_conn_open;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
          IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
          ELSE
            osp$set_status_condition (nae$sk_socket_disconnected, status);
          IFEND;
          osp$append_status_integer (osc$status_parameter_delimiter, tcp_connection^.socket_id, 10, TRUE,
                status);
          IF tcp_connection^.state = nlc$tcp_conn_closed THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
          nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          osp$set_status_condition (nae$sk_socket_terminated, status);
        ELSE { closing state

{ Ignore the closing state.

        IFEND;
      ELSE { Layer inactive

{ The connection is assumed to be terminated via application management.

        osp$set_status_condition (nae$sk_socket_terminated, status);
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL

{ The connection is assumed to be terminated via application management.

      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;

  PROCEND nlp$sk_tcp_cancel_socket_offer;
?? OLDTITLE ??
?? NEWTITLE := 'nlp$sk_tcp_check_accept_socket', EJECT ??
*copyc nlh$sk_tcp_check_accept_socket

  PROCEDURE [XDCL] nlp$sk_tcp_check_accept_socket
    (    application: nat$application_name;
         port: nat$sk_port_number;
         bound_address: nat$sk_ip_address;
         wait: boolean;
     VAR activity_complete: boolean);

    VAR
      current_task_id: ost$global_task_id,
      listen_socket: ^nlt$tcp_listen_socket,
      previous_wait_for_socket: ^^nlt$tcp_wait_for_socket,
      wait_for_socket: ^nlt$tcp_wait_for_socket;

    activity_complete := FALSE;
    pmp$get_executing_task_gtid (current_task_id);
    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
    listen_socket := nlv$tcp_listen_sockets.list;
    WHILE (listen_socket <> NIL) AND ((listen_socket^.application <> application) OR
          (listen_socket^.port <> port) OR (listen_socket^.bound_address <> bound_address)) DO
      listen_socket := listen_socket^.next_entry;
    WHILEND;

    IF listen_socket <> NIL THEN
      nlp$get_exclusive_access (listen_socket^.access_control);
      IF listen_socket^.wait_for_socket_list = NIL THEN
        IF listen_socket^.received_sockets <> NIL THEN
          activity_complete := TRUE;
        ELSEIF wait THEN

{ Queue the task.

          REPEAT
            ALLOCATE wait_for_socket IN nav$network_paged_heap^;
            IF wait_for_socket = NIL THEN
              syp$cycle;
            IFEND;
          UNTIL wait_for_socket <> NIL;
          wait_for_socket^.next_entry := NIL;
          wait_for_socket^.task_id := current_task_id;
          listen_socket^.wait_for_socket_list := wait_for_socket;
        IFEND;
      ELSE { listen_socket^.wait_for_socket_list <> NIL

{ Check if the task is already queued.

        previous_wait_for_socket := ^listen_socket^.wait_for_socket_list;
        WHILE (previous_wait_for_socket^ <> NIL) AND (previous_wait_for_socket^^.task_id <> current_task_id)
              DO
          previous_wait_for_socket := ^previous_wait_for_socket^^.next_entry;
        WHILEND;
        IF previous_wait_for_socket^ = NIL THEN
          IF wait THEN

{ Queue the task.

            REPEAT
              ALLOCATE wait_for_socket IN nav$network_paged_heap^;
              IF wait_for_socket = NIL THEN
                syp$cycle;
              IFEND;
            UNTIL wait_for_socket <> NIL;
            wait_for_socket^.next_entry := NIL;
            wait_for_socket^.task_id := current_task_id;
            previous_wait_for_socket^ := wait_for_socket;
          IFEND;
        ELSEIF NOT wait THEN

{ Dequeue the task.

          wait_for_socket := previous_wait_for_socket^;
          previous_wait_for_socket^ := wait_for_socket^.next_entry;
          FREE wait_for_socket IN nav$network_paged_heap^;
        IFEND;
      IFEND;
      nlp$release_exclusive_access (listen_socket^.access_control);
    ELSE
      activity_complete := TRUE;
    IFEND;

    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

  PROCEND nlp$sk_tcp_check_accept_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_close_socket', EJECT ??
*copy nlh$sk_tcp_close_socket

  PROCEDURE [XDCL] nlp$sk_tcp_close_socket
    (    connection_id: nat$connection_id;
         graceful_close: boolean);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      ignore_status: ost$status,
      layer_active: boolean,
      tcp_connection: ^nlt$tcp_socket_layer;

    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active THEN
        CASE tcp_connection^.state OF
        = nlc$tcp_conn_closed =
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;

        = nlc$tcp_conn_closing =
          IF (tcp_connection^.received_data <> NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            discard_received_data (tcp_connection);
          IFEND;

          tcp_connection^.state := nlc$tcp_conn_closed;
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;

        = nlc$tcp_conn_open =
          terminate_io (cl_connection, tcp_connection);
          IF graceful_close THEN

{ Send a flush release request to the TCPAP.

            nlp$tcp_flush_release_socket (cl_connection, ignore_status);
          ELSE {non_graceful close
            nlp$tcp_release_socket (cl_connection, ignore_status);
          IFEND;

          IF tcp_connection^.receive_queue = NIL THEN
            discard_received_data (tcp_connection);
          IFEND;
          tcp_connection^.state := nlc$tcp_conn_closed;

          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          ELSE
            tcp_connection^.user_initiated_close := TRUE;
          IFEND;

        = nlc$tcp_conn_await_confirm =

{ Cannot happen as the job socket is kept locked while the connect is in progress.

          nap$namve_system_error ({Recoverable_error=} TRUE, unexpected_state, NIL);

        = nlc$tcp_conn_await_accept =

{ Cannot happen as the user cannot close a socket that has not been accepted.

          nap$namve_system_error ({Recoverable_error=} TRUE, unexpected_state, NIL);

        = nlc$tcp_conn_terminated =

{ Connection has been terminated via application management.

          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
        ELSE { Unexpected state
          nap$namve_system_error ({Recoverable_error=} TRUE, unexpected_state, NIL);
        CASEND;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    IFEND;

  PROCEND nlp$sk_tcp_close_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_conn_event_processor', EJECT ??
*copy nlh$sk_tcp_conn_event_processor

  PROCEDURE [XDCL] nlp$sk_tcp_conn_event_processor
    (    cl_connection: ^nlt$cl_connection;
         event: nlt$tcpaa_event;
     VAR inventory_report: integer);

    VAR
      ignore_layer_active: boolean,
      local_status: ost$status,
      tcp_connection: ^nlt$tcp_socket_layer;

    IF event.kind = nlc$tcpaa_connect_event THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, ignore_layer_active, tcp_connection);
      assign_socket (event.connect.destination_socket, event.connect.source_socket, cl_connection^.identifier,
            local_status);
      IF local_status.normal THEN
        nlp$cl_activate_layer (nlc$tcp_interface, cl_connection);
        tcp_connection^.state := nlc$tcp_conn_await_accept;
        tcp_connection^.device_id := event.connect.device_id;
        tcp_connection^.socket_type := nlc$tcp_accept_socket;
        tcp_connection^.disconnect_reason := 0;
        tcp_connection^.user_initiated_close := FALSE;
        tcp_connection^.inventory_report := 0;
        tcp_connection^.send_queue := NIL;
        tcp_connection^.receive_queue := NIL;
        tcp_connection^.received_data := NIL;
        tcp_connection^.source_socket := event.connect.source_socket;
        tcp_connection^.destination_socket := event.connect.destination_socket;
        tcp_connection^.waiting_task_id.index := 0;

{ These pools will be initialized when the socket is accepted.

        tcp_connection^.available_receiver_pool := NIL;
        tcp_connection^.available_sender_pool := NIL;
        tcp_connection^.available_data_pool := NIL;
      ELSE { application inactive/unknown or max connections limit
        nlp$tcp_release_socket (cl_connection, {ignore} local_status);
      IFEND;
    ELSE { Invalid event
      nap$namve_system_error ({Recoverable_error=} TRUE, 'Invalid TCPAA event received.', NIL);
    IFEND;

  PROCEND nlp$sk_tcp_conn_event_processor;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_device_available', EJECT ??
*copy nlh$sk_tcp_device_available

  PROCEDURE [XDCL] nlp$sk_tcp_device_available
    (    device_id: nlt$device_identifier;
         ip_address: nat$sk_ip_address);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      ignore_layer_active: boolean,
      listen_socket: ^nlt$tcp_listen_socket,
      local_status: ost$status,
      tcp_connection: ^nlt$tcp_socket_layer,
      tcp_socket_devive_p: ^nlt$tcp_socket_device;

{ Scan all listen sockets bound to the given device.

    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
    listen_socket := nlv$tcp_listen_sockets.list;
    WHILE listen_socket <> NIL DO
      nlp$get_exclusive_access (listen_socket^.access_control);
      tcp_socket_devive_p := ^listen_socket^.device_list [device_id];
      IF ((listen_socket^.bound_address = nac$sk_all_ip_addresses) OR
            (listen_socket^.bound_address = ip_address))
{   } AND ((tcp_socket_devive_p^.status = nlc$tcp_device_closed) OR
            (tcp_socket_devive_p^.status = nlc$tcp_device_res_constraint)) THEN
        tcp_socket_devive_p^.device_id := device_id;
        tcp_socket_devive_p^.ip_address := ip_address;
        nlp$cl_create_connection (nlc$tcp_interface, cl_connection);
        IF cl_connection <> NIL THEN
          nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, ignore_layer_active, tcp_connection);
          tcp_connection^.device_id := device_id;
          tcp_connection^.disconnect_reason := 0;
          tcp_connection^.user_initiated_close := FALSE;
          tcp_connection^.socket_id := listen_socket^.identifier;
          tcp_connection^.socket_type := nlc$tcp_listen_socket;
          tcp_connection^.inventory_report := 0;
          tcp_connection^.send_queue := NIL;
          tcp_connection^.receive_queue := NIL;
          tcp_connection^.received_data := NIL;
          tcp_connection^.available_sender_pool := NIL;
          tcp_connection^.available_receiver_pool := NIL;
          tcp_connection^.available_data_pool := NIL;
          tcp_connection^.source_socket.port := listen_socket^.port;
          tcp_connection^.source_socket.ip_address := ip_address;
          tcp_connection^.destination_socket.port := 0;
          tcp_connection^.destination_socket.ip_address := 0;
          tcp_connection^.waiting_task_id.index := 0;
          nlp$tcp_listen_socket (cl_connection, listen_socket^.port, listen_socket^.queue_limit,
                listen_socket^.selection_criteria, device_id, nlc$cc_normal_class, local_status);
          IF local_status.normal THEN
            nlp$cl_activate_layer (nlc$tcp_interface, cl_connection);
            tcp_connection^.state := nlc$tcp_conn_await_confirm;
            tcp_socket_devive_p^.connection_id := cl_connection^.identifier;
            tcp_socket_devive_p^.status := nlc$tcp_device_await_confirm;
          ELSE { Resource constraint
            tcp_socket_devive_p^.status := nlc$tcp_device_res_constraint;
          IFEND;
          nlp$cl_release_exclusive_access (cl_connection);
        ELSE { cl_connection = NIL
          tcp_socket_devive_p^.status := nlc$tcp_device_res_constraint;
        IFEND;
      IFEND;

      nlp$release_exclusive_access (listen_socket^.access_control);
      listen_socket := listen_socket^.next_entry;
    WHILEND;
    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

  PROCEND nlp$sk_tcp_device_available;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_event_processor', EJECT ??
*copy nlh$sk_tcp_event_processor

  PROCEDURE [XDCL] nlp$sk_tcp_event_processor
    (    cl_connection: ^nlt$cl_connection;
         event: nlt$tcpaa_event;
     VAR inventory_report: integer);

    VAR
      buffers_freed: nat$data_length,
      current_task_id: ost$global_task_id,
      data: nlt$bm_message_id,
      data_length: integer,
      ignore_status: ost$status,
      layer_active: boolean,
      listen_socket: ^nlt$tcp_listen_socket,
      new_received_data: ^nlt$tcp_received_data,
      number_of_buffers_received: integer,
      partial_fragments: array [1 .. 2] of nlt$bm_message_id,
      previous_receiver_task: ^^nlt$tcp_receiver_task,
      previous_sender_task: ^^nlt$tcp_sender_task,
      received_data: ^nlt$tcp_received_data,
      receiver_task: ^nlt$tcp_receiver_task,
      sender_task: ^nlt$tcp_sender_task,
      tcp_connection: ^nlt$tcp_socket_layer;

    inventory_report := 0;
    pmp$get_executing_task_gtid (current_task_id);
    nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
    IF layer_active THEN
      CASE event.kind OF
      = nlc$tcpaa_data_event =
        data := event.data.data;
        IF (tcp_connection^.state = nlc$tcp_conn_open) OR (tcp_connection^.state = nlc$tcp_conn_offered) THEN
          nlp$bm_get_message_resources (data, data_length, number_of_buffers_received);
          buffers_freed := 0;
          IF tcp_connection^.received_data <> NIL THEN

{ Find the last received data.

            received_data := tcp_connection^.received_data;
            WHILE received_data^.next_entry <> NIL DO
              received_data := received_data^.next_entry;
            WHILEND;

            IF (event.data.urgent_data) OR (event.data.urgent_data = received_data^.urgent_flag) THEN

{ Merge the incoming message with the queued data.
{ The receipt of urgent data will make all preceeding data urgent.

              partial_fragments [1] := received_data^.message_id;
              partial_fragments [2] := data;
              nlp$bm_concatenate_messages (partial_fragments, received_data^.message_id);
              received_data^.urgent_flag := event.data.urgent_data;
              received_data^.push_flag := event.data.push_data;
              received_data^.length := received_data^.length + data_length;
              received_data^.buffer_count := received_data^.buffer_count + number_of_buffers_received;
            ELSE { queue it as a separate fragment
              IF tcp_connection^.available_data_pool <> NIL THEN
                new_received_data := tcp_connection^.available_data_pool;
                tcp_connection^.available_data_pool := new_received_data^.next_entry;
              ELSE
                REPEAT
                  ALLOCATE new_received_data IN nav$network_paged_heap^;
                  IF new_received_data = NIL THEN
                    syp$cycle;
                  IFEND;
                UNTIL new_received_data <> NIL;
              IFEND;
              new_received_data^.next_entry := NIL;
              new_received_data^.message_id := data;
              new_received_data^.push_flag := event.data.push_data;
              new_received_data^.urgent_flag := event.data.urgent_data;
              new_received_data^.length := data_length;
              new_received_data^.buffer_count := number_of_buffers_received;
              received_data^.next_entry := new_received_data;
            IFEND;
          ELSE { tcp_connection^.received_data = NIL
            IF tcp_connection^.receive_queue <> NIL THEN
              IF tcp_connection^.receive_queue^.task_id = current_task_id THEN
                IF tcp_connection^.receive_queue^.receive_type = nlc$tcp_receive_data THEN
                  continue_to_receive (cl_connection, tcp_connection, data, data_length, event.data.push_data,
                        event.data.urgent_data, buffers_freed);
                IFEND;
              ELSE
                pmp$ready_task (tcp_connection^.receive_queue^.task_id, ignore_status);
              IFEND;
            IFEND;
            IF data_length > 0 THEN

{ Queue the remaining message.

              IF tcp_connection^.available_data_pool <> NIL THEN
                new_received_data := tcp_connection^.available_data_pool;
                tcp_connection^.available_data_pool := new_received_data^.next_entry;
              ELSE
                REPEAT
                  ALLOCATE new_received_data IN nav$network_paged_heap^;
                  IF new_received_data = NIL THEN
                    syp$cycle;
                  IFEND;
                UNTIL new_received_data <> NIL;
              IFEND;
              new_received_data^.next_entry := NIL;
              new_received_data^.message_id := data;
              new_received_data^.push_flag := event.data.push_data;
              new_received_data^.urgent_flag := event.data.urgent_data;
              new_received_data^.length := data_length;
              new_received_data^.buffer_count := number_of_buffers_received - buffers_freed;
              tcp_connection^.received_data := new_received_data;
            IFEND;
          IFEND;
          tcp_connection^.inventory_report := tcp_connection^.inventory_report + number_of_buffers_received -
                buffers_freed;
          inventory_report := tcp_connection^.inventory_report;
        ELSE { unexpected state
          nlp$bm_release_message (data);
          issue_disconnect (cl_connection, tcp_connection);
        IFEND;

      = nlc$tcpaa_release_event =
        IF (tcp_connection^.state <> nlc$tcp_conn_closed) AND
              (tcp_connection^.state <> nlc$tcp_conn_closing) AND
              (tcp_connection^.state <> nlc$tcp_conn_terminated) THEN
          tcp_connection^.disconnect_reason := event.release.reason;
          IF tcp_connection^.socket_type = nlc$tcp_listen_socket THEN
            nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
            listen_socket := nlv$tcp_listen_sockets.list;

{ Find the listen socket.

            WHILE (listen_socket <> NIL) AND ((listen_socket^.port <> tcp_connection^.source_socket.port) OR
                  ((listen_socket^.bound_address <> nac$sk_all_ip_addresses) AND
                  (listen_socket^.bound_address <> tcp_connection^.source_socket.ip_address))) DO
              listen_socket := listen_socket^.next_entry;
            WHILEND;
            IF listen_socket <> NIL THEN
              nlp$get_exclusive_access (listen_socket^.access_control);
              listen_socket^.device_list [tcp_connection^.device_id].status := nlc$tcp_device_closed;
              listen_socket^.device_list [tcp_connection^.device_id].connection_id := nac$null_connection_id;
              nlp$release_exclusive_access (listen_socket^.access_control);
            ELSE

{ Ignore it. The listen socket is being terminated.

            IFEND;
            nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
            tcp_connection^.state := nlc$tcp_conn_closed;
            nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
          ELSEIF (tcp_connection^.socket_type = nlc$tcp_accept_socket) AND
                (tcp_connection^.state = nlc$tcp_conn_await_accept) THEN
            disconnect_unaccepted_socket (cl_connection, tcp_connection, event.release.reason);
          ELSEIF (tcp_connection^.socket_type = nlc$tcp_connect_socket) AND
                (tcp_connection^.state = nlc$tcp_conn_await_confirm) THEN
            tcp_connection^.state := nlc$tcp_conn_closed;
            inventory_report := 0;
            pmp$ready_task (tcp_connection^.waiting_task_id, ignore_status);
          ELSE { Connect socket or accept socket that has been accepted

{ Ready all tasks waiting for the data available indication and signal all tasks waiting to receive data.

            terminate_io (cl_connection, tcp_connection);

{ Discard queued data only if there is no active receiver.

            IF (tcp_connection^.received_data = NIL) THEN
              inventory_report := 0;
            ELSE
              inventory_report := tcp_connection^.inventory_report;
            IFEND;
            tcp_connection^.state := nlc$tcp_conn_closing;
          IFEND;
        ELSE { tcp_connection^.state = closed , closing or terminated
          nap$namve_system_error ({Recoverable_error=} TRUE, 'Received a TCPAA release event in closed state.'
                , NIL);
        IFEND;

      = nlc$tcpaa_listen_confirm_event =
        IF (tcp_connection^.state = nlc$tcp_conn_await_confirm) AND
              (tcp_connection^.socket_type = nlc$tcp_listen_socket) THEN
          tcp_connection^.state := nlc$tcp_conn_open;
          nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
          listen_socket := nlv$tcp_listen_sockets.list;

{ Find the listen socket for the given listen port.

          WHILE (listen_socket <> NIL) AND ((listen_socket^.port <> tcp_connection^.source_socket.port) OR
                ((listen_socket^.bound_address <> nac$sk_all_ip_addresses) AND
                (listen_socket^.bound_address <> tcp_connection^.source_socket.ip_address))) DO
            listen_socket := listen_socket^.next_entry;
          WHILEND;
          IF listen_socket <> NIL THEN
            nlp$get_exclusive_access (listen_socket^.access_control);
            listen_socket^.device_list [tcp_connection^.device_id].status := nlc$tcp_device_open;
            nlp$release_exclusive_access (listen_socket^.access_control);
          ELSE

{ Ignore, the listen has been terminated.

          IFEND;
          nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
        ELSE { Unexpected state
          issue_disconnect (cl_connection, tcp_connection);
        IFEND;

      = nlc$tcpaa_connect_confirm_event =
        IF (tcp_connection^.state = nlc$tcp_conn_await_confirm) AND
              (tcp_connection^.socket_type = nlc$tcp_connect_socket) THEN
          tcp_connection^.state := nlc$tcp_conn_open;
          IF tcp_connection^.waiting_task_id.index > 0 THEN
            pmp$ready_task (tcp_connection^.waiting_task_id, ignore_status);
          IFEND;
        ELSE { Protocol error
          issue_disconnect (cl_connection, tcp_connection);
        IFEND;

      = nlc$tcpaa_clear_to_send_event =
        IF (tcp_connection^.send_queue <> NIL) AND (tcp_connection^.send_queue^.task_id <> current_task_id)
              THEN
          activate_next_sender (tcp_connection);
        IFEND;

      = nlc$tcpaa_listen_reject_event =
        IF (tcp_connection^.state = nlc$tcp_conn_await_confirm) AND
              (tcp_connection^.socket_type = nlc$tcp_listen_socket) THEN
          tcp_connection^.disconnect_reason := event.listen_reject.reason;
          nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
          listen_socket := nlv$tcp_listen_sockets.list;

{ Find the listen socket.

          WHILE (listen_socket <> NIL) AND ((listen_socket^.port <> tcp_connection^.source_socket.port) OR
                ((listen_socket^.bound_address <> nac$sk_all_ip_addresses) AND
                (listen_socket^.bound_address <> tcp_connection^.source_socket.ip_address))) DO
            listen_socket := listen_socket^.next_entry;
          WHILEND;
          IF listen_socket <> NIL THEN
            nlp$get_exclusive_access (listen_socket^.access_control);
            listen_socket^.device_list [tcp_connection^.device_id].status := nlc$tcp_device_closed;
            listen_socket^.device_list [tcp_connection^.device_id].connection_id := nac$null_connection_id;
            nlp$release_exclusive_access (listen_socket^.access_control);
          ELSE

{ Ignore it. The listen socket is being terminated.

          IFEND;
          nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
          tcp_connection^.state := nlc$tcp_conn_closed;
          nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
        ELSE { protocol error
          issue_disconnect (cl_connection, tcp_connection);
        IFEND;

      ELSE { Invalid event
        nap$namve_system_error ({Recoverable_error=} TRUE, 'Received an invalid TCPAA event', NIL);
      CASEND;
    ELSE { Layer inactive
      nap$namve_system_error ({Recoverable_error=} TRUE,
            'Received a TCPAA event and the TCP socket layer is inactive.', NIL);
      IF event.kind = nlc$tcpaa_data_event THEN
        data := event.data.data;
        nlp$bm_release_message (data);
      IFEND;
    IFEND;

  PROCEND nlp$sk_tcp_event_processor;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_get_listen_addresses', EJECT ??
*copy nlh$sk_tcp_get_listen_addresses

  PROCEDURE [XDCL] nlp$sk_tcp_get_listen_addresses
    (    application: nat$application_name;
         port: nat$sk_port_number;
         bound_address: nat$sk_ip_address;
     VAR listen_addresses: array [1 .. * ] of nat$sk_ip_address;
     VAR count: nlt$device_count;
     VAR status: ost$status);

    VAR
      device_id: integer,
      listen_socket: ^nlt$tcp_listen_socket;

    count := 0;
    status.normal := TRUE;
    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

{ Find the listen socket corresponding to the given application name, port
{ number and IP address.

    listen_socket := nlv$tcp_listen_sockets.list;
    WHILE (listen_socket <> NIL) AND ((listen_socket^.application <> application) OR
          (listen_socket^.port <> port) OR (listen_socket^.bound_address <> bound_address)) DO
      listen_socket := listen_socket^.next_entry;
    WHILEND;
    IF listen_socket <> NIL THEN
      nlp$get_nonexclusive_access (listen_socket^.access_control);

    /get_addresses/
      FOR device_id := 1 TO UPPERBOUND (listen_socket^.device_list) DO
        IF (listen_socket^.device_list [device_id].status <> nlc$tcp_device_closed) AND
              (listen_socket^.device_list [device_id].status <> nlc$tcp_device_res_constraint) THEN
          count := count + 1;
          listen_addresses [count] := listen_socket^.device_list [device_id].ip_address;
          IF count = UPPERBOUND (listen_addresses) THEN
            EXIT /get_addresses/; {----->
          IFEND;
        IFEND;
      FOREND /get_addresses/;
      nlp$release_nonexclusive_access (listen_socket^.access_control);
    ELSE { listen_socket = NIL

{ The listen socket is assumed to be terminated via application management.

      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;
    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

  PROCEND nlp$sk_tcp_get_listen_addresses;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_get_socket_status', EJECT ??
*copy nlh$sk_tcp_get_socket_status

  PROCEDURE [XDCL] nlp$sk_tcp_get_socket_status
    (    connection_id: nat$connection_id;
     VAR clear_to_send: boolean;
     VAR data_pending_receive: integer;
     VAR status: ost$status);

    VAR
      capacity: nat$data_length,
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      layer_active: boolean,
      tcp_connection: ^nlt$tcp_socket_layer;

    status.normal := TRUE;
    clear_to_send := FALSE;
    data_pending_receive := 0;
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active THEN
        IF tcp_connection^.state = nlc$tcp_conn_open THEN
          IF (tcp_connection^.receive_queue = NIL) AND (tcp_connection^.received_data <> NIL) THEN
            data_pending_receive := tcp_connection^.received_data^.length;
          IFEND;
          IF tcp_connection^.send_queue = NIL THEN
            nlp$osi_get_outbound_capacity (cl_connection, capacity);
            clear_to_send := (capacity > 0);
          IFEND;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN
          IF (tcp_connection^.receive_queue = NIL) AND (tcp_connection^.received_data <> NIL) THEN
            data_pending_receive := tcp_connection^.received_data^.length;
          ELSE
            IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
              tcp_connection^.state := nlc$tcp_conn_closed;
              nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
            IFEND;
            IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
              osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
            ELSE
              osp$set_status_condition (nae$sk_socket_disconnected, status);
            IFEND;
          IFEND;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
          IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
          ELSE
            osp$set_status_condition (nae$sk_socket_disconnected, status);
          IFEND;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
          osp$set_status_condition (nae$sk_socket_terminated, status);
        IFEND;
      ELSE { layer_active = FALSE

{ The connection is assumed to be terminated via application management.

        osp$set_status_condition (nae$sk_socket_terminated, status);
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL

{ The connection is assumed to be terminated via application management.

      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;

  PROCEND nlp$sk_tcp_get_socket_status;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_initialize', EJECT ??
*copy nlh$sk_tcp_initialize

  PROCEDURE [XDCL] nlp$sk_tcp_initialize;

    VAR
      null_connect_event_processor: nlt$cl_event_processor,
      null_sap_event_processor: nlt$cl_event_processor;

    null_connect_event_processor.layer := nlc$tcp_interface;
    null_sap_event_processor.layer := nlc$tcp_interface;
    nlp$cl_initialize_template (nlc$tcp_interface, nlc$tcp_interface, #SIZE (nlt$tcp_socket_layer),
          {maximum_protocol_header_size =} 0, null_sap_event_processor, nac$nil, null_connect_event_processor,
          nac$nil);
    nlp$tcp_initialize (nlc$tcp_interface, nlc$sk_tcp_conn_event_processor, nlc$sk_tcp_event_processor);

  PROCEND nlp$sk_tcp_initialize;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_offer_socket', EJECT ??
*copy nlh$sk_tcp_offer_socket

  PROCEDURE [XDCL] nlp$sk_tcp_offer_socket
    (    connection_id: nat$connection_id;
     VAR status: ost$status);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      layer_active: boolean,
      tcp_connection: ^nlt$tcp_socket_layer;

    status.normal := TRUE;
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active THEN
        IF tcp_connection^.state = nlc$tcp_conn_open THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            tcp_connection^.state := nlc$tcp_conn_offered;
          ELSE { Io pending
            osp$set_status_condition (nae$sk_io_pending, status);
            osp$append_status_integer (osc$status_parameter_delimiter, tcp_connection^.socket_id, 10, TRUE,
                  status);
          IFEND;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN
          IF (tcp_connection^.receive_queue <> NIL) OR (tcp_connection^.send_queue <> NIL) THEN
            osp$set_status_condition (nae$sk_io_pending, status);
            osp$append_status_integer (osc$status_parameter_delimiter, tcp_connection^.socket_id, 10, TRUE,
                  status);
          ELSE

{ Leave the state unchanged but allow the offer socket request to complete.

          IFEND;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
          IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
          ELSE
            osp$set_status_condition (nae$sk_socket_disconnected, status);
          IFEND;
          osp$append_status_integer (osc$status_parameter_delimiter, tcp_connection^.socket_id, 10, TRUE,
                status);
        ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
          osp$set_status_condition (nae$sk_socket_terminated, status);
        IFEND;
      ELSE { Layer inactive

{ The connection is assumed to be terminated via application management.

        osp$set_status_condition (nae$sk_socket_terminated, status);
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL

{ The connection is assumed to be terminated via application management.

      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;

  PROCEND nlp$sk_tcp_offer_socket;
?? OLDTITLE ??
?? NEWTITLE := 'nlp$sk_tcp_remove_accept_socket', EJECT ??
*copyc nlh$sk_tcp_remove_accept_socket

  PROCEDURE [XDCL] nlp$sk_tcp_remove_accept_socket
    (    application: nat$application_name;
         port: nat$sk_port_number;
         bound_address: nat$sk_ip_address);

    VAR
      current_task_id: ost$global_task_id,
      listen_socket: ^nlt$tcp_listen_socket,
      previous_wait_for_socket: ^^nlt$tcp_wait_for_socket,
      wait_for_socket: ^nlt$tcp_wait_for_socket;

    pmp$get_executing_task_gtid (current_task_id);
    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);
    listen_socket := nlv$tcp_listen_sockets.list;
    WHILE (listen_socket <> NIL) AND ((listen_socket^.application <> application) OR
          (listen_socket^.port <> port) OR (listen_socket^.bound_address <> bound_address)) DO
      listen_socket := listen_socket^.next_entry;
    WHILEND;

    IF listen_socket <> NIL THEN
      nlp$get_exclusive_access (listen_socket^.access_control);
      IF listen_socket^.wait_for_socket_list <> NIL THEN
        previous_wait_for_socket := ^listen_socket^.wait_for_socket_list;
        wait_for_socket := listen_socket^.wait_for_socket_list;
        WHILE (wait_for_socket <> NIL) AND (wait_for_socket^.task_id <> current_task_id) DO
          previous_wait_for_socket := ^wait_for_socket^.next_entry;
          wait_for_socket := wait_for_socket^.next_entry;
        WHILEND;
        IF wait_for_socket <> NIL THEN
          previous_wait_for_socket^ := wait_for_socket^.next_entry;
          FREE wait_for_socket IN nav$network_paged_heap^;
        IFEND;
      IFEND;
      nlp$release_exclusive_access (listen_socket^.access_control);
    IFEND;
    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

  PROCEND nlp$sk_tcp_remove_accept_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_remove_clear_to_send', EJECT ??
*copyc nlh$sk_tcp_remove_clear_to_send

  PROCEDURE [XDCL] nlp$sk_tcp_remove_clear_to_send
    (    connection_id: nat$connection_id);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      current_task_id: ost$global_task_id,
      layer_active: boolean,
      previous_sender_task: ^^nlt$tcp_sender_task,
      sender_task: ^nlt$tcp_sender_task,
      tcp_connection: ^nlt$tcp_socket_layer;

    pmp$get_executing_task_gtid (current_task_id);
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF (layer_active) AND (tcp_connection^.state = nlc$tcp_conn_open) AND
            (tcp_connection^.send_queue <> NIL) THEN
        sender_task := tcp_connection^.send_queue;
        IF sender_task^.task_id = current_task_id THEN

{ Current task queued at the head of the receive queue.

          nlp$cl_deactivate_sender (cl_connection);
          tcp_connection^.send_queue := sender_task^.next_entry;
          nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
        ELSE
          previous_sender_task := ^tcp_connection^.send_queue;
          WHILE (sender_task <> NIL) AND (sender_task^.task_id <> current_task_id) DO
            previous_sender_task := ^sender_task^.next_entry;
            sender_task := sender_task^.next_entry;
          WHILEND;
          IF sender_task <> NIL THEN
            previous_sender_task^ := sender_task^.next_entry;
            nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
          IFEND;
        IFEND;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    IFEND;

  PROCEND nlp$sk_tcp_remove_clear_to_send;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_remove_data_avail', EJECT ??
*copyc nlh$sk_tcp_remove_data_avail

  PROCEDURE [XDCL] nlp$sk_tcp_remove_data_avail
    (    connection_id: nat$connection_id);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      current_task_id: ost$global_task_id,
      ignore_status: ost$status,
      layer_active: boolean,
      previous_receiver_task: ^^nlt$tcp_receiver_task,
      receiver_task: ^nlt$tcp_receiver_task,
      tcp_connection: ^nlt$tcp_socket_layer;

    pmp$get_executing_task_gtid (current_task_id);
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF (layer_active) AND (tcp_connection^.state = nlc$tcp_conn_open) AND
            (tcp_connection^.receive_queue <> NIL) THEN
        previous_receiver_task := ^tcp_connection^.receive_queue;
        receiver_task := tcp_connection^.receive_queue;
        WHILE (receiver_task <> NIL) AND (receiver_task^.task_id <> current_task_id) DO
          previous_receiver_task := ^receiver_task^.next_entry;
          receiver_task := receiver_task^.next_entry;
        WHILEND;
        IF receiver_task <> NIL THEN
          previous_receiver_task^ := receiver_task^.next_entry;
          IF receiver_task^.receiver_active THEN
            nlp$cl_deactivate_receiver (cl_connection);
          IFEND;
          nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
          IF tcp_connection^.receive_queue <> NIL THEN
            pmp$ready_task (tcp_connection^.receive_queue^.task_id, ignore_status);
          IFEND;
        IFEND;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    IFEND;

  PROCEND nlp$sk_tcp_remove_data_avail;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_send_data', EJECT ??
*copy nlh$sk_tcp_send_data

  PROCEDURE [XDCL] nlp$sk_tcp_send_data
    (    cl_connection: ^nlt$cl_connection;
         initial_capacity: nat$data_length;
     VAR data: nat$data_fragments;
         data_length: nat$data_length;
         push_flag: boolean;
         urgent_flag: boolean;
         starting_fragment: nat$data_fragment_count;
     VAR remaining_fragment: nat$data_fragment_count;
     VAR remaining_data_length: integer);

    VAR
      capacity: nat$data_length,
      current_fragment: nat$data_fragment_count,
      fragment: ^nat$data_fragments,
      fragment_size: integer,
      ignore_status: ost$status;

    capacity := initial_capacity;
    IF capacity >= data_length THEN
      nlp$tcp_send_data_fragments (cl_connection, data, push_flag, urgent_flag, ignore_status);
      remaining_data_length := 0;
    ELSE { Insufficient outbound capacity
      PUSH fragment: [1 .. UPPERBOUND (data)];
      current_fragment := starting_fragment;
      remaining_data_length := data_length;
      REPEAT
        fragment_size := capacity;
        nlp$sk_fragment_data (fragment_size, current_fragment, data, remaining_fragment, fragment^);
        nlp$tcp_send_data_fragments (cl_connection, fragment^, push_flag, urgent_flag, ignore_status);
        remaining_data_length := remaining_data_length - fragment_size;
        IF remaining_data_length > 0 THEN
          nlp$osi_get_outbound_capacity (cl_connection, capacity);
          IF capacity > 0 THEN
            IF capacity > remaining_data_length THEN
              capacity := remaining_data_length;
            IFEND;
            current_fragment := remaining_fragment;
          IFEND;
        IFEND;
      UNTIL (capacity = 0) OR (remaining_data_length = 0);
    IFEND;

  PROCEND nlp$sk_tcp_send_data;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_set_socket_options', EJECT ??
*copy nlh$sk_tcp_set_socket_options

  PROCEDURE [XDCL] nlp$sk_tcp_set_socket_options
    (    connection_id: nat$connection_id;
         graceful_close: boolean;
         traffic_pattern: nat$sk_traffic_pattern;
     VAR status: ost$status);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      layer_active: boolean,
      tcp_connection: ^nlt$tcp_socket_layer;

    status.normal := TRUE;
    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active THEN
        IF tcp_connection^.state = nlc$tcp_conn_open THEN

{ Should it by pass all queued senders and receivers?

          nlp$tcp_set_socket_options (cl_connection, graceful_close, traffic_pattern, {ignore} status);
          status.normal := TRUE;
        ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN

{ Do nothing.

        ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
          IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
          ELSE
            osp$set_status_condition (nae$sk_socket_disconnected, status);
          IFEND;
          osp$append_status_integer (osc$status_parameter_delimiter, tcp_connection^.socket_id, 10, TRUE,
                status);
        ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;
          osp$set_status_condition (nae$sk_socket_terminated, status);
        IFEND;
      ELSE { layer_active = FALSE

{ The connection is assumed to be terminated via application management.

        osp$set_status_condition (nae$sk_socket_terminated, status);
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL

{ The connection is assumed to be terminated via application management.

      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;

  PROCEND nlp$sk_tcp_set_socket_options;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_terminate_all_listen', EJECT ??
*copy nlh$sk_tcp_terminate_all_listen

  PROCEDURE [XDCL] nlp$sk_tcp_terminate_all_listen
    (    application: nat$application_name);

    VAR
      done: boolean,
      listen_socket: ^nlt$tcp_listen_socket,
      previous_listen_socket: ^^nlt$tcp_listen_socket;

{ Find all listen sockets corresponding to the given application name.

  /terminate_listen/
    REPEAT
      nlp$get_exclusive_access (nlv$tcp_listen_sockets.access_control);
      listen_socket := nlv$tcp_listen_sockets.list;
      previous_listen_socket := ^nlv$tcp_listen_sockets.list;
      WHILE (listen_socket <> NIL) AND (listen_socket^.application <> application) DO
        previous_listen_socket := ^listen_socket^.next_entry;
        listen_socket := listen_socket^.next_entry;
      WHILEND;
      done := listen_socket = NIL;
      IF listen_socket <> NIL THEN
        previous_listen_socket^ := listen_socket^.next_entry;
        nlp$release_exclusive_access (nlv$tcp_listen_sockets.access_control);
        terminate_listen_socket (listen_socket);
      ELSE
        nlp$release_exclusive_access (nlv$tcp_listen_sockets.access_control);
      IFEND;
    UNTIL done;

  PROCEND nlp$sk_tcp_terminate_all_listen;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_terminate_listen', EJECT ??
*copy nlh$sk_tcp_terminate_listen

  PROCEDURE [XDCL] nlp$sk_tcp_terminate_listen
    (    application: nat$application_name;
         port: nat$sk_port_number;
         bound_address: nat$sk_ip_address);

    VAR
      listen_socket: ^nlt$tcp_listen_socket,
      previous_listen_socket: ^^nlt$tcp_listen_socket;

    nlp$get_exclusive_access (nlv$tcp_listen_sockets.access_control);

{ Find the listen socket corresponding to the given application name, port
{ number and IP address.

    listen_socket := nlv$tcp_listen_sockets.list;
    previous_listen_socket := ^nlv$tcp_listen_sockets.list;
    WHILE (listen_socket <> NIL) AND ((listen_socket^.application <> application) OR
          (listen_socket^.port <> port) OR (listen_socket^.bound_address <> bound_address)) DO
      previous_listen_socket := ^listen_socket^.next_entry;
      listen_socket := listen_socket^.next_entry;
    WHILEND;

{ If listen_socket = NIL, it is assumed that the listen socket must have been terminated
{ via application management.

    IF listen_socket <> NIL THEN

{ Delink the listen socket from the list.

      previous_listen_socket^ := listen_socket^.next_entry;
      nlp$release_exclusive_access (nlv$tcp_listen_sockets.access_control);
      terminate_listen_socket (listen_socket);
    ELSE
      nlp$release_exclusive_access (nlv$tcp_listen_sockets.access_control);
    IFEND;

  PROCEND nlp$sk_tcp_terminate_listen;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$sk_tcp_terminate_socket', EJECT ??
*copy nlh$sk_tcp_terminate_socket

  PROCEDURE [XDCL] nlp$sk_tcp_terminate_socket
    (    connection_id: nat$connection_id);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      ignore_status: ost$status,
      layer_active: boolean,
      tcp_connection: ^nlt$tcp_socket_layer;

    nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
    IF cl_connection <> NIL THEN
      nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
      IF layer_active THEN
        CASE tcp_connection^.state OF
        = nlc$tcp_conn_closed =
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;

        = nlc$tcp_conn_closing =
          IF (tcp_connection^.received_data <> NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            discard_received_data (tcp_connection);
          IFEND;
          tcp_connection^.state := nlc$tcp_conn_terminated;
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;

        = nlc$tcp_conn_open =
          nlp$tcp_release_socket (cl_connection, ignore_status);
          terminate_io (cl_connection, tcp_connection);
          discard_received_data (tcp_connection);
          tcp_connection^.state := nlc$tcp_conn_terminated;
          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
          IFEND;

        = nlc$tcp_conn_await_confirm =
          nlp$tcp_release_socket (cl_connection, ignore_status);
          pmp$ready_task (tcp_connection^.waiting_task_id, ignore_status);
          tcp_connection^.state := nlc$tcp_conn_terminated;
          nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);

        = nlc$tcp_conn_await_accept =
          nlp$tcp_release_socket (cl_connection, ignore_status);
          tcp_connection^.state := nlc$tcp_conn_terminated;
          nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);

        = nlc$tcp_conn_offered =
          nlp$tcp_release_socket (cl_connection, ignore_status);
          tcp_connection^.state := nlc$tcp_conn_terminated;
          nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
        ELSE { unexpected state
          nap$namve_system_error ({Recoverable_error=} TRUE, unexpected_state, NIL);
          tcp_connection^.state := nlc$tcp_conn_terminated;
        CASEND;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    IFEND;

  PROCEND nlp$sk_tcp_terminate_socket;
?? OLDTITLE ??
?? NEWTITLE := 'activate_next_sender', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to scan the send queue and
{ ready the next task queued to send data. All intermediate tasks
{ that are awaitng a clear to send indication and are queued ahead
{ of the task waiting to send data are readied and dequeued.

  PROCEDURE activate_next_sender
    (VAR tcp_connection: ^nlt$tcp_socket_layer);

    VAR
      ignore_status: ost$status,
      previous_sender_task: ^^nlt$tcp_sender_task,
      sender_task: ^nlt$tcp_sender_task;

    sender_task := tcp_connection^.send_queue;
    previous_sender_task := ^tcp_connection^.send_queue;

  /activate_sender/
    WHILE sender_task <> NIL DO
      pmp$ready_task (sender_task^.task_id, ignore_status);
      IF sender_task^.send_type = nlc$tcp_await_clear_to_send THEN
        previous_sender_task^ := sender_task^.next_entry;
        nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
        sender_task := previous_sender_task^;
      ELSE
        EXIT /activate_sender/; {----->
      IFEND;
    WHILEND /activate_sender/;

  PROCEND activate_next_sender;
?? OLDTITLE ??
?? NEWTITLE := 'assign_socket', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to queue the incoming channel connection on the
{   listen socket for the given destination socket address.

  PROCEDURE [INLINE] assign_socket
    (    destination_socket: nat$sk_socket_address;
         source_socket: nat$sk_socket_address;
         connection_id: nat$connection_id;
     VAR status: ost$status);

    VAR
      listen_socket: ^nlt$tcp_listen_socket,
      previous_received_socket: ^^nlt$tcp_received_socket,
      received_socket: ^nlt$tcp_received_socket,
      wait_for_socket: ^nlt$tcp_wait_for_socket;

    status.normal := TRUE;
    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

{ Find the listen socket corresponding to the given destination socket address.

    listen_socket := nlv$tcp_listen_sockets.list;

  /find_listen_socket/
    WHILE listen_socket <> NIL DO
      IF listen_socket^.port = destination_socket.port THEN
        IF (listen_socket^.bound_address = nac$sk_all_ip_addresses) OR
              (listen_socket^.bound_address = destination_socket.ip_address) THEN
          EXIT /find_listen_socket/; {----->
        IFEND;
      IFEND;
      listen_socket := listen_socket^.next_entry;
    WHILEND /find_listen_socket/;

    IF listen_socket <> NIL THEN
      nlp$get_exclusive_access (listen_socket^.access_control);

{ Increment application management count of maximum sockets.

      nlp$tcpip_increment_appl_access (listen_socket^.application, {assigned} FALSE,
            nlc$udp_null_global_socket_id, connection_id, status);
      IF status.normal THEN
        REPEAT
          ALLOCATE received_socket IN nav$network_paged_heap^;
          IF received_socket = NIL THEN
            syp$cycle;
          IFEND;
        UNTIL received_socket <> NIL;
        received_socket^.next_entry := NIL;
        received_socket^.destination_socket := destination_socket;
        received_socket^.source_socket := source_socket;
        received_socket^.connected := TRUE;
        received_socket^.connection_id := connection_id;

{ Queue the received socket at the end of the received sockets queue.

        previous_received_socket := ^listen_socket^.received_sockets;
        WHILE previous_received_socket^ <> NIL DO
          previous_received_socket := ^previous_received_socket^^.next_entry;
        WHILEND;
        previous_received_socket^ := received_socket;

{ Ready a waiting task.  Dequeue the task from the wait for socket list
{ so another task will be readied if another connect request is received.

        IF listen_socket^.wait_for_socket_list <> NIL THEN
          wait_for_socket := listen_socket^.wait_for_socket_list;
          listen_socket^.wait_for_socket_list := wait_for_socket^.next_entry;
          pmp$ready_task (wait_for_socket^.task_id, {ignore} status);
          status.normal := TRUE;
          FREE wait_for_socket IN nav$network_paged_heap^;
        IFEND;
      IFEND;
      nlp$release_exclusive_access (listen_socket^.access_control);
    ELSE { listen_socket = NIL
      osp$set_status_condition (nae$sk_socket_terminated, status);
    IFEND;
    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

  PROCEND assign_socket;
?? OLDTITLE ??
?? NEWTITLE := 'continue_to_receive', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to deliver data to the task at the
{   head of the receive queue. If receive is complete, the receiver is
{   deactivated and the next receiver is readied.

  PROCEDURE continue_to_receive
    (    cl_connection: ^nlt$cl_connection;
     VAR tcp_connection: ^nlt$tcp_socket_layer;
     VAR data { input, output } : nlt$bm_message_id;
     VAR data_length { input, output } : integer;
         push_flag: boolean;
         urgent_flag: boolean;
     VAR buffers_freed: nat$data_length);

?? NEWTITLE := 'terminate_receive', EJECT ??

    PROCEDURE terminate_receive
      (    condition: pmt$condition;
           ignore_condition_descriptor: ^pmt$condition_information;
           sa: ^ost$stack_frame_save_area;
       VAR condition_status: ost$status);

      nap$condition_handler_trace (condition, sa);
      CASE condition.selector OF
      = pmc$system_conditions, mmc$segment_access_condition =
        IF receiver_task <> NIL THEN

{ Mark receive complete.

          receiver_task^.activity_status^.complete := TRUE;
          osp$set_status_from_condition (nac$status_id, condition, sa, receiver_task^.activity_status^.status,
                condition_status);

{ Dequeue the receiver task.

          tcp_connection^.receive_queue := receiver_task^.next_entry;
          nlp$cl_deactivate_receiver (cl_connection);
          IF tcp_connection^.receive_queue <> NIL THEN
            pmp$ready_task (tcp_connection^.receive_queue^.task_id, ignore_status);
          IFEND;
        IFEND;

        EXIT continue_to_receive; {----->
      = pmc$user_defined_condition =
        IF condition.user_condition_name = osc$job_recovery_condition_name THEN
          pmp$continue_to_cause (pmc$execute_standard_procedure, condition_status);
        IFEND;
        condition_status.normal := TRUE;
      ELSE
        condition_status.normal := TRUE;
      CASEND;

    PROCEND terminate_receive;
?? OLDTITLE ??
?? EJECT ??

    VAR
      delivered_data_length: integer,
      ignore_status: ost$status,
      receiver_task: ^nlt$tcp_receiver_task;

    buffers_freed := 0;
    osp$establish_condition_handler (^terminate_receive, FALSE);
    receiver_task := tcp_connection^.receive_queue;
    #SPOIL (receiver_task);

{ Deliver the data to the users buffer.

    nlp$bm_deliver_message (receiver_task^.data_buffer^, data, delivered_data_length, buffers_freed);
    receiver_task^.received_data_length^ := receiver_task^.received_data_length^ + delivered_data_length;
    receiver_task^.urgent_flag^ := urgent_flag;
    receiver_task^.remaining_buffer_capacity := receiver_task^.remaining_buffer_capacity -
          delivered_data_length;
    data_length := data_length - delivered_data_length;
    IF (receiver_task^.remaining_buffer_capacity = 0) OR (push_flag) OR (urgent_flag) THEN

{ Receive is complete, dequeue receiver task.

      tcp_connection^.receive_queue := receiver_task^.next_entry;
      receiver_task^.activity_status^.complete := TRUE;
      nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
      nlp$cl_deactivate_receiver (cl_connection);

{ Ready the next task in the receive queue.

      IF tcp_connection^.receive_queue <> NIL THEN
        pmp$ready_task (tcp_connection^.receive_queue^.task_id, ignore_status);
      IFEND;
    IFEND;

  PROCEND continue_to_receive;
?? OLDTITLE ??
?? NEWTITLE := 'discard_received_data', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to discard all
{ received data queued on the TCP socket layer.

  PROCEDURE discard_received_data
    (VAR tcp_connection: ^nlt$tcp_socket_layer);

    VAR
      next_received_data: ^nlt$tcp_received_data,
      received_data: ^nlt$tcp_received_data;

    received_data := tcp_connection^.received_data;
    WHILE received_data <> NIL DO
      nlp$bm_release_message (received_data^.message_id);
      next_received_data := received_data^.next_entry;
      nlp$sk_tcp_ret_rec_data_entry (tcp_connection, received_data);
      received_data := next_received_data;
    WHILEND;
    tcp_connection^.received_data := NIL;
    tcp_connection^.inventory_report := 0;

  PROCEND discard_received_data;
?? OLDTITLE ??
?? NEWTITLE := 'disconnect_unaccepted_socket', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to find the accept socket queued on the corresponding
{   listen socket for the given channel connection and to mark i as disconnected. The accept
{   process will recognize the disconnected socket via the 'connected' state variable.

  PROCEDURE disconnect_unaccepted_socket
    (    cl_connection: ^nlt$cl_connection;
         tcp_connection: ^nlt$tcp_socket_layer;
         release_reason: nlt$tcpaa_release_ind_reason);

    VAR
      ignore_status: ost$status,
      listen_socket: ^nlt$tcp_listen_socket,
      received_socket: ^nlt$tcp_received_socket;

    nlp$get_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

{ Find the listen socket corresponding to the given destination socket address.

    listen_socket := nlv$tcp_listen_sockets.list;

  /find_listen_socket/
    WHILE listen_socket <> NIL DO
      IF listen_socket^.port = tcp_connection^.destination_socket.port THEN
        IF (listen_socket^.bound_address = nac$sk_all_ip_addresses) OR
              (listen_socket^.bound_address = tcp_connection^.destination_socket.ip_address) THEN
          EXIT /find_listen_socket/; {----->
        IFEND;
      IFEND;
      listen_socket := listen_socket^.next_entry;
    WHILEND /find_listen_socket/;

    IF listen_socket <> NIL THEN
      nlp$get_exclusive_access (listen_socket^.access_control);
      received_socket := listen_socket^.received_sockets;
      WHILE (received_socket <> NIL) AND (received_socket^.connection_id <> cl_connection^.identifier) DO
        received_socket := received_socket^.next_entry;
      WHILEND;
      IF received_socket <> NIL THEN
        received_socket^.connection_id := nac$null_connection_id;
        received_socket^.connected := FALSE;
        received_socket^.release_reason := release_reason;
        nlp$tcpip_decrement_appl_access (listen_socket^.application, nlc$udp_null_global_socket_id,
              cl_connection^.identifier, ignore_status);
      IFEND;
      nlp$release_exclusive_access (listen_socket^.access_control);
    IFEND;
    nlp$release_nonexclusive_access (nlv$tcp_listen_sockets.access_control);

    tcp_connection^.state := nlc$tcp_conn_closed;
    nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);

  PROCEND disconnect_unaccepted_socket;
?? OLDTITLE ??
?? NEWTITLE := 'issue_disconnect', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to send a disconnect to the TCP access provider.

  PROCEDURE issue_disconnect
    (    cl_connection: ^nlt$cl_connection;
     VAR tcp_connection: ^nlt$tcp_socket_layer);

    VAR
      ignore_status: ost$status;

    IF (tcp_connection^.state <> nlc$tcp_conn_closed) AND
          (tcp_connection^.state <> nlc$tcp_conn_terminated) AND
          (tcp_connection^.state <> nlc$tcp_conn_closing) THEN
      nlp$tcp_release_socket (cl_connection, ignore_status);
      terminate_io (cl_connection, tcp_connection);

      IF tcp_connection^.receive_queue = NIL THEN
        discard_received_data (tcp_connection);
      IFEND;
      tcp_connection^.state := nlc$tcp_conn_closed;
    IFEND;

  PROCEND issue_disconnect;
?? OLDTITLE ??
?? NEWTITLE := 'terminate_io', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to cause all senders and
{ receivers to terminate. All tasks awaiting data or the clear
{ to send indication are readied and dequeued. All tasks waiting
{ to send or receive data are readied so they can terminate.

  PROCEDURE terminate_io
    (    cl_connection: ^nlt$cl_connection;
     VAR tcp_connection: ^nlt$tcp_socket_layer);

    VAR
      ignore_status: ost$status,
      previous_receiver_task: ^^nlt$tcp_receiver_task,
      previous_sender_task: ^^nlt$tcp_sender_task,
      receiver_task: ^nlt$tcp_receiver_task,
      sender_task: ^nlt$tcp_sender_task;

    IF tcp_connection^.send_queue <> NIL THEN
      sender_task := tcp_connection^.send_queue;
      previous_sender_task := ^tcp_connection^.send_queue;
      WHILE sender_task <> NIL DO
        pmp$ready_task (sender_task^.task_id, ignore_status);
        IF sender_task^.send_type = nlc$tcp_await_clear_to_send THEN
          previous_sender_task^ := sender_task^.next_entry;
          FREE sender_task IN nav$network_paged_heap^;
          sender_task := previous_sender_task^;
        ELSE
          previous_sender_task := ^sender_task^.next_entry;
          sender_task := sender_task^.next_entry;
        IFEND;
      WHILEND;
    IFEND;
    IF tcp_connection^.receive_queue <> NIL THEN
      receiver_task := tcp_connection^.receive_queue;
      previous_receiver_task := ^tcp_connection^.receive_queue;
      WHILE receiver_task <> NIL DO
        pmp$ready_task (receiver_task^.task_id, ignore_status);
        IF receiver_task^.receive_type = nlc$tcp_await_data_available THEN
          previous_receiver_task^ := receiver_task^.next_entry;
          FREE receiver_task IN nav$network_paged_heap^;
          receiver_task := previous_receiver_task^;
        ELSE
          previous_receiver_task := ^receiver_task^.next_entry;
          receiver_task := receiver_task^.next_entry;
        IFEND;
      WHILEND;
    IFEND;

  PROCEND terminate_io;
?? OLDTITLE ??
?? NEWTITLE := 'terminate_listen', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to disconnect the
{ channel connections associated with the given listen socket.
{ The listen socket is freed.

  PROCEDURE terminate_listen_socket
    (VAR listen_socket: ^nlt$tcp_listen_socket);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      device_id: integer,
      layer_active: boolean,
      ignore_status: ost$status,
      next_received_socket: ^nlt$tcp_received_socket,
      next_wait_for_socket: ^nlt$tcp_wait_for_socket,
      received_socket: ^nlt$tcp_received_socket,
      tcp_connection: ^nlt$tcp_socket_layer,
      wait_for_socket: ^nlt$tcp_wait_for_socket;

{ Free all received socket and wait for socket list entries.
{ The connection associated with the received socket is terminated.

    received_socket := listen_socket^.received_sockets;
    WHILE received_socket <> NIL DO
      IF received_socket^.connected THEN
        nlp$sk_tcp_terminate_socket (received_socket^.connection_id);
        nlp$tcpip_decrement_appl_access (listen_socket^.application, nlc$udp_null_global_socket_id,
              received_socket^.connection_id, ignore_status);
      IFEND;
      next_received_socket := received_socket^.next_entry;
      FREE received_socket IN nav$network_paged_heap^;
      received_socket := next_received_socket;
    WHILEND;

{ Ready all tasks waiting for a socket and dequeue the entries.

    wait_for_socket := listen_socket^.wait_for_socket_list;
    WHILE wait_for_socket <> NIL DO
      pmp$ready_task (wait_for_socket^.task_id, ignore_status);
      next_wait_for_socket := wait_for_socket^.next_entry;
      FREE wait_for_socket IN nav$network_paged_heap^;
      wait_for_socket := next_wait_for_socket;
    WHILEND;

    FOR device_id := 1 TO UPPERBOUND (listen_socket^.device_list) DO
      IF (listen_socket^.device_list [device_id].status <> nlc$tcp_device_closed) AND
            (listen_socket^.device_list [device_id].status <> nlc$tcp_device_res_constraint) THEN
        nlp$cl_get_exclusive_via_cid (listen_socket^.device_list [device_id].connection_id, connection_exists,
              cl_connection);
        IF cl_connection <> NIL THEN
          nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
          IF layer_active THEN
            IF tcp_connection^.state <> nlc$tcp_conn_closed THEN
              nlp$tcp_release_socket (cl_connection, ignore_status);
              tcp_connection^.state := nlc$tcp_conn_closed;
            IFEND;
            nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
          IFEND;
          nlp$cl_release_exclusive_access (cl_connection);
        IFEND;
      IFEND;
    FOREND;

    FREE listen_socket IN nav$network_paged_heap^;

  PROCEND terminate_listen_socket;
?? OLDTITLE ??
MODEND nlm$sk_tcp_socket_layer;

