?? RIGHT := 110 ??
?? NEWTITLE := ' NOS/VE FIle Server: queue_entry_control', EJECT ??
MODULE dfm$queue_entry_control;
{
{  This module contains code to control the assignment and releasing of
{  queue entries. Also included is code to queue a request.
{  This module resides both in monitor and task services.
{
?? NEWTITLE := '    Global Declarations', EJECT ??
*copyc dfc$poll_constants
*copyc dft$request_buffer
?? PUSH (LISTEXT := ON) ??
*copyc dfd$driver_queue_types
*copyc dfk$keypoints
*copyc dft$assign_queue_entry_status
*copyc dft$cpu_queue
*copyc dft$inquiry_message
*copyc dft$queue_index
*copyc dft$queue_request_status
*copyc dft$release_queue_entry_status
*copyc dft$transaction_state
*copyc dfv$false_queue_entry_flags
*copyc dfv$file_server_debug_enabled
*copyc dfv$null_global_task_id
*copyc dfv$null_request_buffer_entry
*copyc i#program_error
*copyc osd$virtual_address
*copyc osv$external_interrupt_selector
*copyc osp$fetch_locked_variable
*copyc pmp$zero_out_table
?? POP ??
?? TITLE := ' Key INLINE procedures ', EJECT ??
*copyc dfp$assign_entry
*copyc dfp$free_entry_assignment
*copyc dfp$test_driver
?? TITLE := ' Global Declarations ', EJECT ??

  VAR
    dmv$external_interrupt_selector: [XREF] 0 .. 0ff(16);


?? TITLE := ' [XDCL] dfp$assign_queue_entry ', EJECT ??
*copyc dfh$assign_queue_entry

  PROCEDURE [XDCL] dfp$assign_queue_entry
    (    p_queue_interface_table: dft$p_queue_interface_table;
         queue_index: dft$queue_index;
         queue_entry_type: dft$queue_entry_type;
     VAR queue_entry_index: dft$queue_entry_index;
     VAR assign_status: dft$assign_queue_entry_status);

{ NOTE: It is assummed that prior to the execution of this procedure
{       osp$begin_system_activity will have been executed.

    VAR
      entry_found: integer,
      p_cpu_queue: ^dft$cpu_queue;

    p_cpu_queue := p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].
        p_cpu_queue;
    #KEYPOINT (osk$entry, osk$m * queue_index, dfk$assign_queue_entry);
    { Trap code
    IF dfv$file_server_debug_enabled THEN
      IF (p_queue_interface_table = NIL) OR (queue_index > p_queue_interface_table^.queue_directory.
            number_of_queues) THEN
        i#program_error;
        RETURN;
      IFEND;
    IFEND; { End of Trap code.

   IF (p_cpu_queue^.queue_header.partner_status.server_state = dfc$terminated) OR
      (p_cpu_queue^.queue_header.partner_status.server_state = dfc$inactive) OR
     (p_cpu_queue^.queue_header.partner_status.server_state = dfc$awaiting_recovery) THEN
   { It is assumed that the poller does not assign its queue entry index via here
      assign_status := dfc$aqes_server_terminated;
      #KEYPOINT (osk$exit, 0, dfk$assign_queue_entry);
      RETURN;
   IFEND;

    assign_status := dfc$aqes_no_available_entries;
    IF queue_entry_type = dfc$monitor THEN
      dfp$assign_entry ({starting_position = } dfc$poll_queue_index + 1,
            p_cpu_queue^.queue_header.number_of_monitor_queue_entries,
            p_cpu_queue^.
            queue_header.queue_entry_assignment_table, entry_found);
    ELSE { task services }
      dfp$assign_entry ({starting_position = }
      (p_cpu_queue^.
            queue_header.number_of_monitor_queue_entries + dfc$poll_queue_index + 1),
            p_cpu_queue^.
            queue_header.number_of_task_queue_entries,
            p_cpu_queue^.queue_header.queue_entry_assignment_table,
            entry_found);
    IFEND;
    IF entry_found > 0 THEN
      queue_entry_index := entry_found;

      { Trap code
      IF dfv$file_server_debug_enabled THEN
        IF (p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
            p_driver_queue^.queue_entries [queue_entry_index].flags.driver_action) THEN
          i#program_error;
        IFEND;
        IF p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
           p_driver_queue^.queue_entries [queue_entry_index].flags.active_entry THEN
          i#program_error;
        IFEND;
      IFEND; { End of Trap code.

      assign_status := dfc$aqes_entry_assigned;
      p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
            queue_entries [queue_entry_index].flags.active_entry := TRUE;
{     Only client side tasks use dfp$assign_queue_entry to get a queue entry.
      p_cpu_queue^.
            queue_entries [queue_entry_index].transaction_state := dfc$queue_entry_assigned;
    IFEND;
    #KEYPOINT (osk$exit, osk$m * queue_entry_index, dfk$assign_queue_entry);
  PROCEND dfp$assign_queue_entry;
?? TITLE := ' [XDCL] dfp$queue_inquiry_request ', EJECT ??
*copyc dfh$queue_inquiry_request

  PROCEDURE [XDCL] dfp$queue_inquiry_request
    (    p_queue_interface_table: dft$p_queue_interface_table;
         queue_index: dft$queue_index;
         queue_entry_index: dft$queue_entry_index;
         inquiry_message: dft$inquiry_message;
     VAR queue_request_status: dft$queue_request_status);

{ The Inquiry request provides a means by which the file server may correspond
{ between client and server side in regard to the status of a task. The driver
{ processes inquiry requests without regard to the driver queue entry.

    VAR
      request_buffer_entry: dft$request_buffer_entry;


    #KEYPOINT (osk$entry, osk$m * queue_entry_index, dfk$queue_inquiry_request);
    { Trap code
    IF dfv$file_server_debug_enabled THEN
      IF (p_queue_interface_table = NIL) OR (queue_index > p_queue_interface_table^.queue_directory.
          number_of_queues) THEN
        i#program_error;
      IFEND;
      IF (queue_entry_index <= 0) OR (queue_entry_index > p_queue_interface_table^.queue_directory.
          driver_queue_pva_directory [queue_index].p_driver_queue^.queue_header.number_of_queue_entries) THEN
        i#program_error;
      IFEND;
      IF NOT p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
         p_driver_queue^.queue_entries [queue_entry_index].flags.active_entry THEN
        i#program_error;
      IFEND;
    IFEND; { End of Trap code.

    request_buffer_entry := dfv$null_request_buffer_entry;
    request_buffer_entry.flags.inquiry := TRUE;
    request_buffer_entry.inquiry_message := inquiry_message;
    request_buffer_entry.queue_index := queue_index;
    request_buffer_entry.queue_entry_index := queue_entry_index;
    store_request_buffer_entry ( p_queue_interface_table, queue_index, queue_entry_index,
         request_buffer_entry, TRUE {INQUIRY}, queue_request_status);
    { Queueing Inquiry request does not affect transaction state.

    CASE queue_request_status OF
    = dfc$qrs_entry_queued =
      #KEYPOINT (osk$exit, osk$m * queue_entry_index, dfk$queue_inquiry_request);

    ELSE
      #KEYPOINT (osk$exit, 0, dfk$queue_inquiry_request);
    CASEND;


  PROCEND dfp$queue_inquiry_request;
?? TITLE := ' [XDCL] dfp$queue_request ', EJECT ??
*copyc dfh$queue_request

  PROCEDURE [XDCL] dfp$queue_request
    (    p_queue_interface_table: dft$p_queue_interface_table;
         queue_index: dft$queue_index;
         queue_entry_index: dft$queue_entry_index;
     VAR queue_request_status: dft$queue_request_status);

    VAR
      copied_queue_entry_flags: dft$queue_entry_flags,
      request_buffer_entry: dft$request_buffer_entry;


    #KEYPOINT (osk$entry, osk$m * queue_entry_index, dfk$queue_request);
    { Trap code
    IF dfv$file_server_debug_enabled THEN
      IF (p_queue_interface_table = NIL) OR (queue_index > p_queue_interface_table^.queue_directory.
          number_of_queues) THEN
        i#program_error;
      IFEND;
      IF (queue_entry_index <= 0) OR (queue_entry_index > p_queue_interface_table^.queue_directory.
          driver_queue_pva_directory [queue_index].p_driver_queue^.queue_header.number_of_queue_entries) THEN
        i#program_error;
      IFEND;
      IF NOT p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
         p_driver_queue^.queue_entries [queue_entry_index].flags.active_entry THEN
        i#program_error;
      IFEND;
      IF (NOT p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
          p_driver_queue^.queue_header.connection_descriptor.source.flags.server_to_client) AND
          (p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
          queue_header.queue_entry_assignment_table (queue_entry_index) = dfc$free_entry_char) THEN
        { Client to Server queue, and entry free.
        i#program_error;
      IFEND;
      IF (NOT p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
          p_driver_queue^.queue_entries [queue_entry_index].flags.driver_action) THEN
        i#program_error;
      IFEND;
      IF p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
         p_driver_queue^.queue_entries [queue_entry_index].flags.send_ready_for_data THEN
        IF (p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
           p_driver_queue^.queue_entries [queue_entry_index].flags.send_command)
           OR (p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
           p_driver_queue^.queue_entries [queue_entry_index].flags.send_data) THEN
          i#program_error;
        IFEND;
      IFEND;
    IFEND; { End of Trap code.

    IF dmv$external_interrupt_selector = 1 THEN
      p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
            queue_header.interrupt.interrupt.value := TRUE;
      p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
            queue_header.interrupt.interrupt.port_number := osv$external_interrupt_selector;
    ELSE
      p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
            queue_header.interrupt.interrupt.value := FALSE;
    IFEND;
    copied_queue_entry_flags := p_queue_interface_table^.queue_directory.
          driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].flags;
    request_buffer_entry := dfv$null_request_buffer_entry;
    request_buffer_entry.queue_index := queue_index;
    request_buffer_entry.queue_entry_index := queue_entry_index;
    store_request_buffer_entry ( p_queue_interface_table, queue_index, queue_entry_index,
         request_buffer_entry, FALSE {NOT INQUIRY}, queue_request_status);

    IF (copied_queue_entry_flags.send_command) AND (queue_request_status = dfc$qrs_entry_queued) THEN
      { Save request/response driver queue entry command flags in cpu queue entry.
      p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
            queue_entries [queue_entry_index].copied_queue_entry_flags := copied_queue_entry_flags;

      IF  p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
            p_driver_queue^.queue_header.connection_descriptor.source.flags.server_to_client THEN
        { SERVER SENDING TO CLIENT.
        { RESPONSE TO CLIENT - save response command flags, and reset server transaction state.
        p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
              queue_entries [queue_entry_index].transaction_state := dfc$server_waiting_request;
      ELSE
        { CLIENT SENDING TO SERVER.
        { INITIAL SEND REQUEST - save flags for possible retransmission, and advance state.
        p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
              queue_entries [queue_entry_index].transaction_state := dfc$request_queued;
      IFEND;
    IFEND;

    CASE queue_request_status OF
    = dfc$qrs_entry_queued =
      #KEYPOINT (osk$exit, osk$m * queue_entry_index, dfk$queue_request);

    ELSE
      #KEYPOINT (osk$exit, 0, dfk$queue_request);
    CASEND;

  PROCEND dfp$queue_request;
?? TITLE := '  [XDCL] dfp$release_queue_entry ', EJECT ??
*copyc dfh$release_queue_entry

  PROCEDURE [XDCL] dfp$release_queue_entry
    (    p_queue_interface_table: dft$p_queue_interface_table;
         queue_index: dft$queue_index;
         queue_entry_index: dft$queue_entry_index;
     VAR release_status: dft$release_queue_entry_status);


    #KEYPOINT (osk$entry, osk$m * queue_index, dfk$release_queue_entry);
    { Trap code
    IF dfv$file_server_debug_enabled THEN
      IF queue_index > p_queue_interface_table^.queue_directory.number_of_queues THEN
        i#program_error;
      IFEND;
      IF queue_entry_index > p_queue_interface_table^.queue_directory.
         driver_queue_pva_directory [queue_index].p_driver_queue^.queue_header.number_of_queue_entries THEN
        i#program_error;
      IFEND;
      IF p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
          queue_header.queue_entry_assignment_table (queue_entry_index) = dfc$free_entry_char THEN
        i#program_error;
      IFEND;
      IF (p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].flags.driver_action) THEN
        i#program_error;
      IFEND;
      IF p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
         p_driver_queue^.queue_entries [queue_entry_index].held_over_esm_division_number <> 0 THEN
        i#program_error;
      IFEND;
    IFEND; { End of Trap code.

    p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].flags := dfv$false_queue_entry_flags;
    p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].error_condition := 0;
    p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].held_over_cm_word_count := 0;
    p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].held_over_esm_division_number := 0;
    p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].p_driver_queue^.
          queue_entries [queue_entry_index].data_descriptor.actual_length := 0;
    p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
          queue_entries [queue_entry_index].transaction_state := dfc$queue_entry_available;
    p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
          queue_entries [queue_entry_index].request_timeout_count := 0;
    p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
          queue_entries [queue_entry_index].retransmission_count := 0;
    p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].p_cpu_queue^.
          queue_entries [queue_entry_index].global_task_id := dfv$null_global_task_id;


    release_status := dfc$rqes_entry_released;
    dfp$free_entry_assignment (queue_entry_index, p_queue_interface_table^.queue_directory.
          cpu_queue_pva_directory [queue_index].p_cpu_queue^.queue_header.queue_entry_assignment_table);
    #KEYPOINT (osk$exit, osk$m * queue_entry_index, dfk$release_queue_entry);
  PROCEND dfp$release_queue_entry;
?? TITLE := ' STORE_REQUEST_BUFFER_ENTRY [INLINE] ', EJECT ??

  PROCEDURE [INLINE] store_request_buffer_entry
    (    p_queue_interface_table: dft$p_queue_interface_table;
         queue_index: dft$queue_index;
         queue_entry_index: dft$queue_entry_index;
         request_buffer_entry: dft$request_buffer_entry;
         inquiry: boolean;
     VAR queue_request_status: dft$queue_request_status);
{
{  This process makes the request visable to the driver by obtaining an entry
{ in the request_buffer and storing the request pointers into the entry.
{ Since more than one copy of this program is executing the request_buffer
{ in_offset must be locked when incremented.  Note that the request_buffer is
{ a circular buffer with IN , OUT, and LIMIT offsets.  This process updates the
{ IN offset to point to the next available entry if an entry is assigned.
{ The file server driver updates the OUT offset when an entry is processed.

    VAR
      actual: integer,
      initial_in: integer,
      new_in: integer,
      out: integer,
      p_cpu_queue: ^dft$cpu_queue,
      request_buffer_entry_index: 1 .. dfc$max_request_buffer_entries,
      result: 0 .. 2;

    CONST
      swap_successful = 0,
      swap_failed = 2;

    p_cpu_queue := p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].
        p_cpu_queue;
    IF ((p_cpu_queue^.queue_header.partner_status.server_state = dfc$terminated) OR
       (p_cpu_queue^.queue_header.partner_status.server_state = dfc$inactive) OR
       (p_cpu_queue^.queue_header.partner_status.server_state = dfc$awaiting_recovery)) AND
         (NOT p_queue_interface_table^.queue_directory.driver_queue_pva_directory [queue_index].
         p_driver_queue^.queue_header.connection_descriptor.source.flags.server_to_client)
         AND (queue_entry_index <> dfc$poll_queue_index) THEN   {not poller
      queue_request_status := dfc$qrs_server_terminated;
      RETURN;
    IFEND;

    result := swap_failed;

  /get_request_buffer_entry/
    REPEAT
      osp$fetch_locked_variable (p_queue_interface_table^.request_buffer_directory.inn, initial_in);
      new_in := initial_in + 8;
      #SPOIL (p_queue_interface_table^.request_buffer_directory.limit);
      IF new_in >= p_queue_interface_table^.request_buffer_directory.limit THEN
        new_in := 0;
      IFEND;
      #SPOIL (p_queue_interface_table^.request_buffer_directory.out);
      IF new_in = p_queue_interface_table^.request_buffer_directory.out THEN
        queue_request_status := dfc$qrs_request_buffer_full;
        RETURN;
      ELSE
        IF NOT inquiry THEN
          p_cpu_queue^.queue_entries [queue_entry_index].request_start_time := #FREE_RUNNING_CLOCK (0);
          p_cpu_queue^.queue_entries [queue_entry_index].last_time_progress_checked := 0;
          p_cpu_queue^.queue_entries [queue_entry_index].request_timeout_count := 0;
        IFEND;
        request_buffer_entry_index := (initial_in DIV 8) + 1;
        #COMPARE_SWAP (p_queue_interface_table^.request_buffer_directory.inn, initial_in, new_in, actual,
              result);
      IFEND;
    UNTIL result = swap_successful;

    { The request buffer entry must be initialized with ONE assignment
    { since the PP is expecting a consistent entry.
    { Note: The interval between the compare_swap and the assignement should
    {   be as small as possible since the request buffer is frozen till the
    {   entry is queued.
    p_queue_interface_table^.request_buffer_directory.p_request_buffer^.
          request_buffer_entries [request_buffer_entry_index] := request_buffer_entry;
    #SPOIL (p_queue_interface_table^.request_buffer_directory.p_request_buffer^.
          request_buffer_entries [request_buffer_entry_index]);
    queue_request_status := dfc$qrs_entry_queued;
    IF (#RING (p_queue_interface_table) = osc$user_ring) AND
          (p_cpu_queue^.queue_header.connection_type = dfc$mock_connection) THEN
      dfp$test_driver (p_queue_interface_table);
    IFEND;

  PROCEND store_request_buffer_entry;

MODEND dfm$queue_entry_control;
