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