HP OpenVMS Systems Documentation

Content starts here

VMS DECwindows Transport Manual


Previous Contents Index

8.2.2 Sample XTFT$A_EXECUTE_WRITE Routine

XTFT$A_EXECUTE_WRITE is invoked to write an XTCB to a transport connection.

The procedure is expected to remove the head XTCB from the output work queue and, if the remove was successful, initiate I/O on the data in the XTCB.

Note that the xtcb parameter in the call sequence is merely a placeholder retained for historical reasons; its value is never accessed or relied upon. Example 8-2 shows a sample implementation of the XTFT$A_EXECUTE_WRITE routine.

Example 8-2 Sample XTFT$A_EXECUTE_WRITE Routine

   .
   .
   .
ROUTINE DECW$$TCPIP_EXECUTE_WRITE(
        xtcc:           REF $BBLOCK,
        xtcb:           REF $BBLOCK,
        mode
) =

BEGIN
BUILTIN
    REMQHI ;
LOCAL
    tcb : REF $BBLOCK,
    status ;

(1)IF NOT xport_out_write_disable( xtcc )
THEN
    BEGIN
    (2)WHILE ( status = REMQHI( .xtcc [xtcc$a_ow_queue], tcb ) )
          EQL xport$k_queue_locked DO
        WHILE ..xtcc [xtcc$a_ow_queue] DO ;
    IF .status EQL xport$k_queue_no_entry
    THEN
        xport_out_write_enable( xtcc )
    ELSE
        (3)RETURN DECW$$XPORT_WRITE( .xtcc, .tcb, .mode ) ;
    END ;

SS$_NORMAL
END ;
   .
   .
   .
  1. If write operations are enabled, then disable them and execute the following block. Otherwise, leave them disabled and return.
  2. Attempt to remove an XTCB from the head of the output work queue. If there was no XTCB on the work queue, enable future write operations and return.
  3. If the queue was not empty, initiate a write operation for the XTCB by invoking the common transport routine (DECW$$XPORT_WRITE). This form of write operation expects a "real" XTCB in the argument list, not just a placeholder.

8.2.3 Sample XTFT$A_WRITE Routine

The XTFT$A_WRITE routine is invoked to write an XTCB to a transport connection. Unlike XTFT$A_EXECUTE_WRITE, the XTCB parameter in the argument list is significant and is the address of an XTCB, not an element of any queue, whose data is to be written to a connection.

Example 8-3 shows a sample implementation of the XTFT$A_WRITE routine.

Example 8-3 Sample XTFT$A_WRITE Routine

   .
   .
   .

GLOBAL ROUTINE DECW$$TCPIP_WRITE( itcc : REF $BBLOCK VOLATILE, tcb : REF
$BBLOCK, mode ) =

BEGIN

BIND
   xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK,
   xtcc = .itcc [ixtcc$a_tcc] : $BBLOCK,
   xtcb = .tcb : $BBLOCK ;

LOCAL
    status ;

(1)IF .xtcc [xtcc$v_dying]
THEN
    BEGIN
    $INSQHI( xtcb, .itcc [ixtcc$a_ow_queue] ) ;
    RETURN DECW$_CNXABORT ;
    END ;
(2)IF .xtcb [xtcb$l_length] GTRU xport_xtcb_total( xtcb )
THEN
    RETURN SS$_IVBUFLEN ;

(3)IF .xtcb [xtcb$l_length] EQLU 0
THEN
    BEGIN
    IF .xtcb [xtcb$b_subtype] EQLU decw$c_dyn_xtcb_srp
    THEN
        status = $INSQHI( xtcb, .itcc [ixtcc$a_ofs_queue] )
    ELSE
        status = $INSQHI( xtcb, .itcc [ixtcc$a_ofl_queue] ) ;

    (4)IF .status EQL xport$k_queue_corrupted
    THEN
       BEGIN
       xtcc [xtcc$v_dying] = 1 ;
       xtcc_status( xtcc, DECW$_BADQUEUE ) ;
       RETURN DECW$_CNXABORT ;
       END ;

    RETURN SS$_NORMAL ;
    END ;


