#if defined(VMTOOLS_USE_GLIB)
# include "vmware/tools/guestrpc.h"
+# include "vmware/tools/utils.h"
#endif
#include "vmware.h"
char const *errmsg;
char const *reply;
size_t repLen;
+ Bool resched = FALSE;
+ unsigned int current;
in = (RpcIn *)clientData;
ASSERT(in);
/* The event has fired: it is no longer valid */
ASSERT(in->nextEvent);
-#if defined(VMTOOLS_USE_GLIB)
- g_source_unref(in->nextEvent);
-#endif
- in->nextEvent = NULL;
+ current = in->delay;
/* This is very important: this is the only way to signal the existence
of this guest application to VMware */
in->mustSend = TRUE;
#if defined(VMTOOLS_USE_GLIB)
- RPCIN_SCHED_EVENT(in, g_timeout_source_new(in->delay * 10));
+ resched = (in->delay != current);
+ if (resched) {
+ g_source_unref(in->nextEvent);
+ RPCIN_SCHED_EVENT(in, VMTools_CreateTimer(in->delay * 10));
+ }
#else
+ resched = TRUE; /* Avoid unused warning. */
in->nextEvent = EventManager_Add(gTimerEventQueue, in->delay, RpcInLoop, in);
#endif
if (in->nextEvent == NULL) {
}
#if defined(VMTOOLS_USE_GLIB)
- return FALSE;
+ return !resched;
#else
return TRUE;
#endif
ASSERT(in->nextEvent == NULL);
#if defined(VMTOOLS_USE_GLIB)
- RPCIN_SCHED_EVENT(in, g_timeout_source_new(in->delay * 10));
+ RPCIN_SCHED_EVENT(in, VMTools_CreateTimer(in->delay * 10));
#else
in->nextEvent = EventManager_Add(gTimerEventQueue, 0, RpcInLoop, in);
if (in->nextEvent == NULL) {
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/**
+ * @file monotonicTimer.c
+ *
+ * A GSource that implements a timer backed by a monotonic time source.
+ */
+
+#include <limits.h>
+#include "vmware.h"
+#include "system.h"
+#include "vmware/tools/utils.h"
+
+typedef struct MTimerSource {
+ GSource src;
+ gint timeout;
+ uint64 last;
+} MTimerSource;
+
+
+/*
+ *******************************************************************************
+ * MTimerSourcePrepare -- */ /**
+ *
+ * Callback for the "prepare()" event source function. Sets the timeout to
+ * the number of milliseconds this timer expects to sleep for. If the timeout
+ * has already expired, update the internal state tracking the last time the
+ * timer was fired.
+ *
+ * @param[in] src The source.
+ * @param[out] timeout Where to store the timeout.
+ *
+ * @return TRUE if timeout has already expired.
+ *
+ *******************************************************************************
+ */
+
+static gboolean
+MTimerSourcePrepare(GSource *src,
+ gint *timeout)
+{
+ MTimerSource *timer = (MTimerSource *) src;
+
+ if (timer->timeout == 0) {
+ *timeout = 0;
+ return TRUE;
+ } else {
+ uint64 now = System_GetTimeMonotonic() * 10;
+ uint64 diff;
+
+ ASSERT(now >= timer->last);
+
+ diff = now - timer->last;
+ if (diff >= timer->timeout) {
+ timer->last = now;
+ *timeout = 0;
+ return TRUE;
+ }
+
+ *timeout = MIN(INT_MAX, timer->timeout - diff);
+ return FALSE;
+ }
+}
+
+
+/*
+ *******************************************************************************
+ * MTimerSourceCheck -- */ /**
+ *
+ * Checks whether the timeout has expired.
+ *
+ * @param[in] src The source.
+ *
+ * @return Whether the timeout has expired.
+ *
+ *******************************************************************************
+ */
+
+static gboolean
+MTimerSourceCheck(GSource *src)
+{
+ gint unused;
+ return MTimerSourcePrepare(src, &unused);
+}
+
+
+/*
+ *******************************************************************************
+ * MTimerSourceDispatch -- */ /**
+ *
+ * Calls the callback associated with the timer, if any.
+ *
+ * @param[in] src Unused.
+ * @param[in] callback The callback to be called.
+ * @param[in] data User-supplied data.
+ *
+ * @return The return value of the callback, or FALSE if the callback is NULL.
+ *
+ *******************************************************************************
+ */
+
+static gboolean
+MTimerSourceDispatch(GSource *src,
+ GSourceFunc callback,
+ gpointer data)
+{
+ return (callback != NULL) ? callback(data) : FALSE;
+}
+
+
+/*
+ *******************************************************************************
+ * MTimerSourceFinalize -- */ /**
+ *
+ * Does nothing. The main glib code already does all the cleanup needed.
+ *
+ * @param[in] src The source.
+ *
+ *******************************************************************************
+ */
+
+static void
+MTimerSourceFinalize(GSource *src)
+{
+}
+
+
+/**
+ *
+ * @addtogroup vmtools_utils
+ * @{
+ */
+
+/*
+ *******************************************************************************
+ * VMTools_CreateTimer -- */ /**
+ *
+ * @brief Create a timer based on a monotonic clock source.
+ *
+ * This timer differs from the glib timeout source, which uses the system time.
+ * It is recommended for code that needs more reliable time tracking, using a
+ * clock that is not affected by changes in the system time (which can happen
+ * when using NTP or the Tools time synchronization feature).
+ *
+ * @param[in] timeout The timeout for the timer, must be >= 0.
+ *
+ * @return The new source.
+ *
+ *******************************************************************************
+ */
+
+GSource *
+VMTools_CreateTimer(gint timeout)
+{
+ static GSourceFuncs srcFuncs = {
+ MTimerSourcePrepare,
+ MTimerSourceCheck,
+ MTimerSourceDispatch,
+ MTimerSourceFinalize,
+ NULL,
+ NULL
+ };
+ MTimerSource *ret;
+
+ ASSERT(timeout >= 0);
+
+ ret = (MTimerSource *) g_source_new(&srcFuncs, sizeof *ret);
+ ret->last = System_GetTimeMonotonic() * 10;
+ ret->timeout = timeout;
+
+ return &ret->src;
+}
+
+/** @} */
+