Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

FlushAllOutput()

FlushAllOutput() is called from the Dispatch() loop (read section 3.2.5) and also from WaitForSomething(). It is implemented as:


 /********************
 * FlushAllOutput()
 *    Flush all clients with output.  However, if some client still
 *    has input in the queue (more requests), then don't flush.  This
 *    will prevent the output queue from being flushed every time around
 *    the round robin queue.  Now, some say that it SHOULD be flushed
 *    every time around, but...
 *
 **********************/

void
FlushAllOutput(void)
{
    register int index, base;
    register fd_mask mask; /* raphael */
    OsCommPtr oc;
    register ClientPtr client;
    Bool newoutput = NewOutputPending;
#if defined(WIN32)
    fd_set newOutputPending;
#endif

    if (FlushCallback)
	CallCallbacks(&FlushCallback, NULL);

    if (!newoutput)
	return;

    /*
     * It may be that some client still has critical output pending,
     * but he is not yet ready to receive it anyway, so we will
     * simply wait for the select to tell us when he's ready to receive.
     */
    CriticalOutputPending = FALSE;
    NewOutputPending = FALSE;

#ifndef WIN32
    for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++)
    {
	mask = OutputPending.fds_bits[ base ];
	OutputPending.fds_bits[ base ] = 0;
	while (mask)
	{
	    index = ffs(mask) - 1;
	    mask &= ~lowbit(mask);
	    if ((index = ConnectionTranslation[(base * (sizeof(fd_mask)*8)) + index]) == 0)
		continue;
	    client = clients[index];
	    if (client->clientGone)
		continue;
	    oc = (OsCommPtr)client->osPrivate;
	    if (
#ifdef LBX
		!oc->proxy &&
#endif
		FD_ISSET(oc->fd, &ClientsWithInput))
	    {
		FD_SET(oc->fd, &OutputPending); /* set the bit again */
		NewOutputPending = TRUE;
	    }
	    else
		(void)FlushClient(client, oc, (char *)NULL, 0);
	}
    }
#else  /* WIN32 */
    FD_ZERO(&newOutputPending);
    for (base = 0; base < XFD_SETCOUNT(&OutputPending); base++)
    {
	    index = XFD_FD(&OutputPending, base);
	    if ((index = GetConnectionTranslation(index)) == 0)
		continue;
	    client = clients[index];
	    if (client->clientGone)
		continue;
	    oc = (OsCommPtr)client->osPrivate;
	    if (
#ifdef LBX
		!oc->proxy &&
#endif
		FD_ISSET(oc->fd, &ClientsWithInput))
	    {
		FD_SET(oc->fd, &newOutputPending); /* set the bit again */
		NewOutputPending = TRUE;
	    }
	    else
		(void)FlushClient(client, oc, (char *)NULL, 0);
    }
    XFD_COPYSET(&newOutputPending, &OutputPending);
#endif /* WIN32 */
}

newoutput is set to NewOutputPending, which indicates the new output for some clients is defined in connection.c.

With the following instruction the NewOutputPending is cleared since the current routine will server the need for output to the clients.

NewOutputPending = FALSE;

The main part of FlushAllOutput() is the the one that follows:

    for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++)
    {
	mask = OutputPending.fds_bits[ base ];
	OutputPending.fds_bits[ base ] = 0;
	while (mask)
	{
	    index = ffs(mask) - 1;
	    mask &= ~lowbit(mask);
	    if ((index = ConnectionTranslation[(base * (sizeof(fd_mask)*8)) + index]) == 0)
		continue;
	    client = clients[index];
	    if (client->clientGone)
		continue;
	    oc = (OsCommPtr)client->osPrivate;
	    if (
		FD_ISSET(oc->fd, &ClientsWithInput))
	    {
		FD_SET(oc->fd, &OutputPending); /* set the bit again */
		NewOutputPending = TRUE;
	    }
	    else
		(void)FlushClient(client, oc, (char *)NULL, 0);
	}
    }

The 'for' loop increases base until it reaches howmany(XFD_SETSIZE, NFDBITS). As explained in section 3.2.5.5 'howmany' is actually the number of integers that is dedicated to the fds_bits[] array belonging to OutputPending. OutputPending is defined in connection.c as:

fd_set OutputPending;		/* clients with reply/event data ready to go */

The next instruction:

