You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PollingScheduler.cxx 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /* 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. // PollingScheduler class implementation.
  20. //
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #ifdef DEBUG
  24. #include <stdio.h>
  25. #endif
  26. #include <x0vncserver/PollingScheduler.h>
  27. PollingScheduler::PollingScheduler(int interval, int maxload)
  28. {
  29. setParameters(interval, maxload);
  30. reset();
  31. }
  32. void PollingScheduler::setParameters(int interval, int maxload)
  33. {
  34. m_interval = interval;
  35. m_maxload = maxload;
  36. if (m_interval < 0) {
  37. m_interval = 0;
  38. }
  39. if (m_maxload < 1) {
  40. m_maxload = 1;
  41. } else if (m_maxload > 100) {
  42. m_maxload = 100;
  43. }
  44. }
  45. void PollingScheduler::reset()
  46. {
  47. m_initialState = true;
  48. }
  49. bool PollingScheduler::isRunning()
  50. {
  51. return !m_initialState;
  52. }
  53. void PollingScheduler::newPass()
  54. {
  55. TimeMillis timeNow;
  56. if (m_initialState) {
  57. // First polling pass: initialize statistics.
  58. m_initialState = false;
  59. m_ratedDuration = 0;
  60. m_sleeping = 0;
  61. memset(m_errors, 0, sizeof(m_errors));
  62. m_errorSum = 0;
  63. m_errorAbsSum = 0;
  64. memset(m_durations, 0, sizeof(m_durations));
  65. m_durationSum = 0;
  66. memset(m_slept, 0, sizeof(m_slept));
  67. m_sleptSum = 0;
  68. m_idx = 0;
  69. m_count = 0;
  70. } else {
  71. // Stop sleeping if not yet.
  72. if (m_sleeping)
  73. sleepFinished();
  74. // Update statistics on sleeping time and total pass duration.
  75. int duration = timeNow.diffFrom(m_passStarted);
  76. int oldest = m_durations[m_idx];
  77. m_durations[m_idx] = duration;
  78. m_durationSum = m_durationSum - oldest + duration;
  79. oldest = m_slept[m_idx];
  80. m_slept[m_idx] = m_sleptThisPass;
  81. m_sleptSum = m_sleptSum - oldest + m_sleptThisPass;
  82. // Compute and save the difference between actual and planned time.
  83. int newError = duration - m_interval;
  84. oldest = m_errors[m_idx];
  85. m_errors[m_idx] = newError;
  86. m_errorSum = m_errorSum - oldest + newError;
  87. m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
  88. //
  89. // Below is the most important part.
  90. // Compute desired duration of the upcoming polling pass.
  91. //
  92. // Estimation based on keeping up constant interval.
  93. m_ratedDuration = m_interval - m_errorSum / 2;
  94. // Estimations based on keeping up desired CPU load.
  95. int optimalLoadDuration1 = 0;
  96. int optimalLoadDuration8 = 0;
  97. int optimalLoadDuration = 0;
  98. if (m_count > 4) {
  99. // Estimation 1 (use previous pass statistics).
  100. optimalLoadDuration1 =
  101. ((duration - m_sleptThisPass) * 100 + m_maxload/2) / m_maxload;
  102. if (m_count > 16) {
  103. // Estimation 2 (use history of 8 previous passes).
  104. optimalLoadDuration8 =
  105. ((m_durationSum - m_sleptSum) * 900 + m_maxload*4) / (m_maxload*8)
  106. - m_durationSum;
  107. // Mix the above two giving more priority to the first.
  108. optimalLoadDuration =
  109. (2 * optimalLoadDuration1 + optimalLoadDuration8) / 3;
  110. } else {
  111. optimalLoadDuration = optimalLoadDuration1;
  112. }
  113. }
  114. #ifdef DEBUG
  115. fprintf(stderr, "<est %3d,%3d,%d>\t",
  116. m_ratedDuration, optimalLoadDuration1, optimalLoadDuration8);
  117. #endif
  118. // Choose final estimation.
  119. if (m_ratedDuration < optimalLoadDuration) {
  120. m_ratedDuration = optimalLoadDuration;
  121. }
  122. if (m_ratedDuration < 0) {
  123. m_ratedDuration = 0;
  124. } else if (m_ratedDuration > 500 && m_interval <= 100) {
  125. m_ratedDuration = 500;
  126. } else if (m_ratedDuration > 1000) {
  127. m_ratedDuration = 1000;
  128. }
  129. #ifdef DEBUG
  130. fprintf(stderr, "<final est %3d>\t", m_ratedDuration);
  131. #endif
  132. // Update ring buffer indexer (8 elements per each arrays).
  133. m_idx = (m_idx + 1) & 7;
  134. // Update pass counter.
  135. m_count++;
  136. }
  137. m_passStarted = timeNow;
  138. m_sleptThisPass = 0;
  139. }
  140. void PollingScheduler::sleepStarted()
  141. {
  142. if (m_initialState || m_sleeping)
  143. return;
  144. m_sleepStarted.update();
  145. m_sleeping = true;
  146. }
  147. void PollingScheduler::sleepFinished()
  148. {
  149. if (m_initialState || !m_sleeping)
  150. return;
  151. TimeMillis timeNow;
  152. m_sleptThisPass += timeNow.diffFrom(m_sleepStarted);
  153. m_sleeping = false;
  154. }
  155. int PollingScheduler::millisRemaining() const
  156. {
  157. if (m_initialState)
  158. return 0;
  159. TimeMillis timeNow;
  160. int elapsed = timeNow.diffFrom(m_passStarted);
  161. if (elapsed > m_ratedDuration)
  162. return 0;
  163. return (m_ratedDuration - elapsed);
  164. }
  165. bool PollingScheduler::goodTimeToPoll() const
  166. {
  167. if (m_initialState)
  168. return true;
  169. // Average error (per 8 elements in the ring buffer).
  170. int errorAvg = (m_errorAbsSum + 4) / 8;
  171. // It's ok to poll earlier if new error is no more than half-average.
  172. return (millisRemaining() <= errorAvg / 2);
  173. }