(5)xtcb [xtcb$l_rflink] = xtcc ;
IF NOT (status = $QIO(  EFN =.itcc [ixtcc$w_efn],
                        FUNC = IO$_WRITEVBLK,
                        CHAN = .itcc [ixtcc$w_chan],
                        IOSB = xtcb [xtcb$w_iosb],
                        ASTADR = write_ast,
                        ASTPRM = xtcb,
                        P1 = xtcb [xtcb$t_data],
                        P2 = .xtcb [xtcb$l_length] ) )
(6)THEN
    BEGIN
    $INSQHI( xtcb, .itcc [ixtcc$a_ow_queue] ) ;
    xtcc [xtcc$v_dying] = 1 ;
    xtcc_status( xtcc, .status ) ;
    END ;
.status
END ;
   .
   .
   .
  1. See if the connection is marked dying. If so, return the XTCB to the head of the output work queue and return with a status indicating that the connection has died.
  2. This is a consistency check on the information provided in the XTCB. If it is bad, return a fatal status.
  3. If this XTCB is empty, return it to either the large or small output free queue, as appropriate for the XTCB type in the XTCB$B_SUBTYPE field, and return with a successful status.
  4. If the queue was found to be corrupted, close the connection and return with a fatal status.
  5. The XTCB has a valid data length. Initiate a write $QIO on the data in the XTCB. P1 is the address of the first byte of user data in the XTCB; P2 is the length of the data in the buffer. The ASTADR argument specifies a WRITE_AST routine, as described in Section 8.2.4. ASTPRM is the address of the XTCB being operated on.
  6. If the $QIO service failed, return the XTCB to the head of the output work queue and mark the connection as dying. Use the XTCC_STATUS macro to save the failure code in the XTCC if it has not yet been set.

8.2.4 Sample WRITE_AST Routine

WRITE_AST is an AST completion routine for TCP/IP write operations. WRITE_AST returns the XTCB to the appropriate free queue. If the $QIO failed or the connection is dying, failure processing is performed and the transport prohibits further operations on this connection.

Failure processing is dependent upon the type of transport being used. In the case of the TCP/IP implemented by UCX, a failed I/O attempt to the connection indicates that a connection has aborted. When an I/O operation completes and the status indicates failure, the code must perform all logical-link rundown operations including setting the dying bit and error status, completing any process waits for input or output, and sending notification to the process that the connection has died.

Other transports such as DECnet provide a separate mechanism for receiving notice that a connection has aborted. For such a transport, these logical-link rundown operations need only be performed by the code that receives this notification.

If the I/O completed successfully, the procedure attempts to remove another XTCB from the head of the output work queue and initiate a write operation on this XTCB. If the queue was empty, the procedure must enable write operations on the connection to cause the common transport to call the specific transport when an XTCB is next inserted on the output work queue. If the queue was not empty, the I/O operation is performed and the usual error processing is performed on the result. Example 8-4 shows a sample implementation of the WRITE_AST routine.

Example 8-4 Sample WRITE_AST Routine

   .
   .
   .
ROUTINE write_ast( xtcb : REF $BBLOCK ) : NOVALUE =

BEGIN
BIND
    tcc = .xtcb [xtcb$l_rflink] : $BBLOCK,
    iosb = xtcb [xtcb$w_iosb] : VECTOR [4,WORD,UNSIGNED] ;


LOCAL
    itcc : REF $BBLOCK,
    tcb : REF $BBLOCK,
    status,
    type ;

(1)
VALIDATE_XTCC( tcc, itcc ) ;
IF .xtcb [xtcb$b_subtype] EQLU decw$c_dyn_xtcb_srp
THEN
    BEGIN
    BIND
        xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

    status = $INSQHI( .xtcb, .itcc [ixtcc$a_ofs_queue] ) ;
    xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_srp ) ;
    END
