TimedAction.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using System;
  2. using System.Threading;
  3. namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
  4. {
  5. /// <summary>
  6. /// A threaded executor of periodic actions that can be cancelled. The total execution time is optional
  7. /// and, in this case, a progress is reported back to the action.
  8. /// </summary>
  9. class TimedAction
  10. {
  11. public const int MaxThreadSleep = 100;
  12. private class SleepSubstepData
  13. {
  14. public readonly int SleepMilliseconds;
  15. public readonly int SleepCount;
  16. public readonly int SleepRemainderMilliseconds;
  17. public SleepSubstepData(int sleepMilliseconds)
  18. {
  19. SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep);
  20. SleepCount = sleepMilliseconds / SleepMilliseconds;
  21. SleepRemainderMilliseconds = sleepMilliseconds - SleepCount * SleepMilliseconds;
  22. }
  23. }
  24. private TRef<bool> _cancelled = null;
  25. private Thread _thread = null;
  26. private object _lock = new object();
  27. public bool IsRunning
  28. {
  29. get
  30. {
  31. lock (_lock)
  32. {
  33. if (_thread == null)
  34. {
  35. return false;
  36. }
  37. return _thread.IsAlive;
  38. }
  39. }
  40. }
  41. public void RequestCancel()
  42. {
  43. lock (_lock)
  44. {
  45. if (_cancelled != null)
  46. {
  47. Volatile.Write(ref _cancelled.Value, true);
  48. }
  49. }
  50. }
  51. public TimedAction() { }
  52. private void Reset(Thread thread, TRef<bool> cancelled)
  53. {
  54. lock (_lock)
  55. {
  56. // Cancel the current task.
  57. if (_cancelled != null)
  58. {
  59. Volatile.Write(ref _cancelled.Value, true);
  60. }
  61. _cancelled = cancelled;
  62. _thread = thread;
  63. _thread.IsBackground = true;
  64. _thread.Start();
  65. }
  66. }
  67. public void Reset(Action<float> action, int totalMilliseconds, int sleepMilliseconds)
  68. {
  69. // Create a dedicated cancel token for each task.
  70. var cancelled = new TRef<bool>(false);
  71. Reset(new Thread(() =>
  72. {
  73. var substepData = new SleepSubstepData(sleepMilliseconds);
  74. int totalCount = totalMilliseconds / sleepMilliseconds;
  75. int totalRemainder = totalMilliseconds - totalCount * sleepMilliseconds;
  76. if (Volatile.Read(ref cancelled.Value))
  77. {
  78. action(-1);
  79. return;
  80. }
  81. action(0);
  82. for (int i = 1; i <= totalCount; i++)
  83. {
  84. if (SleepWithSubstep(substepData, cancelled))
  85. {
  86. action(-1);
  87. return;
  88. }
  89. action((float)(i * sleepMilliseconds) / totalMilliseconds);
  90. }
  91. if (totalRemainder > 0)
  92. {
  93. if (SleepWithSubstep(substepData, cancelled))
  94. {
  95. action(-1);
  96. return;
  97. }
  98. action(1);
  99. }
  100. }), cancelled);
  101. }
  102. public void Reset(Action action, int sleepMilliseconds)
  103. {
  104. // Create a dedicated cancel token for each task.
  105. var cancelled = new TRef<bool>(false);
  106. Reset(new Thread(() =>
  107. {
  108. var substepData = new SleepSubstepData(sleepMilliseconds);
  109. while (!Volatile.Read(ref cancelled.Value))
  110. {
  111. action();
  112. if (SleepWithSubstep(substepData, cancelled))
  113. {
  114. return;
  115. }
  116. }
  117. }), cancelled);
  118. }
  119. public void Reset(Action action)
  120. {
  121. // Create a dedicated cancel token for each task.
  122. var cancelled = new TRef<bool>(false);
  123. Reset(new Thread(() =>
  124. {
  125. while (!Volatile.Read(ref cancelled.Value))
  126. {
  127. action();
  128. }
  129. }), cancelled);
  130. }
  131. private static bool SleepWithSubstep(SleepSubstepData substepData, TRef<bool> cancelled)
  132. {
  133. for (int i = 0; i < substepData.SleepCount; i++)
  134. {
  135. if (Volatile.Read(ref cancelled.Value))
  136. {
  137. return true;
  138. }
  139. Thread.Sleep(substepData.SleepMilliseconds);
  140. }
  141. if (substepData.SleepRemainderMilliseconds > 0)
  142. {
  143. if (Volatile.Read(ref cancelled.Value))
  144. {
  145. return true;
  146. }
  147. Thread.Sleep(substepData.SleepRemainderMilliseconds);
  148. }
  149. return Volatile.Read(ref cancelled.Value);
  150. }
  151. }
  152. }