194 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright 2002-2004 Red Hat Inc., Durham, North Carolina.
 | |
|  *
 | |
|  * All Rights Reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining
 | |
|  * a copy of this software and associated documentation files (the
 | |
|  * "Software"), to deal in the Software without restriction, including
 | |
|  * without limitation on the rights to use, copy, modify, merge,
 | |
|  * publish, distribute, sublicense, and/or sell copies of the Software,
 | |
|  * and to permit persons to whom the Software is furnished to do so,
 | |
|  * subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice (including the
 | |
|  * next paragraph) shall be included in all copies or substantial
 | |
|  * portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
|  * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
 | |
|  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | |
|  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | |
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
|  * SOFTWARE.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Authors:
 | |
|  *   Rickard E. (Rik) Faith <faith@redhat.com>
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  *
 | |
|  * The DMX server code is written to call #dmxSync() whenever an XSync()
 | |
|  * might be necessary.  However, since XSync() requires a two way
 | |
|  * communication with the other X server, eliminating unnecessary
 | |
|  * XSync() calls is a key performance optimization.  Support for this
 | |
|  * optimization is provided here.  Statistics about XSync() calls and
 | |
|  * latency are gathered in \a dmxstat.c.
 | |
|  *
 | |
|  * During the initial conversion from calling XSync() immediately to the
 | |
|  * XSync() batching method implemented in this file, it was noted that,
 | |
|  * out of more than 300 \a x11perf tests, 8 tests became more than 100
 | |
|  * times faster, with 68 more than 50X faster, 114 more than 10X faster,
 | |
|  * and 181 more than 2X faster. */
 | |
| 
 | |
| #ifdef HAVE_DMX_CONFIG_H
 | |
| #include <dmx-config.h>
 | |
| #endif
 | |
| 
 | |
| #include "dmx.h"
 | |
| #include "dmxsync.h"
 | |
| #include "dmxstat.h"
 | |
| #include "dmxlog.h"
 | |
| #include <sys/time.h>
 | |
| 
 | |
| static int        dmxSyncInterval = 100; /* Default interval in milliseconds */
 | |
| static OsTimerPtr dmxSyncTimer;
 | |
| static int        dmxSyncPending;
 | |
| 
 | |
| static void dmxDoSync(DMXScreenInfo *dmxScreen)
 | |