ELSE
    BEGIN
    BIND
        xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

    status = $INSQHI( .xtcb, .itcc [ixtcc$a_ofl_queue] ) ;
    xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_lrp ) ;
    END ;

IF .status EQL xport$k_queue_corrupted
THEN
    BEGIN
    BIND
        xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

    IF TESTBITCS( tcc [xtcc$v_dying] )
    THEN
        BEGIN
        xport_abort_send( tcpip_tdb, tcc ) ;
        IF .tcc [xtcc$v_lrp_on_output]
        THEN xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_lrp )
        ELSE xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_srp ) ;
        xport_in_notify_send( tcc, xtpb ) ;
        END ;

    xport_out_write_enable( tcc ) ;
    xport_write_unwait( tcc, xtpb ) ;
    xtcc_status( tcc, DECW$_BADQUEUE ) ;
    RETURN ;
    END ;


(2)IF .tcc [xtcc$v_dying] OR NOT .iosb [0]
THEN
    BEGIN
    BIND
        xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

    IF TESTBITCS( tcc [xtcc$v_dying] )
    THEN
        BEGIN

        xport_abort_send( tcpip_tdb, tcc ) ;
        IF .tcc [xtcc$v_lrp_on_output]
        THEN xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_lrp )
        ELSE xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_srp ) ;
        xport_in_notify_send( tcc, xtpb ) ;
        END ;

    (3)xport_out_write_enable( tcc ) ;
    xport_write_unwait( tcc, xtpb ) ;
    xtcc_status( tcc, .iosb [0] ) ;
    RETURN ;
    END ;

(4)status = $REMQHI( .itcc [ixtcc$a_ow_queue], tcb ) ;
IF .status EQL xport$k_queue_corrupted
THEN
    BEGIN
    BIND
         xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

    IF TESTBITCS( tcc [xtcc$v_dying] )
    THEN
        BEGIN
        xport_abort_send( tcpip_tdb, tcc ) ;
        IF .tcc [xtcc$v_lrp_on_output]
        THEN xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_lrp )
        ELSE xport_out_notify_send( tcc, xtpb, decw$c_xport_buffer_srp ) ;
        xport_in_notify_send( tcc, xtpb ) ;
        END ;

    xport_out_write_enable( tcc ) ;
    xport_write_unwait( tcc, xtpb ) ;
    xtcc_status( tcc, DECW$_BADQUEUE ) ;
    RETURN ;
    END ;

IF .status EQL xport$k_queue_no_entry
THEN
    BEGIN
    BIND
        xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;
    xport_out_write_enable( tcc ) ;
    xport_write_unwait( tcc, xtpb ) ;
    RETURN ;
    END ;

(5)tcb [xtcb$l_rflink] = tcc ;
IF NOT ( status = $qio( EFN = .itcc [ixtcc$w_efn],
                        FUNC = IO$_WRITEVBLK,
                        CHAN = .itcc [ixtcc$w_chan],
                        IOSB = tcb [xtcb$w_iosb],
                        ASTADR = write_ast,
                        ASTPRM = .tcb,
                        P1 = tcb [xtcb$t_data],
                        P2 = .tcb [xtcb$l_length] ) )
THEN
    BEGIN
    BIND
        xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

    (6)$INSQHI( .tcb, .itcc [ixtcc$a_ow_queue] ) ;
    IF TESTBITCS( tcc [xtcc$v_dying] )
    THEN
        BEGIN
        xport_abort_send( tcpip_tdb, tcc ) ;
        xport_in_notify_send( tcc, xtpb ) ;
        xtcc_status( tcc, .status ) ;
        END ;
    xport_write_unwait( tcc, xtpb ) ;
    END ;
