 |
VMS DECwindows Transport Manual
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 ;
.
.
.
|
- If write operations are enabled, then disable
them and execute the following block. Otherwise, leave them disabled
and return.
- 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.
- 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 ;
.
.
.
|
- 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.
- This is a consistency check on the
information provided in the XTCB. If it is bad, return a fatal status.
- 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.
- If the queue was found to be corrupted,
close the connection and return with a fatal status.
- 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.
- 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 ;
.
.
.
|
- 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.
- 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.
- 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.
- 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.
- There was an XTCB on the output work queue.
Initiate a write $QIO on the data.
- 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 ;
.
.
.
|
- If the dying field is set, return an abort
status.
- If the address or length of the data equals
zero, return a normal completion status.
- 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.
- 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.
- 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.
- 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.
- 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.
- Check if the connection is dying. Wakeup may
be due to connection abort.
- 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.
- 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.
- 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 ;
.
.
.
|
- If free-input operations are disabled, leave
them disabled and return a successful status. No additional work is
necessary.
- 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.
- If the queue was empty this was a false
start. Enable free-input operations and return a successful status.
- 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 ;
.
.
.
|
|