blob: c9d8d6022f2e18d6e0576c8a449cc452ab7c9842 [file] [log] [blame]
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +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//
Constantin Kaplinskye47123e2006-02-16 16:44:50 +000020// PollingScheduler class implementation.
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000021//
22
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000023#include <string.h>
24#include <stdlib.h>
25
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +000026#ifdef DEBUG
27#include <stdio.h>
28#endif
29
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000030#include <x0vncserver/PollingScheduler.h>
31
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000032PollingScheduler::PollingScheduler(int interval, int maxload)
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000033{
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000034 setParameters(interval, maxload);
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000035 reset();
36}
37
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000038void PollingScheduler::setParameters(int interval, int maxload)
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000039{
40 m_interval = interval;
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000041 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 }
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000051}
52
53void PollingScheduler::reset()
54{
Constantin Kaplinskyd2644442006-02-16 16:49:27 +000055 m_initialState = true;
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +000056}
57
58void PollingScheduler::newPass()
59{
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000060 TimeMillis timeNow;
61
62 if (m_initialState) {
63
64 // First polling pass: initialize statistics.
65 m_initialState = false;
66 m_ratedDuration = 0;
67 m_sleeping = 0;
68 memset(m_errors, 0, sizeof(m_errors));
69 m_errorSum = 0;
70 m_errorAbsSum = 0;
71 memset(m_durations, 0, sizeof(m_durations));
72 m_durationSum = 0;
73 memset(m_slept, 0, sizeof(m_slept));
74 m_sleptSum = 0;
75 m_idx = 0;
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +000076 m_count = 0;
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +000077
78 } else {
79
80 // Stop sleeping if not yet.
81 if (m_sleeping)
82 sleepFinished();
83
84 // Update statistics on sleeping time and total pass duration.
85 int duration = timeNow.diffFrom(m_passStarted);
86
87 int oldest = m_durations[m_idx];
88 m_durations[m_idx] = duration;
89 m_durationSum = m_durationSum - oldest + duration;
90
91 oldest = m_slept[m_idx];
92 m_slept[m_idx] = m_sleptThisPass;
93 m_sleptSum = m_sleptSum - oldest + m_sleptThisPass;
94
95 // Compute and save the difference between actual and planned time.
96 int newError = duration - m_interval;
97 oldest = m_errors[m_idx];
98 m_errors[m_idx] = newError;
99 m_errorSum = m_errorSum - oldest + newError;
100 m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
101
102 //
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +0000103 // Below is the most important part.
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000104 // Compute desired duration of the upcoming polling pass.
105 //
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000106
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +0000107 // Estimation based on keeping up constant interval.
108 m_ratedDuration = m_interval - m_errorSum / 2;
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000109
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +0000110 // Estimations based on keeping up desired CPU load.
111 int optimalLoadDuration1 = 0;
112 int optimalLoadDuration8 = 0;
113 int optimalLoadDuration = 0;
114
115 if (m_count > 4) {
116 // Estimation 1 (use previous pass statistics).
117 optimalLoadDuration1 =
118 ((duration - m_sleptThisPass) * 100 + m_maxload/2) / m_maxload;
119
120 if (m_count > 16) {
121 // Estimation 2 (use history of 8 previous passes).
122 optimalLoadDuration8 =
123 ((m_durationSum - m_sleptSum) * 900 + m_maxload*4) / (m_maxload*8)
124 - m_durationSum;
125 // Mix the above two giving more priority to the first.
126 optimalLoadDuration =
127 (2 * optimalLoadDuration1 + optimalLoadDuration8) / 3;
128 } else {
129 optimalLoadDuration = optimalLoadDuration1;
130 }
131 }
132
133#ifdef DEBUG
134 fprintf(stderr, "<est %3d,%3d,%d>\t",
135 m_ratedDuration, optimalLoadDuration1, optimalLoadDuration8);
136#endif
137
138 // Choose final estimation.
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000139 if (m_ratedDuration < optimalLoadDuration) {
140 m_ratedDuration = optimalLoadDuration;
141 }
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000142 if (m_ratedDuration < 0) {
143 m_ratedDuration = 0;
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +0000144 } else if (m_ratedDuration > 500 && m_interval <= 100) {
145 m_ratedDuration = 500;
146 } else if (m_ratedDuration > 1000) {
147 m_ratedDuration = 1000;
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000148 }
149
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +0000150#ifdef DEBUG
151 fprintf(stderr, "<final est %3d>\t", m_ratedDuration);
152#endif
153
154 // Update ring buffer indexer (8 elements per each arrays).
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000155 m_idx = (m_idx + 1) & 7;
156
Constantin Kaplinsky6a34ca52006-02-17 11:29:17 +0000157 // Update pass counter.
158 m_count++;
159
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000160 }
161
162 m_passStarted = timeNow;
163 m_sleptThisPass = 0;
164}
165
166void PollingScheduler::sleepStarted()
167{
168 if (m_initialState || m_sleeping)
169 return;
170
171 m_sleepStarted.update();
172
173 m_sleeping = true;
174}
175
176void PollingScheduler::sleepFinished()
177{
178 if (m_initialState || !m_sleeping)
179 return;
180
181 TimeMillis timeNow;
182 m_sleptThisPass += timeNow.diffFrom(m_sleepStarted);
183
184 m_sleeping = false;
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +0000185}
186
187int PollingScheduler::millisRemaining() const
188{
Constantin Kaplinskyd2644442006-02-16 16:49:27 +0000189 if (m_initialState)
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +0000190 return 0;
191
192 TimeMillis timeNow;
193 int elapsed = timeNow.diffFrom(m_passStarted);
194
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000195 if (elapsed > m_ratedDuration)
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +0000196 return 0;
197
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000198 return (m_ratedDuration - elapsed);
Constantin Kaplinsky0cbad332006-02-16 14:51:11 +0000199}
200
Constantin Kaplinskye47123e2006-02-16 16:44:50 +0000201bool PollingScheduler::goodTimeToPoll() const
202{
Constantin Kaplinskye179b5a2006-02-17 09:30:21 +0000203 if (m_initialState)
204 return true;
205
206 // Average error (per 8 elements in the ring buffer).
207 int errorAvg = (m_errorAbsSum + 4) / 8;
208
209 // It's ok to poll earlier if new error is no more than half-average.
210 return (millisRemaining() <= errorAvg / 2);
Constantin Kaplinskye47123e2006-02-16 16:44:50 +0000211}