END ;
   .
   .
   .
  1. First, assume that the write operation worked and put the buffer back on either the small or large OutputFreeQueue. The XPORT_OUT_NOTIFY_SEND macro informs the process that a write operation has completed. This operation may consist of sending a user-mode AST to the process, or of code that completes the $SYNCH system service call performed by the XPORT_OUT_WAIT macro.
    If the output free queue was corrupted, perform failure processing to close this connection.
  2. In the case of TCP/IP under UCX, this procedure must detect and respond to any problem on the connection/socket. If the $QIO failed, and the connection is not yet marked as dying, then mark it. Additionally, invoke the XPORT_ABORT_SEND macro to declare a user-mode AST to the process indicating that the connection has died. Invoke the XPORT_OUT_NOTIFY_SEND and XPORT_IN_NOTIFY_SEND macros to complete any $SYNCH service used to wait for this connection.
  3. Allow write operations. The XPORT_WRITE_UNWAIT macro tests the XTCC$V_WAIT_ON_WRITE flag to see if it is set. If set, the common transport is waiting for the specific transport to empty the output work queue so that a write-user operation can be initiated. XPORT_WRITE_UNWAIT clears the XTPB$W_ON_EFN flag.

    Note

    Most transports should implement the write-user function as a call to DECW$COPY_AND_WRITE and need not invoke the XPORT_WRITE_UNWAIT macro.

    Save the reason for the link abort and return.
  4. The I/O operation was successful, so attempt to get another XTCB from the output work queue. If it is empty, enable write operations on this connection and call XPORT_WRITE_UNWAIT. If the queue was corrupted, close the connection and perform abort processing.
  5. There was an XTCB on the output work queue. Initiate a write $QIO on the data.
  6. If the $QIO does not return successfully, insert the XTCB back on the output work queue. Test and set the dying flag and perform abort processing if it was clear.

8.2.5 Sample XTFT$A_WRITE_USER Routine

The XTFT$A_WRITE_USER routine attempts to write a buffer in the user's address space to a TCP/IP connection. The purpose of this interface is to avoid a data copy into XTCBs when the caller has a large, contiguous block of data to be written to a connection, such as when sending image data between client and server.

There are two methods for implementing the XTFT$A_WRITE_USER routine. The first is to wait for the output work queue to become empty and then perform the I/O operation on the user's buffer, typically by means of a $QIO.

The other method is to invoke the common transport's DECW$XPORT_COPY_AND_WRITE routine with the user's buffer as an argument. Due to the way this feature is being used in the DECwindows software, it is strongly recommended that transports use the DECW$XPORT_COPY_AND_WRITE routine.

Example 8-5 shows a sample implementation of the XTFT$A_WRITE_USER routine.

Example 8-5 Sample XTFT$A_WRITE_USER Routine

     .
     .
     .
  GLOBAL ROUTINE DECW$$TCPIP_WRITE_USER( itcc : REF $BBLOCK VOLATILE, buffer :
          REF $BBLOCK, mode ) =

  BEGIN
  BUILTIN
      INSQTI,
      INSQHI,
      REMQHI ;

  BIND
      xtcc = .itcc [ixtcc$a_tcc] : $BBLOCK,
      xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

  LOCAL
      status,
      data_adr,
      data_len,
      size,
      lcl_iosb : VECTOR[4,WORD,UNSIGNED] ;

(1) IF .xtcc [xtcc$v_dying]
   THEN
       BEGIN
       RETURN DECW$_CNXABORT ;
       END ;

(2) IF (data_adr = .buffer [dsc$a_pointer]) EQLA 0
       OR
      (data_len = .buffer [dsc$w_length]) EQLU 0
   THEN
       RETURN SS$_NORMAL ;

(3) %IF USER_WRITE_BY_COPY
   %THEN

(4) status = DECW$XPORT_COPY_AND_WRITE( xtcc, 0, .data_adr,
                 .data_len, size ) ;

   %ELSE

(5)
   (xtcc [xtcc$w_ow_iosb])< 0,32, 0> = 0 ;
   (xtcc [xtcc$w_ow_iosb])<32,32, 0> = 0 ;
   TESTBITCS( (xtcc [xtcc$l_flags])<$BITPOSITION( xtcc$v_wait_on_write ), 1> ) ;
