Home | History | Annotate | Line # | Download | only in dmx
      1 /*
      2  * Copyright 2002-2004 Red Hat Inc., Durham, North Carolina.
      3  *
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining
      7  * a copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation on the rights to use, copy, modify, merge,
     10  * publish, distribute, sublicense, and/or sell copies of the Software,
     11  * and to permit persons to whom the Software is furnished to do so,
     12  * subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial
     16  * portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
     22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     25  * SOFTWARE.
     26  */
     27 
     28 /*
     29  * Authors:
     30  *   Rickard E. (Rik) Faith <faith (at) redhat.com>
     31  *
     32  */
     33 
     34 /** \file
     35  *
     36  * The DMX server code is written to call #dmxSync() whenever an XSync()
     37  * might be necessary.  However, since XSync() requires a two way
     38  * communication with the other X server, eliminating unnecessary
     39  * XSync() calls is a key performance optimization.  Support for this
     40  * optimization is provided here.  Statistics about XSync() calls and
     41  * latency are gathered in \a dmxstat.c.
     42  *
     43  * During the initial conversion from calling XSync() immediately to the
     44  * XSync() batching method implemented in this file, it was noted that,
     45  * out of more than 300 \a x11perf tests, 8 tests became more than 100
     46  * times faster, with 68 more than 50X faster, 114 more than 10X faster,
     47  * and 181 more than 2X faster. */
     48 
     49 #ifdef HAVE_DMX_CONFIG_H
     50 #include <dmx-config.h>
     51 #endif
     52 
     53 #include "dmx.h"
     54 #include "dmxsync.h"
     55 #include "dmxstat.h"
     56 #include "dmxlog.h"
     57 #include <sys/time.h>
     58 
     59 static int        dmxSyncInterval = 100; /* Default interval in milliseconds */
     60 static OsTimerPtr dmxSyncTimer;
     61 static int        dmxSyncPending;
     62 
     63 static void dmxDoSync(DMXScreenInfo *dmxScreen)
     64 {
     65     dmxScreen->needsSync = FALSE;
     66 
     67     if (!dmxScreen->beDisplay)
     68 	return; /* FIXME: Is this correct behavior for sync stats? */
     69 
     70     if (!dmxStatInterval) {
     71         XSync(dmxScreen->beDisplay, False);
     72     } else {
     73         struct timeval start, stop;
     74 
     75         gettimeofday(&start, 0);
     76         XSync(dmxScreen->beDisplay, False);
     77         gettimeofday(&stop, 0);
     78         dmxStatSync(dmxScreen, &stop, &start, dmxSyncPending);
     79     }
     80 }
     81 
     82 static CARD32 dmxSyncCallback(OsTimerPtr timer, CARD32 time, pointer arg)
     83 {
     84     int           i;
     85 
     86     if (dmxSyncPending) {
     87         for (i = 0; i < dmxNumScreens; i++) {
     88             DMXScreenInfo *dmxScreen = &dmxScreens[i];
     89             if (dmxScreen->needsSync) dmxDoSync(dmxScreen);
     90         }
     91     }
     92     dmxSyncPending = 0;
     93     return 0;                   /* Do not place on queue again */
     94 }
     95 
     96 static void dmxSyncBlockHandler(pointer blockData, OSTimePtr pTimeout,
     97                                 pointer pReadMask)
     98 {
     99     TimerForce(dmxSyncTimer);
    100 }
    101 
    102 static void dmxSyncWakeupHandler(pointer blockData, int result,
    103                                  pointer pReadMask)
    104 {
    105 }
    106 
    107 /** Request the XSync() batching optimization with the specified \a
    108  * interval (in mS).  If the \a interval is 0, 100mS is used.  If the \a
    109  * interval is less than 0, then the XSync() batching optimization is
    110  * not requested (e.g., so the -syncbatch -1 command line option can
    111  * turn off the default 100mS XSync() batching).
    112  *
    113  * Note that the parameter to this routine is a string, since it will
    114  * usually be called from #ddxProcessArgument in \a dmxinit.c */
    115 void dmxSyncActivate(const char *interval)
    116 {
    117     dmxSyncInterval = (interval ? atoi(interval) : 100);
    118 
    119     if (dmxSyncInterval < 0) dmxSyncInterval = 0;
    120 }
    121 
    122 /** Initialize the XSync() batching optimization, but only if
    123  * #dmxSyncActivate was last called with a non-negative value. */
    124 void dmxSyncInit(void)
    125 {
    126     if (dmxSyncInterval) {
    127         RegisterBlockAndWakeupHandlers(dmxSyncBlockHandler,
    128                                        dmxSyncWakeupHandler,
    129                                        NULL);
    130         dmxLog(dmxInfo, "XSync batching with %d ms interval\n",
    131                dmxSyncInterval);
    132     } else {
    133         dmxLog(dmxInfo, "XSync batching disabled\n");
    134     }
    135 }
    136 
    137 /** Request an XSync() to the display used by \a dmxScreen.  If \a now
    138  * is TRUE, call XSync() immediately instead of waiting for the next
    139  * XSync() batching point.  Note that if XSync() batching was deselected
    140  * with #dmxSyncActivate() before #dmxSyncInit() was called, then no
    141  * XSync() batching is performed and this function always calles XSync()
    142  * immediately.
    143  *
    144  * (Note that this function uses TimerSet but works correctly in the
    145  * face of a server generation.  See the source for details.)
    146  *
    147  * If \a dmxScreen is \a NULL, then all pending syncs will be flushed
    148  * immediately.
    149  */
    150 void dmxSync(DMXScreenInfo *dmxScreen, Bool now)
    151 {
    152     static unsigned long dmxGeneration = 0;
    153 
    154     if (dmxSyncInterval) {
    155         if (dmxGeneration != serverGeneration) {
    156             /* Server generation does a TimerInit, which frees all
    157              * timers.  So, at this point dmxSyncTimer is either:
    158              * 1) NULL, iff dmxGeneration == 0,
    159              * 2) freed, if it was on a queue (dmxSyncPending != 0), or
    160              * 3) allocated, if it wasn't on a queue (dmxSyncPending == 0)
    161              */
    162             if (dmxSyncTimer && !dmxSyncPending) free(dmxSyncTimer);
    163             dmxSyncTimer  = NULL;
    164             now           = TRUE;
    165             dmxGeneration = serverGeneration;
    166         }
    167                                 /* Queue sync */
    168         if (dmxScreen) {
    169             dmxScreen->needsSync = TRUE;
    170             ++dmxSyncPending;
    171         }
    172 
    173                                 /* Do sync or set time for later */
    174         if (now || !dmxScreen) {
    175             if (!TimerForce(dmxSyncTimer)) dmxSyncCallback(NULL, 0, NULL);
    176             /* At this point, dmxSyncPending == 0 because
    177              * dmxSyncCallback must have been called. */
    178             if (dmxSyncPending)
    179                 dmxLog(dmxFatal, "dmxSync(%s,%d): dmxSyncPending = %d\n",
    180                        dmxScreen ? dmxScreen->name : "", now, dmxSyncPending);
    181         } else {
    182             dmxScreen->needsSync = TRUE;
    183             if (dmxSyncPending == 1)
    184                 dmxSyncTimer = TimerSet(dmxSyncTimer, 0, dmxSyncInterval,
    185                                         dmxSyncCallback, NULL);
    186         }
    187     } else {
    188                                 /* If dmxSyncInterval is not being used,
    189                                  * then all the backends are already
    190                                  * up-to-date. */
    191         if (dmxScreen) dmxDoSync(dmxScreen);
    192     }
    193 }
    194