Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  Extensions to the Sun RPC Library One-Way Messaging oneway Attribute One-way call using a simple counter service  Previous   Contents   Next 
   
 

Non-Blocking I/O

Non-blocking I/O avoids the client being blocked while waiting for a request to be accepted by the transport layer during one-way messaging for connection-oriented protocols.

For connection-oriented protocols, there is a limit to the amount of data that can be put in a network protocol queue. The limit depends on the transport protocols used. When a client sending a request reaches the data limit, this client is blocked from processing until its request has entered the queue. You cannot determine how long a message will wait before being added to the queue.

In non-blocking I/O, when the transport queue is full, there is an additional buffer available between the client and the transport layer. As requests not accepted by the transport queue can be stored in this buffer, the client is not blocked. The client is free to continue processing as soon as it has put the request in the buffer. The client does not wait until the request is put in the queue and does not receive information on the status of the request after the buffer accepts the request.

By using non-blocking I/O you gain further processing time as compared to two-way and one-way messaging. The client can send requests in succession without being blocked from processing.

The following figure shows a case where you choose the non-blocking argument of the I/O mode and the transport layer queue is full.

Figure 8-3 Non-Blocking Messaging

Using Non-Blocking I/O

To use non-blocking I/O, configure a client handle using the CLSET_IO_MODE rpciomode_t* option of the clnt_control() function with the RPC_CL_NONBLOCKING argument. See the clnt_control(3NSL) man page for further information.

When the transport queue is full, the buffer is used. The buffer continues to be used until two criteria are fulfilled:

  • The buffer is empty

  • The queue can immediately accept a request

Requests then go directly to the transport queue until the queue is full. The default size of the buffer is 16 kbytes.

Note that the buffer is not emptied automatically. You must flush the buffer when it contains data.

When you chose the RPC_CL_NONBLOCKING argument of CLSET_IO_MODE, you have a choice of flush modes. You can specify either the RPC_CLBESTEFFORT_FLUSH or RPC_CL_BLOCKING_FLUSH argument to CLSET_FLUSH_MODE. You can also empty the buffer by sending a synchronous call, such as clnt_call(). See the clnt_control(3NSL) man page for further information.

If the buffer is full, an RPC_CANTSTORE error is returned to the client and the request is not sent. The client must send the message again later. You can find out or change the size of the buffer by using the CLSET_CONNMAXREC and CLGET_CONNMAXREC commands. To determine the size of all pending request stored in the buffer, use the CLGET_CURRENT_REC_SIZE command. For further information on these commands see the clnt_control(3NSL) man page.

The server does not confirm whether the request is received or processed. After a request enters a buffer, you can use clnt_control() to obtain information on the status of the request.

Using a simple counter with non-blocking I/O

The client.c file in the one-way messaging example is modified in this section to demonstrate how to use the non-blocking I/O mode. In this new file, client_nonblo.c, the I/O mode is specified as non-blocking with the RPC_CL_NONBLOCKING argument, the flush mode is chosen to be blocking by use of the RPC_CL_BLOCKING_FLUSH. The I/O mode and flush mode are invoked with CLSET_IO_MODE. When an error occurs RPC_CANT_STORE is returned to the client and the program tries to flush the buffer. To choose a different method of flush consult the clnt_control(3NSL) man page.

#include <stdio.h>
#include "counter.h"

main(int argc, char *argv[])
{
    CLIENT* clnt;
    enum clnt_stat result;
    char *server;
    int number;
    bool_t bres;
		/* 
		 * Choose the I/O mode and flush method to be used.
		 * The non-blocking I/O mode and blocking flush are 
		 * chosen in this example.
		 */
    int mode = RPC_CL_NONBLOCKING;
    int flushMode = RPC_CL_BLOCKING_FLUSH;

    if (argc != 3) {
			  fprintf(stderr, "usage: %s server_name number\n", argv[0]);
			  exit(1);
    }
    server = argv[1];
    number = atoi(argv[2]);

    clnt= clnt_create(server, COUNTERPROG, COUNTERVERS, "tcp");
    if (clnt == (CLIENT*) NULL) {
			  clnt_pcreateerror(server);
			  exit(1);
    }

		/* 
 	 * Use clnt_control to set the I/O mode. 
 	 * The non-blocking I/O mode is 
 	 * chosen for this example.
 	 */
    bres = clnt_control(clnt, CLSET_IO_MODE, (char*)&mode);
    if (bres)
	    /* 
		  * Set flush mode to blocking 
		  */
        bres = clnt_control(clnt, CLSET_FLUSH_MODE, (char*)&flushMode);

    if (!bres) {
        clnt_perror(clnt, "clnt_control");
        exit(1);
    }
		 /* 
 	  * Call the RPC services.
 	  */
    result = add_1(number, clnt);

    switch (result) {
    case RPC_SUCCESS:
        fprintf(stdout,"Success\n");
        break;
		/*
 	 * RPC_CANTSTORE is a new value returned to the 
		 * client when the buffer cannot store a request.
		 */
    case RPC_CANTSTORE:
        fprintf(stdout,"RPC_CANTSTORE error. Flushing ... \n");
		/*
		 * The buffer is flushed using the blocking flush method
		 */
			  bres = clnt_control(clnt, CLFLUSH, NULL);
        if (!bres) {
	    		   clnt_perror(clnt, "clnt_control");
			  }
        break;
    default:
			  clnt_perror(clnt, "call failed");
        break;
    }

    /* Flush */
    bres = clnt_control(clnt, CLFLUSH, NULL);
    if (!bres) {
			  clnt_perror(clnt, "clnt_control");
    }

    clnt_destroy(clnt);
    exit(0);
}
 
 
 
  Previous   Contents   Next