(6) WHILE (xport_out_write_disable( xtcc )) DO
       BEGIN
       xport_write_wait( xtcc, xtpb ) ;
       END ;

(7) xport_out_write_enable( xtcc ) ;
   TESTBITCC( (xtcc [xtcc$l_flags])<$BITPOSITION( xtcc$v_wait_on_write ), 1> ) ;
(8) IF .xtcc [xtcc$v_dying]
   THEN
       BEGIN
       RETURN DECW$_CNXABORT ;
       END ;
(9) DO
       BEGIN
       size = MINU( WRITE_MAXIMUM_LENGTH, .data_len ) ;
    (10) IF (status = $QIOW(EFN = .itcc [ixtcc$w_efn],
                          FUNC = IO$_WRITEVBLK,
                          CHAN = .itcc [ixtcc$w_chan],
                          IOSB = lcl_iosb,
                          P1 = .data_adr,
                          P2 = .size ) )
       THEN
           status = .lcl_iosb [0] ;
       IF NOT .status
       THEN
           RETURN .status ;
       data_len = .data_len - .size ;
       data_adr = .data_adr + .size ;
       END
   WHILE .data_len NEQU 0 ;

   %FI


(11) .status
   END ;
     .
     .
     .
  1. If the dying field is set, return an abort status.
  2. If the address or length of the data equals zero, return a normal completion status.
  3. In the code developed by Digital, the BLISS literal USER_WRITE_BY_COPY is always set to the value 1 and the code that invokes the DECW$XPORT_COPY_AND_WRITE procedure is compiled. For completeness, the uncompiled code is also described.
  4. Invoke the common transport DECW$XPORT_COPY_AND_WRITE routine to copy the data in the user's buffer into XTCBs and make them available for transmission to the connection.
  5. Clear the waiting-for-output IOSB field of the XTCC and set the XTCC$V_WAIT_ON_WRITE bit to indicate that it is waiting for the output work queue to become empty. This operation is similar to the operation performed by the XPORT_IN_NOTIFY_SET and XPORT_OUT_NOTIFY_SET macros.
  6. Begin waiting for the output work queue to empty. The wait is required to ensure that the data sent to the connection is not reordered. When the output work queue is emptied, writing is enabled on the connection, hence the WHILE loop. Invoke the XPORT_WRITE_WAIT macro inside the loop to perform the $SYNCH service.
  7. The wait has been satisfied, so continue processing the write-user request. The XPORT_OUT_WRITE_DISABLE macro tests and sets the disable flag, so enable write operations (this is a branch-on-bit-clear-and-clear-interlocked [BBCCI] instruction). Clear the XTCC$V_WAIT_ON_WRITE bit.
  8. Check if the connection is dying. Wakeup may be due to connection abort.
  9. Begin a loop that writes the user's data. For very large user data blocks, it may be necessary to break the data into smaller pieces for transmission. The optimal size of these pieces may be different for each type of transport. In the example, this size is given by the literal WRITE_MAXIMUM_LENGTH, which is a value that can be represented by a 15-bit integer.
  10. Write the data to the connection using the $QIOW system service. The wait form of the service is required because the user's buffer is not safely copied until the I/O request completes.
  11. Return the status.

8.2.6 Sample XTFT$A_EXECUTE_FREE Routine

The XTFT$A_EXECUTE_FREE routine logically returns an XTCB to a local logical link. The common transport invokes XTFT$A_EXECUTE_FREE after it returns an input XTCB to a previously empty free queue. XTFT$A_EXECUTE_FREE checks to see if input-free operations are enabled for this connection, and if so, attempts to start a read operation into an XTCB after removing it from the head of the queue.

The tcb argument is a placeholder; it is never referred to by the routine and its contents are unpredictable.

Example 8-6 shows a sample implementation of the XTFT$A_EXECUTE_FREE routine.

