Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@590 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/unix/x0vncserver/PollingScheduler.cxx b/unix/x0vncserver/PollingScheduler.cxx
new file mode 100644
index 0000000..c9d8d60
--- /dev/null
+++ b/unix/x0vncserver/PollingScheduler.cxx
@@ -0,0 +1,211 @@
+/* Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+//
+// PollingScheduler class implementation.
+//
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include <x0vncserver/PollingScheduler.h>
+
+PollingScheduler::PollingScheduler(int interval, int maxload)
+{
+ setParameters(interval, maxload);
+ reset();
+}
+
+void PollingScheduler::setParameters(int interval, int maxload)
+{
+ m_interval = interval;
+ m_maxload = maxload;
+
+ if (m_interval < 0) {
+ m_interval = 0;
+ }
+ if (m_maxload < 1) {
+ m_maxload = 1;
+ } else if (m_maxload > 100) {
+ m_maxload = 100;
+ }
+}
+
+void PollingScheduler::reset()
+{
+ m_initialState = true;
+}
+
+void PollingScheduler::newPass()
+{
+ TimeMillis timeNow;
+
+ if (m_initialState) {
+
+ // First polling pass: initialize statistics.
+ m_initialState = false;
+ m_ratedDuration = 0;
+ m_sleeping = 0;
+ memset(m_errors, 0, sizeof(m_errors));
+ m_errorSum = 0;
+ m_errorAbsSum = 0;
+ memset(m_durations, 0, sizeof(m_durations));
+ m_durationSum = 0;
+ memset(m_slept, 0, sizeof(m_slept));
+ m_sleptSum = 0;
+ m_idx = 0;
+ m_count = 0;
+
+ } else {
+
+ // Stop sleeping if not yet.
+ if (m_sleeping)
+ sleepFinished();
+
+ // Update statistics on sleeping time and total pass duration.
+ int duration = timeNow.diffFrom(m_passStarted);
+
+ int oldest = m_durations[m_idx];
+ m_durations[m_idx] = duration;
+ m_durationSum = m_durationSum - oldest + duration;
+
+ oldest = m_slept[m_idx];
+ m_slept[m_idx] = m_sleptThisPass;
+ m_sleptSum = m_sleptSum - oldest + m_sleptThisPass;
+
+ // Compute and save the difference between actual and planned time.
+ int newError = duration - m_interval;
+ oldest = m_errors[m_idx];
+ m_errors[m_idx] = newError;
+ m_errorSum = m_errorSum - oldest + newError;
+ m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
+
+ //
+ // Below is the most important part.
+ // Compute desired duration of the upcoming polling pass.
+ //
+
+ // Estimation based on keeping up constant interval.
+ m_ratedDuration = m_interval - m_errorSum / 2;
+
+ // Estimations based on keeping up desired CPU load.
+ int optimalLoadDuration1 = 0;
+ int optimalLoadDuration8 = 0;
+ int optimalLoadDuration = 0;
+
+ if (m_count > 4) {
+ // Estimation 1 (use previous pass statistics).
+ optimalLoadDuration1 =
+ ((duration - m_sleptThisPass) * 100 + m_maxload/2) / m_maxload;
+
+ if (m_count > 16) {
+ // Estimation 2 (use history of 8 previous passes).
+ optimalLoadDuration8 =
+ ((m_durationSum - m_sleptSum) * 900 + m_maxload*4) / (m_maxload*8)
+ - m_durationSum;
+ // Mix the above two giving more priority to the first.
+ optimalLoadDuration =
+ (2 * optimalLoadDuration1 + optimalLoadDuration8) / 3;
+ } else {
+ optimalLoadDuration = optimalLoadDuration1;
+ }
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "<est %3d,%3d,%d>\t",
+ m_ratedDuration, optimalLoadDuration1, optimalLoadDuration8);
+#endif
+
+ // Choose final estimation.
+ if (m_ratedDuration < optimalLoadDuration) {
+ m_ratedDuration = optimalLoadDuration;
+ }
+ if (m_ratedDuration < 0) {
+ m_ratedDuration = 0;
+ } else if (m_ratedDuration > 500 && m_interval <= 100) {
+ m_ratedDuration = 500;
+ } else if (m_ratedDuration > 1000) {
+ m_ratedDuration = 1000;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "<final est %3d>\t", m_ratedDuration);
+#endif
+
+ // Update ring buffer indexer (8 elements per each arrays).
+ m_idx = (m_idx + 1) & 7;
+
+ // Update pass counter.
+ m_count++;
+
+ }
+
+ m_passStarted = timeNow;
+ m_sleptThisPass = 0;
+}
+
+void PollingScheduler::sleepStarted()
+{
+ if (m_initialState || m_sleeping)
+ return;
+
+ m_sleepStarted.update();
+
+ m_sleeping = true;
+}
+
+void PollingScheduler::sleepFinished()
+{
+ if (m_initialState || !m_sleeping)
+ return;
+
+ TimeMillis timeNow;
+ m_sleptThisPass += timeNow.diffFrom(m_sleepStarted);
+
+ m_sleeping = false;
+}
+
+int PollingScheduler::millisRemaining() const
+{
+ if (m_initialState)
+ return 0;
+
+ TimeMillis timeNow;
+ int elapsed = timeNow.diffFrom(m_passStarted);
+
+ if (elapsed > m_ratedDuration)
+ return 0;
+
+ return (m_ratedDuration - elapsed);
+}
+
+bool PollingScheduler::goodTimeToPoll() const
+{
+ if (m_initialState)
+ return true;
+
+ // Average error (per 8 elements in the ring buffer).
+ int errorAvg = (m_errorAbsSum + 4) / 8;
+
+ // It's ok to poll earlier if new error is no more than half-average.
+ return (millisRemaining() <= errorAvg / 2);
+}