?? RIGHT := 110 ??
MODULE mmm$read_write_io_ring_3;

{ This module contains the ring 3 procedures for processing
{ user read/write requests.

?? PUSH (LISTEXT := ON) ??
*copyc ioe$st_errors
*copyc mme$condition_codes
*copyc mmt$io_control_block
*copyc mmt$io_status
*copyc mmt$iocb_index
*copyc mmt$rb_memory_manager_io
*copyc osd$virtual_address
*copyc ost$execution_control_block
*copyc ost$status
*copyc syc$monitor_request_codes
?? POP ??

{External procedures used by this module.

*copyc i#call_monitor
*copyc i#move
*copyc mmp$allocate_iocb_r1
*copyc mmp$read
*copyc mmp$reallocate_file_space
*copyc mmp$update_iocb_completions
*copyc mmp$write
*copyc osp$set_status_abnormal
*copyc osp$set_status_from_mtr_status
*copyc osp$verify_system_privilege
*copyc pmp$find_executing_task_xcb
*copyc pmp$wait

  VAR
    last_update_time: [STATIC] ost$free_running_clock := 0;

?? TITLE := '[XDCL, #GATE] mmp$check_io_completions' ??
?? EJECT ??
*copyc mmh$check_io_completions

  PROCEDURE [XDCL, #GATE] mmp$check_io_completions
    (    timestamp: ost$free_running_clock;
         wait_time: integer;
     VAR status: ost$status);

    VAR
      completion_table: mmt$iocb_table_array,
      i: integer,
      io_active: boolean,
      iocb_ptr: ^mmt$io_control_block,
      iocb_table: mmt$iocb_table_array,
      latest_completion_time: integer,
      rb: mmt$rb_memory_manager_io,
      wait_opt: ost$wait,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;

    pmp$find_executing_task_xcb (xcb_p);
    iocb_ptr := xcb_p^.iocb_p;
    IF iocb_ptr = NIL THEN
      osp$set_status_abnormal ('MM', mme$no_io_active, ' ', status);
      RETURN;
    IFEND;

    latest_completion_time := iocb_ptr^.latest_completion_time;
    IF latest_completion_time = last_update_time THEN
      IF (wait_time = 0) OR (latest_completion_time > timestamp) THEN
        RETURN;
      ELSE

{ Verify there is io active

        io_active := FALSE;
        i#move (#LOC (iocb_ptr^.iocb_table), ^completion_table, #SIZE (mmt$iocb_table_entry) *
              iocb_ptr^.maximum_iocb_index_in_use);
        /check_io_active/
        FOR i := LOWERBOUND (completion_table) TO iocb_ptr^.maximum_iocb_index_in_use DO
          IF (completion_table [i].active_io_count > 0) AND
                (completion_table [i].used_for_asynchronous_io) THEN
            io_active := TRUE;
            EXIT /check_io_active/;
          IFEND;
        FOREND /check_io_active/;

{ Issue to request to wait for io, if io is active.

        IF io_active THEN
          rb.reqcode := syc$rc_memory_manager_io;
          rb.status.normal := TRUE;
          rb.sub_reqcode := mmc$iorc_await_io_completion;
          rb.latest_completion_time := latest_completion_time;
          rb.wait_time := wait_time;
          i#call_monitor (#LOC (rb), #SIZE (rb));

        ELSEIF latest_completion_time = iocb_ptr^.latest_completion_time THEN

{ If the timestamps no longer match, io completed since the local copy of latest_completion_time was
{ read; process completions.  If the timestamps match, the caller is confused; there are no active io
{ requests.

          osp$set_status_abnormal ('MM', mme$no_io_active, ' ', status);
          RETURN;
        IFEND;
      IFEND;
    IFEND;

    IF wait_time > 0 THEN
      wait_opt := osc$wait;
    ELSE
      wait_opt := osc$nowait;
    IFEND;

    mmp$process_io_completions (iocb_ptr, wait_opt, latest_completion_time, status);

  PROCEND mmp$check_io_completions;


?? TITLE := 'mmp$check_io_status' ??
?? EJECT ??
{--------------------------------------------------------------------------------------------------------
*copyc mmh$check_io_status
{--------------------------------------------------------------------------------------------------------

  PROCEDURE [XDCL, #GATE] mmp$check_io_status (status_pointer_array: mmt$io_status_pointer_array;
        wait_time: integer;
    VAR index: integer;
    VAR status: ost$status);

    VAR
      rb: mmt$rb_memory_manager_io,
      first_check: boolean,
      i: integer,
      j: integer,
      end_timeout: integer,
      valid_ptr_found: boolean,
      wait_opt: ost$wait,
      xcb_p: ^ost$execution_control_block,
      iocb_ptr: ^mmt$io_control_block,
      iocb_table: mmt$iocb_table_array,
      latest_completion_time: integer;

    status.normal := TRUE;

    index := 0;

    valid_ptr_found := FALSE;

    first_check := TRUE;

    end_timeout := (wait_time * 1000) + #free_running_clock (0);

    IF wait_time > 0 THEN
      wait_opt := osc$wait;
    ELSE
      wait_opt := osc$nowait;
    IFEND;

  /process_check_wait/
    WHILE (first_check) OR (end_timeout > #free_running_clock (0)) DO
      IF NOT first_check THEN { Have the task wait. }

{ Make sure the first non_nil pointer is in the IOCB before having the task wait.

        IF NOT valid_ptr_found THEN

          iocb_table := iocb_ptr^.iocb_table;

        /check_for_valid_ptr/
          FOR i := LOWERBOUND (status_pointer_array) TO UPPERBOUND (status_pointer_array) DO
            IF status_pointer_array [i] <> NIL THEN
              FOR j := LOWERBOUND (iocb_table) TO iocb_ptr^.maximum_iocb_index_in_use DO
                IF status_pointer_array [i] = iocb_table [j].iostatus_p THEN
                  valid_ptr_found := TRUE;
                  EXIT /check_for_valid_ptr/;
                IFEND;
              FOREND;
              EXIT /check_for_valid_ptr/;
            IFEND;
          FOREND /check_for_valid_ptr/;
          IF NOT valid_ptr_found THEN
            osp$set_status_abnormal ('MM', mme$invalid_io_status_ptrs, ' ', status);
            EXIT /process_check_wait/;
          IFEND;
        IFEND;

        rb.reqcode := syc$rc_memory_manager_io;
        rb.status.normal := TRUE;
        rb.sub_reqcode := mmc$iorc_await_io_completion;
        rb.latest_completion_time := latest_completion_time;
        rb.wait_time := wait_time;
        i#call_monitor (#LOC (rb), #SIZE (rb));
      IFEND;

      pmp$find_executing_task_xcb (xcb_p);
      iocb_ptr := xcb_p^.iocb_p;
      IF iocb_ptr = NIL THEN
        osp$set_status_abnormal ('MM', mme$nil_io_control_block, ' ', status);
        RETURN;
      IFEND;

      mmp$process_io_completions (iocb_ptr, wait_opt, latest_completion_time, status);
      IF NOT status.normal THEN
        EXIT /process_check_wait/;
      IFEND;

      FOR i := LOWERBOUND (status_pointer_array) TO UPPERBOUND (status_pointer_array) DO
        IF status_pointer_array [i] <> NIL THEN
          IF status_pointer_array [i]^.request_status = mmc$irs_complete THEN
            index := i;
            EXIT /process_check_wait/;
          IFEND;
        IFEND;
      FOREND;
      first_check := FALSE;
    WHILEND /process_check_wait/;

  PROCEND mmp$check_io_status;

?? TITLE := 'mmp$process_io_completions' ??
?? EJECT ??

  PROCEDURE mmp$process_io_completions
    (    iocb_ptr: ^mmt$io_control_block;
         wait_opt: ost$wait;
     VAR latest_completion_time: integer;
     VAR status: ost$status);

    VAR
      completion_table: mmt$iocb_table_array,
      i: mmt$iocb_index,
      re_request: array [mmt$iocb_index] of mmt$iocb_index,
      re_request_index: mmt$iocb_index,
      wait: ost$wait;

    status.normal := TRUE;

    re_request_index := 0;

    latest_completion_time := iocb_ptr^.latest_completion_time;
    last_update_time := latest_completion_time;

    i#move (#LOC (iocb_ptr^.iocb_table), ^completion_table, #SIZE (mmt$iocb_table_entry) *
          iocb_ptr^.maximum_iocb_index_in_use);
    FOR i := LOWERBOUND (completion_table) TO iocb_ptr^.maximum_iocb_index_in_use DO

{ If a request has been marked already active and needs to be reissued, then save the table
{ indices of those requests in an array (re_request) so that the requests
{ can be reissued, and do not report those requests as complete.

      IF (completion_table [i].active_io_count = 0) AND (completion_table [i].used_for_asynchronous_io) THEN
        IF (completion_table [i].sub_reqcode = mmc$iorc_read_pages) AND (completion_table [i].condition = 0)
              AND (completion_table [i].io_already_active) THEN
          re_request [re_request_index] := i;
          re_request_index := re_request_index + 1;
        ELSEIF (completion_table [i].sub_reqcode = mmc$iorc_write_pages) AND (completion_table [i].condition =
              0) AND (completion_table [i].io_already_active) THEN
          re_request [re_request_index] := i;
          re_request_index := re_request_index + 1;
        ELSEIF completion_table [i].condition = ioc$disk_media_error THEN
          mmp$reallocate_file_space (completion_table [i].pva, status);
          IF status.normal THEN
            re_request [re_request_index] := i;
            re_request_index := re_request_index + 1;
          ELSE
            completion_table [i].iostatus_p^.request_status := mmc$irs_complete;
            completion_table [i].iostatus_p^.condition := completion_table [i].condition;
          IFEND;
        ELSE
          completion_table [i].iostatus_p^.request_status := mmc$irs_complete;
          completion_table [i].iostatus_p^.condition := completion_table [i].condition;
        IFEND;
      IFEND;
    FOREND;

    mmp$update_iocb_completions (completion_table);

    FOR i := 0 TO re_request_index - 1 DO
      IF completion_table [re_request [i]].sub_reqcode = mmc$iorc_read_pages THEN
        mmp$read (completion_table [re_request [i]].pva, completion_table [re_request [i]].length,
              completion_table [re_request [i]].iostatus_p, wait_opt, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      ELSE
        IF completion_table [re_request [i]].condition = ioc$disk_media_error THEN
          wait := osc$wait;
        ELSE
          wait := wait_opt;
        IFEND;
        mmp$write (completion_table [re_request [i]].pva, completion_table [re_request [i]].length, FALSE,
              completion_table [re_request [i]].iostatus_p, wait, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    FOREND;

  PROCEND mmp$process_io_completions;

?? TITLE := 'mmp$allocate_iocb_r3' ??
?? EJECT ??

  PROCEDURE [XDCL, #GATE] mmp$allocate_iocb_r3;

    osp$verify_system_privilege;

    mmp$allocate_iocb_r1;
  PROCEND mmp$allocate_iocb_r3;

?? TITLE := 'mmp$wait_for_iocb_entry' ??
?? EJECT ??

  PROCEDURE [XDCL, #GATE] mmp$wait_for_iocb_entry (VAR status: ost$status);

    VAR
      xcb_p: ^ost$execution_control_block,
      iocb_ptr: ^mmt$io_control_block,
      latest_completion_time: integer,
      i: integer;

    status.normal := TRUE;

    osp$verify_system_privilege;

    pmp$find_executing_task_xcb (xcb_p);
    iocb_ptr := xcb_p^.iocb_p;
    IF iocb_ptr = NIL THEN
      osp$set_status_abnormal ('MM', mme$nil_io_control_block, ' ', status);
      RETURN;
    IFEND;
    WHILE TRUE DO
      mmp$process_io_completions (iocb_ptr, osc$nowait, latest_completion_time, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      FOR i := LOWERBOUND (iocb_ptr^.iocb_table) TO UPPERBOUND (iocb_ptr^.iocb_table) DO
        IF (NOT iocb_ptr^.iocb_table [i].used_for_asynchronous_io) AND
              (iocb_ptr^.iocb_table [i].active_io_count = 0) THEN
          RETURN;
        IFEND;
      FOREND;
      pmp$wait (100, 100);
    WHILEND;
  PROCEND mmp$wait_for_iocb_entry;
MODEND mmm$read_write_io_ring_3;
