Asynchronous reply processing using XCB and more efficient

sync batch processing.
This commit is contained in:
David Reveman 2008-08-01 00:32:45 -04:00
parent e92aa7be90
commit e43b271db3
9 changed files with 295 additions and 140 deletions

View file

@ -71,6 +71,9 @@
#include <GL/glxint.h>
#endif
#include <xcb/xcb.h>
#include <xcb/xcbext.h>
#include <X11/Xlib-xcb.h>
#include "dmxxlibio.h"
@ -85,10 +88,15 @@ typedef enum {
PosRelative
} PositionType;
typedef struct _DMXIgnore {
struct _DMXIgnore *next;
unsigned long sequence;
} DMXIgnore;
typedef struct _DMXSequence {
struct _DMXSequence *next;
unsigned long sequence;
} DMXSequence;
typedef struct _DMXQueue {
DMXSequence *head;
DMXSequence **tail;
} DMXQueue;
typedef struct _DMXPropTrans {
const char *name;
@ -113,9 +121,10 @@ typedef struct _DMXScreenInfo {
/*---------- Back-end X server information ----------*/
int fd;
int inDispatch;
xcb_connection_t *connection;
xcb_get_input_focus_cookie_t syncCookie;
xcb_get_input_focus_cookie_t sync;
Display *beDisplay; /**< Back-end X server's display */
int beWidth; /**< Width of BE display */
@ -217,8 +226,8 @@ typedef struct _DMXScreenInfo {
DMXStatInfo *stat; /**< Statistics about XSync */
Bool needsSync; /**< True if an XSync is pending */
DMXIgnore *ignoreHead;
DMXIgnore **ignoreTail;
DMXQueue ignore;
DMXQueue request;
#ifdef GLXEXT
/** Visual information for glxProxy */

View file

@ -1192,7 +1192,7 @@ static void dmxBECreateWindowTree(int idx)
#endif
XLIB_PROLOGUE (dmxScreen);
dmxSetIgnore (dmxScreen, NextRequest (dmxScreen->beDisplay));
dmxAddSequence (&dmxScreen->ignore, NextRequest (dmxScreen->beDisplay));
XMapWindow(dmxScreen->beDisplay, dmxScreen->rootWin);
XLIB_EPILOGUE (dmxScreen);
@ -2547,7 +2547,5 @@ int dmxDetachScreen(int idx)
RRGetInfo (screenInfo.screens[0]);
#endif
dmxDiscardIgnore (dmxScreen, ~0);
return 0; /* Success */
}

View file

@ -295,12 +295,13 @@ Bool dmxOpenDisplay(DMXScreenInfo *dmxScreen)
return FALSE;
dmxScreen->alive = 1;
dmxScreen->inDispatch = FALSE;
dmxScreen->fd = XConnectionNumber (dmxScreen->beDisplay);
dmxScreen->connection = XGetXCBConnection (dmxScreen->beDisplay);
XSetEventQueueOwner (dmxScreen->beDisplay, XCBOwnsEventQueue);
dmxScreen->syncCookie.sequence = 0;
dmxScreen->sync.sequence = 0;
AddEnabledDevice (dmxScreen->fd);

View file

@ -68,8 +68,6 @@ static jmp_buf jumpbuf;
static char **xbeArgv = 0;
static int nXbeArgv = 0;
static int xbePriority = 0;
static int
dmxAddXbeArguments (char **argv,
int n)

View file

@ -315,6 +315,39 @@ dmxSetWindowPixmap (WindowPtr pWin, PixmapPtr pPixmap)
DMX_WRAP(SetWindowPixmap, dmxSetWindowPixmap, dmxScreen, pScreen);
}
static void
dmxDiscardIgnore (DMXScreenInfo *dmxScreen,
unsigned long sequence)
{
while (dmxScreen->ignore.head)
{
if ((long) (sequence - dmxScreen->ignore.head->sequence) > 0)
{
DMXSequence *next = dmxScreen->ignore.head->next;
free (dmxScreen->ignore.head);
dmxScreen->ignore.head = next;
if (!dmxScreen->ignore.head)
dmxScreen->ignore.tail = &dmxScreen->ignore.head;
}
else
break;
}
}
static Bool
dmxShouldIgnore (DMXScreenInfo *dmxScreen,
unsigned long sequence)
{
dmxDiscardIgnore (dmxScreen, sequence);
if (!dmxScreen->ignore.head)
return FALSE;
return dmxScreen->ignore.head->sequence == sequence;
}
static Bool
dmxScreenEventCheckExpose (ScreenPtr pScreen,
xcb_generic_event_t *event)
@ -712,8 +745,15 @@ dmxScreenEventCheckIgnore (ScreenPtr pScreen,
return FALSE;
}
static Bool
dmxScreenReplyCheckIgnore (ScreenPtr pScreen,
xcb_generic_reply_t *reply)
{
return FALSE;
}
static void
dmxScreenCheckForError (ScreenPtr pScreen)
dmxScreenCheckForIOError (ScreenPtr pScreen)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
@ -723,7 +763,7 @@ dmxScreenCheckForError (ScreenPtr pScreen)
dmxScreen->alive = FALSE;
dmxLogOutput (dmxScreen, "Detect broken connection\n");
dmxLogOutput (dmxScreen, "Detected broken connection\n");
dmxDetachScreen (pScreen->myNum);
for (i = 0; i < dmxNumScreens; i++)
@ -736,6 +776,69 @@ dmxScreenCheckForError (ScreenPtr pScreen)
}
}
void
dmxBEDispatch (ScreenPtr pScreen)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
xcb_generic_event_t *event;
xcb_generic_reply_t *reply;
dmxScreen->inDispatch = TRUE;
while ((event = xcb_poll_for_event (dmxScreen->connection)))
{
if (!dmxScreenEventCheckInput (pScreen, event) &&
!dmxScreenEventCheckManageWindow (pScreen, event) &&
!dmxScreenEventCheckExpose (pScreen, event) &&
#ifdef RANDR
!dmxScreenEventCheckRR (pScreen, event) &&
#endif
!dmxScreenEventCheckIgnore (pScreen, event))
{
dmxLogOutput (dmxScreen, "unhandled event type %d\n",
event->response_type);
}
free (event);
}
while (dmxScreen->request.head &&
xcb_poll_for_reply (dmxScreen->connection,
dmxScreen->request.head->sequence,
(void *) &reply,
NULL))
{
DMXSequence *head = dmxScreen->request.head;
if (reply)
{
if (!dmxScreenReplyCheckSync (pScreen, reply) &&
!dmxScreenReplyCheckIgnore (pScreen, reply))
{
dmxLogOutput (dmxScreen,
"unhandled reply sequence %d\n",
reply->sequence);
}
free (reply);
}
else
{
dmxLogOutput (dmxScreen, "error sequence %d\n", head->sequence);
}
dmxScreen->request.head = head->next;
if (!dmxScreen->request.head)
dmxScreen->request.tail = &dmxScreen->request.head;
free (head);
}
dmxScreen->inDispatch = FALSE;
}
static void
dmxScreenBlockHandler (pointer blockData,
OSTimePtr pTimeout,
@ -747,7 +850,7 @@ dmxScreenBlockHandler (pointer blockData,
if (dmxScreen->beDisplay)
{
xcb_flush (dmxScreen->connection);
dmxScreenCheckForError (pScreen);
dmxScreenCheckForIOError (pScreen);
}
}
@ -760,28 +863,7 @@ dmxScreenWakeupHandler (pointer blockData,
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
if (dmxScreen->beDisplay)
{
xcb_generic_event_t *event;
while ((event = xcb_poll_for_event (dmxScreen->connection)))
{
if (!dmxScreenEventCheckInput (pScreen, event) &&
!dmxScreenEventCheckManageWindow (pScreen, event) &&
!dmxScreenEventCheckExpose (pScreen, event) &&
#ifdef RANDR
!dmxScreenEventCheckRR (pScreen, event) &&
#endif
!dmxScreenEventCheckIgnore (pScreen, event))
{
dmxLogOutput (dmxScreen, "unhandled event type %d\n",
event->response_type);
}
free (event);
}
}
dmxBEDispatch (pScreen);
}
static void
@ -834,8 +916,11 @@ Bool dmxScreenInit(int idx, ScreenPtr pScreen, int argc, char *argv[])
dmxGeneration = serverGeneration;
}
dmxScreen->ignoreHead = NULL;
dmxScreen->ignoreTail = &dmxScreen->ignoreHead;
dmxScreen->ignore.head = NULL;
dmxScreen->ignore.tail = &dmxScreen->ignore.head;
dmxScreen->request.head = NULL;
dmxScreen->request.tail = &dmxScreen->request.head;
#ifdef RANDR
dmxScreen->beRandr = FALSE;
@ -1170,6 +1255,9 @@ void dmxBECloseScreen(ScreenPtr pScreen)
/* Close display */
dmxCloseDisplay (dmxScreen);
dmxScreen->beDisplay = NULL;
dmxClearQueue (&dmxScreen->request);
dmxClearQueue (&dmxScreen->ignore);
}
/** Close screen number \a idx. */
@ -1290,52 +1378,35 @@ static Bool dmxSaveScreen(ScreenPtr pScreen, int what)
return TRUE;
}
void
dmxDiscardIgnore (DMXScreenInfo *dmxScreen,
unsigned long sequence)
{
while (dmxScreen->ignoreHead)
{
if ((long) (sequence - dmxScreen->ignoreHead->sequence) > 0)
{
DMXIgnore *next = dmxScreen->ignoreHead->next;
free (dmxScreen->ignoreHead);
dmxScreen->ignoreHead = next;
if (!dmxScreen->ignoreHead)
dmxScreen->ignoreTail = &dmxScreen->ignoreHead;
}
else
break;
}
}
void
dmxSetIgnore (DMXScreenInfo *dmxScreen,
unsigned long sequence)
{
DMXIgnore *i;
i = malloc (sizeof (DMXIgnore));
if (!i)
return;
i->sequence = sequence;
i->next = 0;
*(dmxScreen->ignoreTail) = i;
dmxScreen->ignoreTail = &i->next;
}
Bool
dmxShouldIgnore (DMXScreenInfo *dmxScreen,
unsigned long sequence)
dmxAddSequence (DMXQueue *q,
unsigned long sequence)
{
dmxDiscardIgnore (dmxScreen, sequence);
DMXSequence *s;
if (!dmxScreen->ignoreHead)
s = malloc (sizeof (DMXSequence));
if (!s)
return FALSE;
return dmxScreen->ignoreHead->sequence == sequence;
s->sequence = sequence;
s->next = 0;
*(q->tail) = s;
q->tail = &s->next;
return TRUE;
}
void
dmxClearQueue (DMXQueue *q)
{
while (q->head)
{
DMXSequence *head = q->head;
q->head = head->next;
free (head);
}
q->tail = &q->head;
}