mask = OutputPending.fds_bits[ base ];
copies the current integer to mask, so that active bits that are set to 1 that correspond to client fds ready for Server output can be served.

The following instruction clears the bits the specific integer so that after the process there are no active client fds:

OutputPending.fds_bits[ base ] = 0;

The while loop:

	while (mask)
iterates until the current integer is zero (all of its bits are cleared) and the instruction:

	    index = ffs(mask) - 1;
As we read in ffs man page fss finds the next bit set in the integer, starting from the least significant bit. index subtracts 1 since index starts counting from zero.

The instruction:

	    mask &= ~lowbit(mask);

uses macro lowbit which is defined in misc.h as:

/*
 * return the least significant bit in x which is set
 *
 * This works on 1's complement and 2's complement machines.
 * If you care about the extra instruction on 2's complement
 * machines, change to ((x) & (-(x)))
 */
#define lowbit(x) ((x) & (~(x) + 1))

A simple example demonstrating the previous macro is shown bellow:

 x = 00000101
~x = 11111010

~x+1 = 11111011

 lowbit = x & (~x+1) 
        = 00000001
~lowbit = 11111110

x &= ~lowbit(x) results to 00000100

Notice that the least significant bit (the red one) at the end of the example is cleared. By clearing each bit (and at the same processing the fd that corresponds to that) the mask is cleared and the 'while' loop stops so that we proceed to the next integer (base) of fds_bits[].

With the instructions:

	    if ((index = ConnectionTranslation[(base * (sizeof(fd_mask)*8)) + index]) == 0)
		continue;
	    client = clients[index];

the absolute position (from bit position inside an integer to bit position to the number of integers that make up fds_bits) of the index is calculated and array ConnectionTranslation[] is used to determine the index in the clients[] array and locate therefore the specific client. ConnectionTranslation[] is defined in connection.c as:


int *ConnectionTranslation = NULL;

The next lines:

	    oc = (OsCommPtr)client->osPrivate;
	    if (
		FD_ISSET(oc->fd, &ClientsWithInput))
	    {
		FD_SET(oc->fd, &OutputPending); /* set the bit again */
		NewOutputPending = TRUE;
	    }
	    else
		(void)FlushClient(client, oc, (char *)NULL, 0);

decide whether it is time for FlushClient() to be called or not. As we read in the FlushAllOutput() comment at the top of the current page:

*    Flush all clients with output.  However, if some client still
*    has input in the queue (more requests), then don't flush. 

FlushClient() is implemented as:

 /********************
 * FlushClient()
 *    If the client isn't keeping up with us, then we try to continue
 *    buffering the data and set the apropriate bit in ClientsWritable
 *    (which is used by WaitFor in the select).  If the connection yields
 *    a permanent error, or we can't allocate any more space, we then
 *    close the connection.
 *
 **********************/

#ifdef LBX
#ifdef LBX_NEED_OLD_SYMBOL_FOR_LOADABLES
#undef FlushClient
int
FlushClient(ClientPtr who, OsCommPtr oc, char *extraBuf, int extraCount)
{
    return (*oc->Flush)(who, oc, extraBuf, extraCount);
}
#endif
int
StandardFlushClient(ClientPtr who, OsCommPtr oc, 
    char *extraBuf, int extraCount)
