Hands-on Projects for the Linux Graphics Subsystem
|
ReadRequestFromClient() as seen in section 3.2.5 is called from Dispatch() as:
result = ReadRequestFromClient(client); |
For ReadRequestFromClient() we read at its comment:
/***************************************************************** * ReadRequestFromClient * Returns one request in client->requestBuffer. The request * length will be in client->req_len. Return status is: * * > 0 if successful, specifies length in bytes of the request * = 0 if entire request is not yet available * < 0 if client should be terminated * * The request returned must be contiguous so that it can be * cast in the dispatcher to the correct request type. Because requests * are variable length, ReadRequestFromClient() must look at the first 4 * or 8 bytes of a request to determine the length (the request length is * in the 3rd and 4th bytes of the request unless it is a Big Request * (see the Big Request Extension), in which case the 3rd and 4th bytes * are zero and the following 4 bytes are the request length. * * Note: in order to make the server scheduler (WaitForSomething()) * "fair", the ClientsWithInput mask is used. This mask tells which * clients have FULL requests left in their buffers. Clients with * partial requests require a read. Basically, client buffers * are drained before select() is called again. But, we can't keep * reading from a client that is sending buckets of data (or has * a partial request) because others clients need to be scheduled. *****************************************************************/ |
ReadRequestFromClient() source code uses the client struct, which we met first when EstablishNewConnections() was called at section 2.2. Since this was a short introduction on the server functionality EstablishNewConnections() is examined more detailed at section 3.2.5.5. As we mentioned there struct client is defined in dixstruct.h as:
typedef struct _Client { int index; Mask clientAsMask; pointer requestBuffer; pointer osPrivate; /* for OS layer, including scheduler */ Bool swapped; ReplySwapPtr pSwapReplyFunc; XID errorValue; int sequence; int closeDownMode; int clientGone; int noClientException; /* this client died or needs to be * killed */ DrawablePtr lastDrawable; Drawable lastDrawableID; GCPtr lastGC; GContext lastGCID; SaveSetElt *saveSet; int numSaved; pointer screenPrivate[MAXSCREENS]; int (**requestVector) ( ClientPtr /* pClient */); CARD32 req_len; /* length of current request */ Bool big_requests; /* supports large requests */ int priority; ClientState clientState; DevUnion *devPrivates; #ifdef XKB unsigned short xkbClientFlags; unsigned short mapNotifyMask; unsigned short newKeyboardNotifyMask; unsigned short vMajor,vMinor; KeyCode minKC,maxKC; #endif #ifdef DEBUG unsigned char requestLog[MAX_REQUEST_LOG]; int requestLogIndex; #endif #ifdef LBX int (*readRequest)(ClientPtr /*client*/); #endif unsigned long replyBytesRemaining; #ifdef XCSECURITY XID authId; unsigned int trustLevel; pointer (* CheckAccess)( ClientPtr /*pClient*/, XID /*id*/, RESTYPE /*classes*/, Mask /*access_mode*/, pointer /*resourceval*/); #endif #ifdef XAPPGROUP struct _AppGroupRec* appgroup; #endif struct _FontResolution * (*fontResFunc) ( /* no need for font.h */ ClientPtr /* pClient */, int * /* num */); #ifdef SMART_SCHEDULE int smart_priority; long smart_start_tick; long smart_stop_tick; long smart_check_tick; #endif } ClientRec; |
From the client the osPivate field is used to hold the input buffer for the client. Also the osPrivate fd is used as the file descriptor os the client:
OsCommPtr oc = (OsCommPtr)client->osPrivate; ConnectionInputPtr oci = oc->input; int fd = oc->fd; |
where OsCommPtr is defined in osdep.h as:
typedef struct _osComm { int fd; ConnectionInputPtr input; ConnectionOutputPtr output; XID auth_id; /* authorization id */ #ifdef K5AUTH k5_state authstate; /* state of setup auth conversation */ #endif CARD32 conn_time; /* timestamp if not established, else 0 */ struct _XtransConnInfo *trans_conn; /* transport connection object */ #ifdef LBX OsProxyPtr proxy; ConnectionInputPtr largereq; OsCloseFunc Close; OsFlushFunc Flush; #endif } OsCommRec, *OsCommPtr; |
and ConnectionInputPtr is defined in the same file as:
typedef struct _connectionInput { struct _connectionInput *next; char *buffer; /* contains current client input */ char *bufptr; /* pointer to current start of data */ int bufcnt; /* count of bytes in buffer */ int lenLastReq; int size; } ConnectionInput, *ConnectionInputPtr; |
If oci (of type ConnectionInputPtr) is NULL then it is allocated in ReadRequestFromClient() by calling AllocateInputBuffer(). Its buffer field (oci->buffer) is BUFSIZE in size, defined in osdep.h to 4096.
At this point is useful to illustrate the comment from io.c:
* A lot of the code in this file manipulates a ConnectionInputPtr: * * ----------------------------------------------- * |------- bufcnt ------->| | | * | |- gotnow ->| | | * | |-------- needed ------>| | * |-----------+--------- size --------+---------->| * ----------------------------------------------- * ^ ^ * | | * buffer bufptr * * buffer is a pointer to the start of the buffer. * bufptr points to the start of the current request. * bufcnt counts how many bytes are in the buffer. * size is the size of the buffer in bytes. |
and then continue with the ReadRequestFromClient() source:
/* advance to start of next request */ oci->bufptr += oci->lenLastReq; |
At the first time, when AllocateInputBuffer() is called, lenLastReq is initialized to zero.
'gotnow' the bytes obtained so far is calculated and in the case they are less than xReq the bytes 'needed' become the xReq header size
gotnow = oci->bufcnt + oci->buffer - oci->bufptr; if (gotnow < sizeof(xReq)) { /* We don't have an entire xReq yet. Can't tell how big * the request will be until we get the whole xReq. */ needed = sizeof(xReq); need_header = TRUE; } |
xReq, the X Protocol request header is defined in Xproto.h as:
/* Request structure */ typedef struct _xReq { CARD8 reqType; CARD8 data; /* meaning depends on request type */ CARD16 length B16; /* length in 4 bytes quantities of whole request, including this header */ } xReq; |
CARD8 and CARD16 are defined in Xmd.h
In the case a whole xReq is obtained we calculate from its req_len field the 'needed' bytes
else { /* We have a whole xReq. We can tell how big the whole * request will be unless it is a Big Request. */ request = (xReq *)oci->bufptr; needed = get_req_len(request, client); . . . client->req_len = needed; needed <<= 2; /* needed is in bytes now */ } |
macro get_req_len obtains the length of the request from the header struct xReq (little and big endianess is considered):
#define get_req_len(req,cli) ((cli)->swapped ? \ lswaps((req)->length) : (req)->length) |
needed (the rest of the request) is calculated in bytes by left-shifting by 2. This actually multiplies the four-byte request length quantities (see xReq struct comment) by four and turns it to bytes.
The code bellow examines the condition:
if (gotnow < needed)
The bytes needed are obtained with a _XSERVTransRead() call. Using the following macro from Xtrans.h:
#define TRANS(func) _XSERVTrans##func |
Therefore in xtrans.c TRANS(Read) is replaced by _XSERVTransRead and the following is the implementation of _XSERVTransRead:
int TRANS(Read) (XtransConnInfo ciptr, char *buf, int size) { return ciptr->transptr->Read (ciptr, buf, size); } |
As seen in the code bellow (bold characters) the first argument (oc->trans_conn) which substitutes the ciptr parameter takes a value at EstablishNewConnections() , called from WaitForSomething() as seen in section 3.2.5.2. We examine EstablishNewConnections() at section 3.2.5.5.
if (gotnow < needed) { /* Need to read more data, either so that we can get a * complete xReq (if need_header is TRUE), a complete * xBigReq (if move_header is TRUE), or the rest of the * request (if need_header and move_header are both FALSE). */ oci->lenLastReq = 0; if (needed > MAXBUFSIZE) { /* request is too big for us to handle */ YieldControlDeath(); return -1; } if ((gotnow == 0) || ((oci->bufptr - oci->buffer + needed) > oci->size)) { /* no data, or the request is too big to fit in the buffer */ if ((gotnow > 0) && (oci->bufptr != oci->buffer)) /* save the data we've already read */ memmove(oci->buffer, oci->bufptr, gotnow); if (needed > oci->size) { /* make buffer bigger to accomodate request */ char *ibuf; ibuf = (char *)xrealloc(oci->buffer, needed); if (!ibuf) { YieldControlDeath(); return -1; } oci->size = needed; oci->buffer = ibuf; } oci->bufptr = oci->buffer; oci->bufcnt = gotnow; } /* XXX this is a workaround. This function is sometimes called * after the trans_conn has been freed. In this case trans_conn * will be null. Really ought to restructure things so that we * never get here in those circumstances. */ if (!oc->trans_conn) { /* treat as if an error occured on the read, which is what * used to happen */ YieldControlDeath(); return -1; } #ifdef LBX if (oc->proxy && oc->proxy->compHandle) result = (*oc->proxy->streamOpts.streamCompRead)(fd, (unsigned char *)oci->buffer + oci->bufcnt, oci->size - oci->bufcnt); else #endif result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt, oci->size - oci->bufcnt); if (result <= 0) { if ((result < 0) && ETEST(errno)) { #if defined(SVR4) && defined(i386) && !defined(sun) #if defined(LBX) && 0 /* * For LBX connections, we can get a valid EWOULDBLOCK * There is probably a better way of distinguishing LBX * connections, but this works. (DHD) */ extern int LbxRead(); if (oc->Read == LbxRead) #else if (0) #endif #endif { YieldControlNoInput(); return 0; } } YieldControlDeath(); return -1; } oci->bufcnt += result; gotnow += result; /* free up some space after huge requests */ if ((oci->size > BUFWATERMARK) && (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) { char *ibuf; ibuf = (char *)xrealloc(oci->buffer, BUFSIZE); if (ibuf) { oci->size = BUFSIZE; oci->buffer = ibuf; oci->bufptr = ibuf + oci->bufcnt - gotnow; } } if (need_header && gotnow >= needed) { /* We wanted an xReq, now we've gotten it. */ request = (xReq *)oci->bufptr; needed = get_req_len(request, client); #ifdef BIGREQS if (!needed && client->big_requests) { move_header = TRUE; if (gotnow < sizeof(xBigReq)) needed = sizeof(xBigReq) >> 2; else needed = get_big_req_len(request, client); } #endif client->req_len = needed; needed <<= 2; } if (gotnow < needed) { /* Still don't have enough; punt. */ YieldControlNoInput(); return 0; } } |
The next part examines the condition
if (needed == 0)
In that case the current request is obtained in full length and another needs to be read from the buffer. Therefore needed becomes the header of the request that needs to be read:
if (needed == 0) { #ifdef BIGREQS if (client->big_requests) needed = sizeof(xBigReq); else #endif needed = sizeof(xReq); } oci->lenLastReq = needed; |
Last case to examine is gotnow > needed (Notice that needed can also be the xReq header). In that case if 'gotnow' is bigger than xReq and also bigger than the request length (obtained from xReq) the specific client's file descriptor (fd) is placed via FD_SET to ClientsWithInput, the fd set for the clients ready to be read.
As seen in section 1.2 a client can prepare multiple requests and send them with a XFlush(). Therefore other requests can also be fetched to a buffer with the last read(). |
gotnow -= needed; if (gotnow >= sizeof(xReq)) { request = (xReq *)(oci->bufptr + needed); if (gotnow >= (result = (get_req_len(request, client) << 2)) #ifdef BIGREQS && (result || (client->big_requests && (gotnow >= sizeof(xBigReq) && gotnow >= (get_big_req_len(request, client) << 2)))) #endif ) FD_SET(fd, &ClientsWithInput); else { #ifdef SMART_SCHEDULE if (!SmartScheduleDisable) FD_CLR(fd, &ClientsWithInput); else #endif YieldControlNoInput(); } } |
In the case gotnow < sizeof(xReq) timesThisConnection is increased by one and if MAX_TIMES_PER is reached macro YieldControl() is called. This is defined as:
#define YieldControl() \ { isItTimeToYield = TRUE; \ |
This makes the while loop seen in the Dispatch() overview at section 3.2.5 break.
else { if (!gotnow) AvailableInput = oc; #ifdef SMART_SCHEDULE if (!SmartScheduleDisable) FD_CLR(fd, &ClientsWithInput); else #endif YieldControlNoInput(); } #ifdef SMART_SCHEDULE if (SmartScheduleDisable) #endif if (++timesThisConnection >= MAX_TIMES_PER) YieldControl(); #ifdef BIGREQS if (move_header) { request = (xReq *)oci->bufptr; oci->bufptr += (sizeof(xBigReq) - sizeof(xReq)); *(xReq *)oci->bufptr = *request; oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq)); client->req_len -= (sizeof(xBigReq) - sizeof(xReq)) >> 2; } #endif client->requestBuffer = (pointer)oci->bufptr; #ifdef DEBUG_COMMUNICATION { xReq *req = client->requestBuffer; ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n", client->index,req->reqType,req->data,req->length); } #endif return needed; } |
The max number the server can read from the same client is represented by MAX_TIMES_PER and it is defined to io.c as:
#define MAX_TIMES_PER 10Therefore the 'purple' while loop from the Dispatch() overview at section 3.2.5, shown bellow as well, can run at most 10 times for a certain X client.
|
We reach now at the heart of the X Server at the point where X Client's requests are dispatched. We previously saw the interaction of X Client- X Server at section 2.3.
The code line that calls the specific routine according to the request type the X Client sent is obtained from requestVector[] using as index the MAJOROP:
result = (* client->requestVector[MAJOROP])(client); |
MAJOROP is defined in the same file as the request type of the xReq:
#define MAJOROP ((xReq *)client->requestBuffer)->reqType |
requestVector[] can either be ProcVector[] or SwappedProcVector[] according to the Big or Small Endianess of the system. Both are defined in tables.c and InitialVector[] intervene in the process before one of the two vectors is selected. This is described in section 3.2.5.5. The contents of the vectors are the functions that are implemented for a certain X Client request using as index in these 'tables' the Request type of the client's message. As seen above this is described also as MAJOROP in the current file. ProcVector[] for instance has the form:
int (* ProcVector[256]) ( ClientPtr /* client */ ) = { ProcBadRequest, ProcCreateWindow, ProcChangeWindowAttributes, ProcGetWindowAttributes, ProcDestroyWindow, ProcDestroySubwindows, /* 5 */ ProcChangeSaveSet, ProcReparentWindow, ProcMapWindow, ProcMapSubwindows, ProcUnmapWindow, /* 10 */ ProcUnmapSubwindows, ProcConfigureWindow, ProcCirculateWindow, ProcGetGeometry, ProcQueryTree, /* 15 */ ProcInternAtom, ProcGetAtomName, ProcChangeProperty, ProcDeleteProperty, ProcGetProperty, /* 20 */ ProcListProperties, ProcSetSelectionOwner, ProcGetSelectionOwner, ProcConvertSelection, ProcSendEvent, /* 25 */ ProcGrabPointer, ProcUngrabPointer, ProcGrabButton, ProcUngrabButton, ProcChangeActivePointerGrab, /* 30 */ ProcGrabKeyboard, ProcUngrabKeyboard, ProcGrabKey, ProcUngrabKey, ProcAllowEvents, /* 35 */ ProcGrabServer, ProcUngrabServer, ProcQueryPointer, ProcGetMotionEvents, ProcTranslateCoords, /* 40 */ ProcWarpPointer, ProcSetInputFocus, ProcGetInputFocus, ProcQueryKeymap, ProcOpenFont, /* 45 */ ProcCloseFont, ProcQueryFont, ProcQueryTextExtents, ProcListFonts, ProcListFontsWithInfo, /* 50 */ ProcSetFontPath, ProcGetFontPath, ProcCreatePixmap, ProcFreePixmap, ProcCreateGC, /* 55 */ ProcChangeGC, ProcCopyGC, ProcSetDashes, ProcSetClipRectangles, ProcFreeGC, /* 60 */ ProcClearToBackground, ProcCopyArea, ProcCopyPlane, ProcPolyPoint, ProcPolyLine, /* 65 */ ProcPolySegment, ProcPolyRectangle, ProcPolyArc, ProcFillPoly, ProcPolyFillRectangle, /* 70 */ ProcPolyFillArc, ProcPutImage, ProcGetImage, ProcPolyText, ProcPolyText, /* 75 */ ProcImageText8, ProcImageText16, ProcCreateColormap, ProcFreeColormap, ProcCopyColormapAndFree, /* 80 */ ProcInstallColormap, ProcUninstallColormap, ProcListInstalledColormaps, ProcAllocColor, ProcAllocNamedColor, /* 85 */ ProcAllocColorCells, ProcAllocColorPlanes, ProcFreeColors, ProcStoreColors, ProcStoreNamedColor, /* 90 */ ProcQueryColors, ProcLookupColor, ProcCreateCursor, ProcCreateGlyphCursor, ProcFreeCursor, /* 95 */ ProcRecolorCursor, ProcQueryBestSize, ProcQueryExtension, ProcListExtensions, ProcChangeKeyboardMapping, /* 100 */ ProcGetKeyboardMapping, ProcChangeKeyboardControl, ProcGetKeyboardControl, ProcBell, ProcChangePointerControl, /* 105 */ ProcGetPointerControl, ProcSetScreenSaver, ProcGetScreenSaver, ProcChangeHosts, ProcListHosts, /* 110 */ ProcChangeAccessControl, ProcChangeCloseDownMode, ProcKillClient, ProcRotateProperties, ProcForceScreenSaver, /* 115 */ ProcSetPointerMapping, ProcGetPointerMapping, ProcSetModifierMapping, ProcGetModifierMapping, 0, /* 120 */ 0, 0, 0, 0, 0, /* 125 */ 0, ProcNoOperation }; |
As argument the specific routine takes the client struct for the certain client that sent the request.
if (result != Success) { if (client->noClientException != Success) CloseDownClient(client); else SendErrorToClient(client, MAJOROP, MinorOpcodeOfRequest(client), client->errorValue, result); break; } |