KTimeManager.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using Ryujinx.Common;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. namespace Ryujinx.HLE.HOS.Kernel.Common
  7. {
  8. class KTimeManager : IDisposable
  9. {
  10. public static readonly long DefaultTimeIncrementNanoseconds = ConvertGuestTicksToNanoseconds(2);
  11. private class WaitingObject
  12. {
  13. public IKFutureSchedulerObject Object { get; }
  14. public long TimePoint { get; }
  15. public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
  16. {
  17. Object = schedulerObj;
  18. TimePoint = timePoint;
  19. }
  20. }
  21. private readonly KernelContext _context;
  22. private readonly List<WaitingObject> _waitingObjects;
  23. private AutoResetEvent _waitEvent;
  24. private bool _keepRunning;
  25. private long _enforceWakeupFromSpinWait;
  26. public KTimeManager(KernelContext context)
  27. {
  28. _context = context;
  29. _waitingObjects = new List<WaitingObject>();
  30. _keepRunning = true;
  31. Thread work = new Thread(WaitAndCheckScheduledObjects)
  32. {
  33. Name = "HLE.TimeManager"
  34. };
  35. work.Start();
  36. }
  37. public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout)
  38. {
  39. long timePoint = PerformanceCounter.ElapsedTicks + ConvertNanosecondsToHostTicks(timeout);
  40. lock (_context.CriticalSection.Lock)
  41. {
  42. _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
  43. if (timeout < 1000000)
  44. {
  45. Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
  46. }
  47. }
  48. _waitEvent.Set();
  49. }
  50. public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj)
  51. {
  52. lock (_context.CriticalSection.Lock)
  53. {
  54. _waitingObjects.RemoveAll(x => x.Object == schedulerObj);
  55. }
  56. }
  57. private void WaitAndCheckScheduledObjects()
  58. {
  59. SpinWait spinWait = new SpinWait();
  60. WaitingObject next;
  61. using (_waitEvent = new AutoResetEvent(false))
  62. {
  63. while (_keepRunning)
  64. {
  65. lock (_context.CriticalSection.Lock)
  66. {
  67. Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 0);
  68. next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
  69. }
  70. if (next != null)
  71. {
  72. long timePoint = PerformanceCounter.ElapsedTicks;
  73. if (next.TimePoint > timePoint)
  74. {
  75. long ms = Math.Min((next.TimePoint - timePoint) / PerformanceCounter.TicksPerMillisecond, int.MaxValue);
  76. if (ms > 0)
  77. {
  78. _waitEvent.WaitOne((int)ms);
  79. }
  80. else
  81. {
  82. while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks <= next.TimePoint)
  83. {
  84. if (spinWait.NextSpinWillYield)
  85. {
  86. Thread.Yield();
  87. spinWait.Reset();
  88. }
  89. spinWait.SpinOnce();
  90. }
  91. spinWait.Reset();
  92. }
  93. }
  94. bool timeUp = PerformanceCounter.ElapsedTicks >= next.TimePoint;
  95. if (timeUp)
  96. {
  97. lock (_context.CriticalSection.Lock)
  98. {
  99. if (_waitingObjects.Remove(next))
  100. {
  101. next.Object.TimeUp();
  102. }
  103. }
  104. }
  105. }
  106. else
  107. {
  108. _waitEvent.WaitOne();
  109. }
  110. }
  111. }
  112. }
  113. public static long ConvertNanosecondsToMilliseconds(long time)
  114. {
  115. time /= 1000000;
  116. if ((ulong)time > int.MaxValue)
  117. {
  118. return int.MaxValue;
  119. }
  120. return time;
  121. }
  122. public static long ConvertMillisecondsToNanoseconds(long time)
  123. {
  124. return time * 1000000;
  125. }
  126. public static long ConvertNanosecondsToHostTicks(long ns)
  127. {
  128. long nsDiv = ns / 1000000000;
  129. long nsMod = ns % 1000000000;
  130. long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
  131. long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
  132. long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
  133. return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
  134. }
  135. public static long ConvertGuestTicksToNanoseconds(long ticks)
  136. {
  137. return (long)Math.Ceiling(ticks * (1000000000.0 / 19200000.0));
  138. }
  139. public static long ConvertHostTicksToTicks(long time)
  140. {
  141. return (long)((time / (double)PerformanceCounter.TicksPerSecond) * 19200000.0);
  142. }
  143. public void Dispose()
  144. {
  145. _keepRunning = false;
  146. _waitEvent?.Set();
  147. }
  148. }
  149. }