#else
int
FlushClient(ClientPtr who, OsCommPtr oc, char *extraBuf, int extraCount)
#endif
{
    ConnectionOutputPtr oco = oc->output;
    int connection = oc->fd;
    XtransConnInfo trans_conn = oc->trans_conn;
    struct iovec iov[3];
    static char padBuffer[3];
    long written;
    long padsize;
    long notWritten;
    long todo;

    if (!oco)
	return 0;
    written = 0;
    padsize = padlength[extraCount & 3];
    notWritten = oco->count + extraCount + padsize;
    todo = notWritten;
    while (notWritten) {
	long before = written;	/* amount of whole thing written */
	long remain = todo;	/* amount to try this time, <= notWritten */
	int i = 0;
	long len;
	
	/* You could be very general here and have "in" and "out" iovecs
	 * and write a loop without using a macro, but what the heck.  This
	 * translates to:
	 *
	 *     how much of this piece is new?
	 *     if more new then we are trying this time, clamp
	 *     if nothing new
	 *         then bump down amount already written, for next piece
	 *         else put new stuff in iovec, will need all of next piece
	 *
	 * Note that todo had better be at least 1 or else we'll end up
	 * writing 0 iovecs.
	 */
#define InsertIOV(pointer, length) \
	len = (length) - before; \
	if (len > remain) \
	    len = remain; \
	if (len <= 0) { \
	    before = (-len); \
	} else { \
	    iov[i].iov_len = len; \
	    iov[i].iov_base = (pointer) + before; \
	    i++; \
	    remain -= len; \
	    before = 0; \
	}

	InsertIOV ((char *)oco->buf, oco->count)
	InsertIOV (extraBuf, extraCount)
	InsertIOV (padBuffer, padsize)

	errno = 0;
	if (trans_conn && (len = _XSERVTransWritev(trans_conn, iov, i)) >= 0)
	{
	    written += len;
	    notWritten -= len;
	    todo = notWritten;
	}
	else if (ETEST(errno)
#ifdef SUNSYSV /* check for another brain-damaged OS bug */
		 || (errno == 0)
#endif
#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
		 || ((errno == EMSGSIZE) && (todo == 1))
#endif
		)
	{
	    /* If we've arrived here, then the client is stuffed to the gills
	       and not ready to accept more.  Make a note of it and buffer
	       the rest. */
	    FD_SET(connection, &ClientsWriteBlocked);
	    AnyClientsWriteBlocked = TRUE;

	    if (written < oco->count)
	    {
		if (written > 0)
		{
		    oco->count -= written;
		    memmove((char *)oco->buf,
			    (char *)oco->buf + written,
			  oco->count);
		    written = 0;
		}
	    }
	    else
	    {
		written -= oco->count;
		oco->count = 0;
	    }

	    if (notWritten > oco->size)
	    {
		unsigned char *obuf;

		obuf = (unsigned char *)xrealloc(oco->buf,
						 notWritten + BUFSIZE);
		if (!obuf)
		{
		    _XSERVTransDisconnect(oc->trans_conn);
		    _XSERVTransClose(oc->trans_conn);
		    oc->trans_conn = NULL;
		    MarkClientException(who);
		    oco->count = 0;
		    return(-1);
		}
		oco->size = notWritten + BUFSIZE;
		oco->buf = obuf;
	    }

	    /* If the amount written extended into the padBuffer, then the
	       difference "extraCount - written" may be less than 0 */
	    if ((len = extraCount - written) > 0)
		memmove ((char *)oco->buf + oco->count,
			 extraBuf + written,
		       len);

	    oco->count = notWritten; /* this will include the pad */
	    /* return only the amount explicitly requested */
	    return extraCount;
	}
#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
	else if (errno == EMSGSIZE)
	{
	    todo >>= 1;
	}
#endif
	else
	{
	    if (oc->trans_conn)
	    {
		_XSERVTransDisconnect(oc->trans_conn);
		_XSERVTransClose(oc->trans_conn);
		oc->trans_conn = NULL;
	    }
	    MarkClientException(who);
	    oco->count = 0;
	    return(-1);
	}
    }

    /* everything was flushed out */
    oco->count = 0;
    /* check to see if this client was write blocked */
    if (AnyClientsWriteBlocked)
    {
	FD_CLR(oc->fd, &ClientsWriteBlocked);
 	if (! XFD_ANYSET(&ClientsWriteBlocked))
	    AnyClientsWriteBlocked = FALSE;
    }
    if (oco->size > BUFWATERMARK)
    {
	xfree(oco->buf);
	xfree(oco);
    }
    else
    {
	oco->next = FreeOutputs;
	FreeOutputs = oco;
    }
    oc->output = (ConnectionOutputPtr)NULL;
    return extraCount; /* return only the amount explicitly requested */
}

The xtrans library provides for another time the transport service. _XSERVTransWritev() is implemented as TRANS(Writev) in Xtrans.c:

int
TRANS(Writev) (XtransConnInfo ciptr, struct iovec *buf, int size)

{
    return ciptr->transptr->Writev (ciptr, buf, size);
}

If ciptr points for instance to TRANS(SocketTCPFuncs) the TRANS(Writev) becomes TRANS(SocketWritev), which for the UNIX system is resolved to a writev (Write Vector) call. See writev() man page about.

Recall that ciptr for the current Server was provided by the instruction that follows (see section 3.2.1):

if ((ciptr = TRANS(OpenCOTSServer(buffer))) == NULL)