dmxsync.c revision 706f2543
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@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 59static int dmxSyncInterval = 100; /* Default interval in milliseconds */ 60static OsTimerPtr dmxSyncTimer; 61static int dmxSyncPending; 62 63static 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 82static 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 96static void dmxSyncBlockHandler(pointer blockData, OSTimePtr pTimeout, 97 pointer pReadMask) 98{ 99 TimerForce(dmxSyncTimer); 100} 101 102static 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 */ 115void 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. */ 124void 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 */ 150void 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