Example 8-6 Sample XTFT$A_EXECUTE_FREE Routine

    .
    .
    .
   ROUTINE DECW$$TCPIP_EXECUTE_FREE(
    tcc:  REF $BBLOCK,
    tcb:  REF $BBLOCK,
    type,
    free_queue
   ) =

   BEGIN
   BUILTIN
       REMQHI ;
   LOCAL
       newtcb : REF $BBLOCK,
       status ;

(1) IF NOT xport_in_free_disable( tcc, .type )
   THEN
        BEGIN
     (2) WHILE (status = REMQHI( .free_queue, newtcb )) EQL xport$k_queue_locked DO
            WHILE ..free_queue DO ;
     (3) IF .status EQL xport$k_queue_no_entry
        THEN
          BEGIN
          xport_in_free_enable( tcc, .type ) ;
          status = SS$_NORMAL ;
          END
     (4) ELSE
            RETURN DECW$$XPORT_FREE_INPUT( .tcc, .newtcb ) ;
        END ;

   SS$_NORMAL
   END ;
     .
     .
     .
  1. If free-input operations are disabled, leave them disabled and return a successful status. No additional work is necessary.
  2. Free-input operations that were enabled are now disabled. Attempt to remove an XTCB from the appropriate free queue. If the queue is locked, test the interlock bit of the free queue until it is no longer set, then go back and do the remove operation again.
  3. If the queue was empty this was a false start. Enable free-input operations and return a successful status.
  4. Otherwise, invoke XTFT$A_FREE_INPUT_BUFFER by means of the common transport DECW$$XPORT_FREE_INPUT routine to start a read operation on the XTCB removed from the queue.

8.2.7 Sample XTFT$A_FREE_INPUT_BUFFER Routine

The XTFT$A_FREE_INPUT_BUFFER does an asynchronous read, by means of a $QIO read, for a connection into the provided buffer. The common transport invokes XTFT$A_FREE_INPUT_BUFFER when an input XTCB has been returned by the caller to a transport and the specific transport needs to receive control in order to initiate a read operation.

Unlike the XTFT$A_EXECUTE_FREE routine, the xtcb argument is a "real" XTCB. It is assumed that any enable/disable checks, performed with the XPORT_IN_FREE_DISABLE macro, have already been performed.

Example 8-7 shows a sample implementation of the XTFT$A_FREE_INPUT_BUFFER routine.

Example 8-7 Sample XTFT$A_FREE_INPUT_BUFFER Routine

      .
      .
      .
   GLOBAL ROUTINE DECW$$TCPIP_FREE_INPUT_BUFFER( itcc : REF $BBLOCK VOLATILE, tcb
   : REF $BBLOCK ) =

   BEGIN
   BIND
       xtcb = .tcb : $BBLOCK,
       xtcc = .itcc [ixtcc$a_tcc] : $BBLOCK,
       xtpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;

   LOCAL
       status,
       size,
       free_queue ;

(1) IF .xtcb [xtcb$b_subtype] EQLU decw$c_dyn_xtcb_srp
   THEN
       free_queue = .itcc [ixtcc$a_ifs_queue]
   ELSE
       free_queue = .itcc [ixtcc$a_ifl_queue] ;

(2) xtcb [xtcb$l_rflink]    = xtcc ;
   IF NOT (status = $QIO( EFN = .itcc [ixtcc$w_efn],
                          CHAN = .itcc [ixtcc$w_chan],
                          FUNC = IO$_READVBLK,
                          IOSB = xtcb [xtcb$w_iosb],
                          ASTADR = free_input_ast,
                          ASTPRM = xtcb,
                          P1 = xtcb [xtcb$t_data],
                          P2 = xport_xtcb_total( xtcb ) ) )
(3) THEN
       BEGIN
       $INSQHI( xtcb, .free_queue ) ;
       xtcc [xtcc$v_dying] = 1 ;
       xtcc_status( xtcc, .status ) ;
       END ;
   .status
   END ;
     .
     .
     .


Previous Next Contents Index