xref: /OK3568_Linux_fs/external/xserver/hw/dmx/dmxsync.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 
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 dmxDoSync(DMXScreenInfo * dmxScreen)
65 {
66     dmxScreen->needsSync = FALSE;
67 
68     if (!dmxScreen->beDisplay)
69         return;                 /* FIXME: Is this correct behavior for sync stats? */
70 
71     if (!dmxStatInterval) {
72         XSync(dmxScreen->beDisplay, False);
73     }
74     else {
75         struct timeval start, stop;
76 
77         gettimeofday(&start, 0);
78         XSync(dmxScreen->beDisplay, False);
79         gettimeofday(&stop, 0);
80         dmxStatSync(dmxScreen, &stop, &start, dmxSyncPending);
81     }
82 }
83 
84 static CARD32
dmxSyncCallback(OsTimerPtr timer,CARD32 time,void * arg)85 dmxSyncCallback(OsTimerPtr timer, CARD32 time, void *arg)
86 {
87     int i;
88 
89     if (dmxSyncPending) {
90         for (i = 0; i < dmxNumScreens; i++) {
91             DMXScreenInfo *dmxScreen = &dmxScreens[i];
92 
93             if (dmxScreen->needsSync)
94                 dmxDoSync(dmxScreen);
95         }
96     }
97     dmxSyncPending = 0;
98     return 0;                   /* Do not place on queue again */
99 }
100 
101 static void
dmxSyncBlockHandler(void * blockData,void * timeout)102 dmxSyncBlockHandler(void *blockData, void *timeout)
103 {
104     TimerForce(dmxSyncTimer);
105 }
106 
107 static void
dmxSyncWakeupHandler(void * blockData,int result)108 dmxSyncWakeupHandler(void *blockData, int result)
109 {
110 }
111 
112 /** Request the XSync() batching optimization with the specified \a
113  * interval (in mS).  If the \a interval is 0, 100mS is used.  If the \a
114  * interval is less than 0, then the XSync() batching optimization is
115  * not requested (e.g., so the -syncbatch -1 command line option can
116  * turn off the default 100mS XSync() batching).
117  *
118  * Note that the parameter to this routine is a string, since it will
119  * usually be called from #ddxProcessArgument in \a dmxinit.c */
120 void
dmxSyncActivate(const char * interval)121 dmxSyncActivate(const char *interval)
122 {
123     dmxSyncInterval = (interval ? atoi(interval) : 100);
124 
125     if (dmxSyncInterval < 0)
126         dmxSyncInterval = 0;
127 }
128 
129 /** Initialize the XSync() batching optimization, but only if
130  * #dmxSyncActivate was last called with a non-negative value. */
131 void
dmxSyncInit(void)132 dmxSyncInit(void)
133 {
134     if (dmxSyncInterval) {
135         RegisterBlockAndWakeupHandlers(dmxSyncBlockHandler,
136                                        dmxSyncWakeupHandler, NULL);
137         dmxLog(dmxInfo, "XSync batching with %d ms interval\n",
138                dmxSyncInterval);
139     }
140     else {
141         dmxLog(dmxInfo, "XSync batching disabled\n");
142     }
143 }
144 
145 /** Request an XSync() to the display used by \a dmxScreen.  If \a now
146  * is TRUE, call XSync() immediately instead of waiting for the next
147  * XSync() batching point.  Note that if XSync() batching was deselected
148  * with #dmxSyncActivate() before #dmxSyncInit() was called, then no
149  * XSync() batching is performed and this function always calles XSync()
150  * immediately.
151  *
152  * (Note that this function uses TimerSet but works correctly in the
153  * face of a server generation.  See the source for details.)
154  *
155  * If \a dmxScreen is \a NULL, then all pending syncs will be flushed
156  * immediately.
157  */
158 void
dmxSync(DMXScreenInfo * dmxScreen,Bool now)159 dmxSync(DMXScreenInfo * dmxScreen, Bool now)
160 {
161     static unsigned long dmxGeneration = 0;
162 
163     if (dmxSyncInterval) {
164         if (dmxGeneration != serverGeneration) {
165             /* Server generation does a TimerInit, which frees all
166              * timers.  So, at this point dmxSyncTimer is either:
167              * 1) NULL, iff dmxGeneration == 0,
168              * 2) freed, if it was on a queue (dmxSyncPending != 0), or
169              * 3) allocated, if it wasn't on a queue (dmxSyncPending == 0)
170              */
171             if (dmxSyncTimer && !dmxSyncPending)
172                 free(dmxSyncTimer);
173             dmxSyncTimer = NULL;
174             now = TRUE;
175             dmxGeneration = serverGeneration;
176         }
177         /* Queue sync */
178         if (dmxScreen) {
179             dmxScreen->needsSync = TRUE;
180             ++dmxSyncPending;
181         }
182 
183         /* Do sync or set time for later */
184         if (now || !dmxScreen) {
185             if (dmxSyncTimer == NULL || !TimerForce(dmxSyncTimer))
186                 dmxSyncCallback(NULL, 0, NULL);
187             /* At this point, dmxSyncPending == 0 because
188              * dmxSyncCallback must have been called. */
189             if (dmxSyncPending)
190                 dmxLog(dmxFatal, "dmxSync(%s,%d): dmxSyncPending = %d\n",
191                        dmxScreen ? dmxScreen->name : "", now, dmxSyncPending);
192         }
193         else {
194             dmxScreen->needsSync = TRUE;
195             if (dmxSyncPending == 1)
196                 dmxSyncTimer = TimerSet(dmxSyncTimer, 0, dmxSyncInterval,
197                                         dmxSyncCallback, NULL);
198         }
199     }
200     else {
201         /* If dmxSyncInterval is not being used,
202          * then all the backends are already
203          * up-to-date. */
204         if (dmxScreen)
205             dmxDoSync(dmxScreen);
206     }
207 }
208