text: /* $XFree86: xc/programs/Xserver/hw/dmx/dmxsync.c,v 1.1 2004/06/30 20:21:39 martin Exp $ */
  /*
   * 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 #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. */
  
  #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 #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) xfree(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);
      }
  }
  