View file

@ -47,9 +47,9 @@ extern Bool dmxScreenInit(int idx, ScreenPtr pScreen, int argc, char *argv[]);
extern void dmxBEScreenInit(int idx, ScreenPtr pScreen);
extern void dmxBECloseScreen(ScreenPtr pScreen);
extern void dmxBEDispatch (ScreenPtr pScreen);
extern void dmxDiscardIgnore (DMXScreenInfo *dmxScreen, unsigned long sequence);
extern void dmxSetIgnore (DMXScreenInfo *dmxScreen, unsigned long sequence);
extern Bool dmxShouldIgnore (DMXScreenInfo *dmxScreen, unsigned long sequence);
extern Bool dmxAddSequence (DMXQueue *q, unsigned long sequence);
extern void dmxClearQueue (DMXQueue *q);
#endif /* DMXSCRINIT_H */

View file

@ -55,42 +55,13 @@
#include "dmxstat.h"
#include "dmxlog.h"
#include "dmxextension.h"
#include "dmxscrinit.h"
#include <sys/time.h>
static int dmxSyncInterval = 100; /* Default interval in milliseconds */
static OsTimerPtr dmxSyncTimer;
static int dmxSyncPending = 0;
static int dmxSyncCookie = 0;
static void dmxWaitSync(DMXScreenInfo *dmxScreen)
{
if (dmxScreen->syncCookie.sequence)
{
if (dmxScreen->beDisplay)
{
if (!dmxStatInterval) {
XLIB_PROLOGUE (dmxScreen);
free (xcb_get_input_focus_reply (dmxScreen->connection,
dmxScreen->syncCookie,
NULL));
XLIB_EPILOGUE (dmxScreen);
} else {
struct timeval start, stop;
gettimeofday(&start, 0);
XLIB_PROLOGUE (dmxScreen);
free (xcb_get_input_focus_reply (dmxScreen->connection,
dmxScreen->syncCookie,
NULL));
XLIB_EPILOGUE (dmxScreen);
gettimeofday(&stop, 0);
dmxStatSync(dmxScreen, &stop, &start, dmxSyncPending);
}
}
dmxScreen->syncCookie.sequence = 0;
}
}
static int dmxSyncRequest = 0;
static void dmxDoSync(DMXScreenInfo *dmxScreen)
{
@ -98,33 +69,86 @@ static void dmxDoSync(DMXScreenInfo *dmxScreen)
if (!dmxScreen->beDisplay)
{
dmxScreen->syncCookie.sequence = 0;
dmxScreen->sync.sequence = 0;
return; /* FIXME: Is this correct behavior for sync stats? */
}
dmxScreen->syncCookie =
xcb_get_input_focus_unchecked (dmxScreen->connection);
if (dmxScreen->sync.sequence)
return;
dmxSyncCookie++;
dmxScreen->sync = xcb_get_input_focus_unchecked (dmxScreen->connection);
dmxAddSequence (&dmxScreen->request, dmxScreen->sync.sequence);
dmxSyncRequest++;
}
static CARD32 dmxSyncCallback(OsTimerPtr timer, CARD32 time, pointer arg)
{
int i;
if (dmxSyncCookie) {
while (dmxSyncRequest)
{
fd_set rfds;
int ret, fd = 0;
/* timer expired and there is pending sync replies that need
to be waited for before we can allow further processing
of client requests */
FD_ZERO (&rfds);
for (i = 0; i < dmxNumScreens; i++)
if (dmxScreens[i].syncCookie.sequence) dmxWaitSync(&dmxScreens[i]);
dmxSyncCookie = 0;
{
if (dmxScreens[i].beDisplay)
{
xcb_flush (dmxScreens[i].connection);
if (xcb_connection_has_error (dmxScreens[i].connection))
{
if (dmxScreens[i].sync.sequence)
{
dmxScreens[i].sync.sequence = 0;
dmxSyncRequest--;
}
}
else
{
FD_SET (dmxScreens[i].fd, &rfds);
if (dmxScreens[i].fd > fd)
fd = dmxScreens[i].fd;
}
}
}
if (fd)
{
do {
ret = select (fd + 1, &rfds, 0, 0, 0);
} while (ret == -1 && errno == EINTR);
}
dmxSyncPending++;
for (i = 0; i < dmxNumScreens; i++)
if (dmxScreens[i].beDisplay)
dmxBEDispatch (screenInfo.screens[i]);
dmxSyncPending--;
}
if (dmxSyncPending) {
if (dmxSyncPending)
{
for (i = 0; i < dmxNumScreens; i++)
if (dmxScreens[i].needsSync) dmxDoSync(&dmxScreens[i]);
if (dmxScreens[i].needsSync)
dmxDoSync (&dmxScreens[i]);
dmxSyncPending = 0;
if (dmxSyncCookie)
if (dmxSyncRequest)
return dmxSyncInterval;
}
dmxSyncTimer = NULL;
return 0; /* Do not place on queue again */
}
@ -185,34 +209,81 @@ void dmxSync(DMXScreenInfo *dmxScreen, Bool now)
now = TRUE;
dmxGeneration = serverGeneration;
}
/* Queue sync */
/* Queue sync */
if (dmxScreen) {
if (now && dmxScreen->inDispatch)
{
dmxLog (dmxWarning,
"Immediate sync from within back-end dispatch\n");
free (xcb_get_input_focus_reply
(dmxScreen->connection,
xcb_get_input_focus_unchecked (dmxScreen->connection),
NULL));
return;
}
dmxScreen->needsSync = TRUE;
++dmxSyncPending;
}
/* Do sync or set time for later */
if (now || !dmxScreen) {
if (!TimerForce(dmxSyncTimer)) dmxSyncCallback(NULL, 0, NULL);
/* Do sync or set time for later */
if (now || !dmxScreen)
{
if (dmxSyncTimer)
{
TimerFree (dmxSyncTimer);
dmxSyncTimer = NULL;
}
while (dmxSyncRequest || dmxSyncPending)
dmxSyncCallback (NULL, 0, NULL);
/* At this point, dmxSyncPending == 0 because
* dmxSyncCallback must have been called. */
if (dmxSyncPending)
dmxLog(dmxFatal, "dmxSync(%s,%d): dmxSyncPending = %d\n",
dmxScreen ? dmxScreen->display : "", now, dmxSyncPending);
} else {
if (dmxSyncCookie == 0 && dmxSyncPending == 1)
dmxSyncTimer = TimerSet(dmxSyncTimer, 0, dmxSyncInterval,
dmxSyncCallback, NULL);
}
} else {
/* If dmxSyncInterval is not being used,
* then all the backends are already
* up-to-date. */
else if (!dmxSyncTimer)
{
dmxSyncTimer = TimerSet(dmxSyncTimer, 0, dmxSyncInterval,
dmxSyncCallback, NULL);
}
}
else
{
/* If dmxSyncInterval is not being used,
* then all the backends are already
* up-to-date. */
if (dmxScreen)
{
dmxDoSync(dmxScreen);
dmxWaitSync(dmxScreen);
dmxSyncCallback(NULL, 0, NULL);
}
}
}
Bool
dmxScreenReplyCheckSync (ScreenPtr pScreen,
xcb_generic_reply_t *reply)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
if (reply->sequence != dmxScreen->sync.sequence)
return FALSE;
dmxScreen->sync.sequence = 0;
dmxSyncRequest--;
if (dmxSyncRequest == 0)
{
if (dmxSyncPending == 0)
{
TimerFree (dmxSyncTimer);
dmxSyncTimer = NULL;
}
}
return TRUE;
}

View file

@ -40,4 +40,6 @@
extern void dmxSyncActivate(const char *interval);
extern void dmxSyncInit(void);
extern void dmxSync(DMXScreenInfo *dmxScreen, Bool now);
extern Bool dmxScreenReplyCheckSync (ScreenPtr pScreen,
xcb_generic_reply_t *reply);
#endif

View file

@ -179,6 +179,11 @@ void dmxResizeScreenWindow(ScreenPtr pScreen,
dmxSync(dmxScreen, False);
}
static void dmxSetIgnore (DMXScreenInfo *dmxScreen, unsigned int sequence)
{
dmxAddSequence (&dmxScreen->ignore, sequence);
}
/** Change the location and size of the "root" window. Called from
* #dmxReconfigureRootWindow(). */
void dmxResizeRootWindow(WindowPtr pRoot,