| {
 | |
|     dmxScreen->needsSync = FALSE;
 | |
| 
 | |
|     if (!dmxScreen->beDisplay)
 | |
| 	return; /* FIXME: Is this correct behavior for sync stats? */
 | |
| 
 | |
|     if (!dmxStatInterval) {
 | |
|         XSync(dmxScreen->beDisplay, False);
 | |
|     } else {
 | |
|         struct timeval start, stop;
 | |
|         
 | |
|         gettimeofday(&start, 0);
 | |
|         XSync(dmxScreen->beDisplay, False);
 | |
|         gettimeofday(&stop, 0);
 | |
|         dmxStatSync(dmxScreen, &stop, &start, dmxSyncPending);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static CARD32 dmxSyncCallback(OsTimerPtr timer, CARD32 time, pointer arg)
 | |
| {
 | |
|     int           i;
 | |
| 
 | |
|     if (dmxSyncPending) {
 | |
|         for (i = 0; i < dmxNumScreens; i++) {
 | |
|             DMXScreenInfo *dmxScreen = &dmxScreens[i];
 | |
|             if (dmxScreen->needsSync) dmxDoSync(dmxScreen);
 | |
|         }
 | |
|     }
 | |
|     dmxSyncPending = 0;
 | |
|     return 0;                   /* Do not place on queue again */
 | |
| }
 | |
| 
 | |
| static void dmxSyncBlockHandler(pointer blockData, OSTimePtr pTimeout,
 | |
|                                 pointer pReadMask)
 | |
| {
 | |
|     TimerForce(dmxSyncTimer);
 | |
| }
 | |
| 
 | |
| static void dmxSyncWakeupHandler(pointer blockData, int result,
 | |
|                                  pointer pReadMask)
 | |
| {
 | |
| }
 | |
| 
 | |
| /** Request the XSync() batching optimization with the specified \a
 | |
|  * interval (in mS).  If the \a interval is 0, 100mS is used.  If the \a
 | |
|  * interval is less than 0, then the XSync() batching optimization is
 | |
|  * not requested (e.g., so the -syncbatch -1 command line option can
 | |
|  * turn off the default 100mS XSync() batching).
 | |
|  *
 | |
|  * Note that the parameter to this routine is a string, since it will
 | |
|  * usually be called from #ddxProcessArgument in \a dmxinit.c */
 | |
| void dmxSyncActivate(const char *interval)
 | |
| {
 | |
|     dmxSyncInterval = (interval ? atoi(interval) : 100);
 | |
| 
 | |
|     if (dmxSyncInterval < 0) dmxSyncInterval = 0;
 | |
| }
 | |
| 
 | |
| /** Initialize the XSync() batching optimization, but only if
 | |
|  * #dmxSyncActivate was last called with a non-negative value. */
 | |
| void dmxSyncInit(void)
 | |
| {
 | |
|     if (dmxSyncInterval) {
 | |
|         RegisterBlockAndWakeupHandlers(dmxSyncBlockHandler,
 | |
|                                        dmxSyncWakeupHandler,
 | |
|                                        NULL);
 | |
|         dmxLog(dmxInfo, "XSync batching with %d ms interval\n",
 | |
|                dmxSyncInterval);
 | |
|     } else {
 | |
|         dmxLog(dmxInfo, "XSync batching disabled\n");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /** Request an XSync() to the display used by \a dmxScreen.  If \a now
 | |
|  * is TRUE, call XSync() immediately instead of waiting for the next
 | |
|  * XSync() batching point.  Note that if XSync() batching was deselected
 | |
|  * with #dmxSyncActivate() before #dmxSyncInit() was called, then no
 | |
|  * XSync() batching is performed and this function always calles XSync()
 | |
|  * immediately.
 | |
|  *
 | |
|  * (Note that this function uses TimerSet but works correctly in the
 | |
|  * face of a server generation.  See the source for details.)
 | |
|  *
 | |
|  * If \a dmxScreen is \a NULL, then all pending syncs will be flushed
 | |
|  * immediately.
 | |
|  */
 | |
| void dmxSync(DMXScreenInfo *dmxScreen, Bool now)
 | |
| {
 | |
|     static unsigned long dmxGeneration = 0;
 | |
| 
 | |
|     if (dmxSyncInterval) {
 | |
|         if (dmxGeneration != serverGeneration) {
 | |
|             /* Server generation does a TimerInit, which frees all
 | |
|              * timers.  So, at this point dmxSyncTimer is either:
 | |
|              * 1) NULL, iff dmxGeneration == 0,
 | |
|              * 2) freed, if it was on a queue (dmxSyncPending != 0), or
 | |
|              * 3) allocated, if it wasn't on a queue (dmxSyncPending == 0)
 | |
|              */
 | |
|             if (dmxSyncTimer && !dmxSyncPending) free(dmxSyncTimer);
 | |
|             dmxSyncTimer  = NULL;
 | |
|             now           = TRUE;
 | |
|             dmxGeneration = serverGeneration;
 | |
|         }
 | |
|                                 /* Queue sync */
 | |
|         if (dmxScreen) {
 | |
|             dmxScreen->needsSync = TRUE;
 | |
|             ++dmxSyncPending;
 | |
|         }
 | |
| 
 | |
|                                 /* Do sync or set time for later */
 | |
|         if (now || !dmxScreen) {
 | |
|             if (!TimerForce(dmxSyncTimer)) 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->name : "", now, dmxSyncPending);
 | |
|         } else {
 | |
|             dmxScreen->needsSync = TRUE;
 | |
|             if (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. */
 | |
|         if (dmxScreen) dmxDoSync(dmxScreen);
 | |
|     }
 | |
| }
 |