blob: 65420ffbe286787e3bc617c2f9ec349abe8a3fd3 [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19//
20// PollingScheduler class implementation.
21//
22
23#include <string.h>
24#include <stdlib.h>
25
26#ifdef DEBUG
27#include <stdio.h>
28#endif
29
30#include <x0vncserver/PollingScheduler.h>
31
32PollingScheduler::PollingScheduler(int interval, int maxload)
33{
34 setParameters(interval, maxload);
35 reset();
36}
37
38void PollingScheduler::setParameters(int interval, int maxload)
39{
40 m_interval = interval;
41 m_maxload = maxload;
42
43 if (m_interval < 0) {
44 m_interval = 0;
45 }
46 if (m_maxload < 1) {
47 m_maxload = 1;
48 } else if (m_maxload > 100) {
49 m_maxload = 100;
50 }
51}
52
53void PollingScheduler::reset()
54{
55 m_initialState = true;
56}
57
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +000058bool PollingScheduler::isRunning()
59{
60 return !m_initialState;
61}
62
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000063void PollingScheduler::newPass()
64{
65 TimeMillis timeNow;
66
67 if (m_initialState) {
68
69 // First polling pass: initialize statistics.
70 m_initialState = false;
71 m_ratedDuration = 0;
72 m_sleeping = 0;
73 memset(m_errors, 0, sizeof(m_errors));
74 m_errorSum = 0;
75 m_errorAbsSum = 0;
76 memset(m_durations, 0, sizeof(m_durations));
77 m_durationSum = 0;
78 memset(m_slept, 0, sizeof(m_slept));
79 m_sleptSum = 0;
80 m_idx = 0;
81 m_count = 0;
82
83 } else {
84
85 // Stop sleeping if not yet.
86 if (m_sleeping)
87 sleepFinished();
88
89 // Update statistics on sleeping time and total pass duration.
90 int duration = timeNow.diffFrom(m_passStarted);
91
92 int oldest = m_durations[m_idx];
93 m_durations[m_idx] = duration;
94 m_durationSum = m_durationSum - oldest + duration;
95
96 oldest = m_slept[m_idx];
97 m_slept[m_idx] = m_sleptThisPass;
98 m_sleptSum = m_sleptSum - oldest + m_sleptThisPass;
99
100 // Compute and save the difference between actual and planned time.
101 int newError = duration - m_interval;
102 oldest = m_errors[m_idx];
103 m_errors[m_idx] = newError;
104 m_errorSum = m_errorSum - oldest + newError;
105 m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
106
107 //
108 // Below is the most important part.
109 // Compute desired duration of the upcoming polling pass.
110 //
111
112 // Estimation based on keeping up constant interval.
113 m_ratedDuration = m_interval - m_errorSum / 2;
114
115 // Estimations based on keeping up desired CPU load.
116 int optimalLoadDuration1 = 0;
117 int optimalLoadDuration8 = 0;
118 int optimalLoadDuration = 0;
119
120 if (m_count > 4) {
121 // Estimation 1 (use previous pass statistics).
122 optimalLoadDuration1 =
123 ((duration - m_sleptThisPass) * 100 + m_maxload/2) / m_maxload;
124
125 if (m_count > 16) {
126 // Estimation 2 (use history of 8 previous passes).
127 optimalLoadDuration8 =
128 ((m_durationSum - m_sleptSum) * 900 + m_maxload*4) / (m_maxload*8)
129 - m_durationSum;
130 // Mix the above two giving more priority to the first.
131 optimalLoadDuration =
132 (2 * optimalLoadDuration1 + optimalLoadDuration8) / 3;
133 } else {
134 optimalLoadDuration = optimalLoadDuration1;
135 }
136 }
137
138#ifdef DEBUG
139 fprintf(stderr, "<est %3d,%3d,%d>\t",
140 m_ratedDuration, optimalLoadDuration1, optimalLoadDuration8);
141#endif
142
143 // Choose final estimation.
144 if (m_ratedDuration < optimalLoadDuration) {
145 m_ratedDuration = optimalLoadDuration;
146 }
147 if (m_ratedDuration < 0) {
148 m_ratedDuration = 0;
149 } else if (m_ratedDuration > 500 && m_interval <= 100) {
150 m_ratedDuration = 500;
151 } else if (m_ratedDuration > 1000) {
152 m_ratedDuration = 1000;
153 }
154
155#ifdef DEBUG
156 fprintf(stderr, "<final est %3d>\t", m_ratedDuration);
157#endif
158
159 // Update ring buffer indexer (8 elements per each arrays).
160 m_idx = (m_idx + 1) & 7;
161
162 // Update pass counter.
163 m_count++;
164
165 }
166
167 m_passStarted = timeNow;
168 m_sleptThisPass = 0;
169}
170
171void PollingScheduler::sleepStarted()
172{
173 if (m_initialState || m_sleeping)
174 return;
175
176 m_sleepStarted.update();
177
178 m_sleeping = true;
179}
180
181void PollingScheduler::sleepFinished()
182{
183 if (m_initialState || !m_sleeping)
184 return;
185
186 TimeMillis timeNow;
187 m_sleptThisPass += timeNow.diffFrom(m_sleepStarted);
188
189 m_sleeping = false;
190}
191
192int PollingScheduler::millisRemaining() const
193{
194 if (m_initialState)
195 return 0;
196
197 TimeMillis timeNow;
198 int elapsed = timeNow.diffFrom(m_passStarted);
199
200 if (elapsed > m_ratedDuration)
201 return 0;
202
203 return (m_ratedDuration - elapsed);
204}
205
206bool PollingScheduler::goodTimeToPoll() const
207{
208 if (m_initialState)
209 return true;
210
211 // Average error (per 8 elements in the ring buffer).
212 int errorAvg = (m_errorAbsSum + 4) / 8;
213
214 // It's ok to poll earlier if new error is no more than half-average.
215 return (millisRemaining() <= errorAvg / 2);
216}