aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs')
-rw-r--r--src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs
new file mode 100644
index 00000000..0de78a0e
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ /// <summary>
+ /// A threaded executor of periodic actions that can be cancelled. The total execution time is optional
+ /// and, in this case, a progress is reported back to the action.
+ /// </summary>
+ class TimedAction
+ {
+ public const int MaxThreadSleep = 100;
+
+ private class SleepSubstepData
+ {
+ public readonly int SleepMilliseconds;
+ public readonly int SleepCount;
+ public readonly int SleepRemainderMilliseconds;
+
+ public SleepSubstepData(int sleepMilliseconds)
+ {
+ SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep);
+ SleepCount = sleepMilliseconds / SleepMilliseconds;
+ SleepRemainderMilliseconds = sleepMilliseconds - SleepCount * SleepMilliseconds;
+ }
+ }
+
+ private TRef<bool> _cancelled = null;
+ private Thread _thread = null;
+ private object _lock = new object();
+
+ public bool IsRunning
+ {
+ get
+ {
+ lock (_lock)
+ {
+ if (_thread == null)
+ {
+ return false;
+ }
+
+ return _thread.IsAlive;
+ }
+ }
+ }
+
+ public void RequestCancel()
+ {
+ lock (_lock)
+ {
+ if (_cancelled != null)
+ {
+ Volatile.Write(ref _cancelled.Value, true);
+ }
+ }
+ }
+
+ public TimedAction() { }
+
+ private void Reset(Thread thread, TRef<bool> cancelled)
+ {
+ lock (_lock)
+ {
+ // Cancel the current task.
+ if (_cancelled != null)
+ {
+ Volatile.Write(ref _cancelled.Value, true);
+ }
+
+ _cancelled = cancelled;
+
+ _thread = thread;
+ _thread.IsBackground = true;
+ _thread.Start();
+ }
+ }
+
+ public void Reset(Action<float> action, int totalMilliseconds, int sleepMilliseconds)
+ {
+ // Create a dedicated cancel token for each task.
+ var cancelled = new TRef<bool>(false);
+
+ Reset(new Thread(() =>
+ {
+ var substepData = new SleepSubstepData(sleepMilliseconds);
+
+ int totalCount = totalMilliseconds / sleepMilliseconds;
+ int totalRemainder = totalMilliseconds - totalCount * sleepMilliseconds;
+
+ if (Volatile.Read(ref cancelled.Value))
+ {
+ action(-1);
+
+ return;
+ }
+
+ action(0);
+
+ for (int i = 1; i <= totalCount; i++)
+ {
+ if (SleepWithSubstep(substepData, cancelled))
+ {
+ action(-1);
+
+ return;
+ }
+
+ action((float)(i * sleepMilliseconds) / totalMilliseconds);
+ }
+
+ if (totalRemainder > 0)
+ {
+ if (SleepWithSubstep(substepData, cancelled))
+ {
+ action(-1);
+
+ return;
+ }
+
+ action(1);
+ }
+ }), cancelled);
+ }
+
+ public void Reset(Action action, int sleepMilliseconds)
+ {
+ // Create a dedicated cancel token for each task.
+ var cancelled = new TRef<bool>(false);
+
+ Reset(new Thread(() =>
+ {
+ var substepData = new SleepSubstepData(sleepMilliseconds);
+
+ while (!Volatile.Read(ref cancelled.Value))
+ {
+ action();
+
+ if (SleepWithSubstep(substepData, cancelled))
+ {
+ return;
+ }
+ }
+ }), cancelled);
+ }
+
+ public void Reset(Action action)
+ {
+ // Create a dedicated cancel token for each task.
+ var cancelled = new TRef<bool>(false);
+
+ Reset(new Thread(() =>
+ {
+ while (!Volatile.Read(ref cancelled.Value))
+ {
+ action();
+ }
+ }), cancelled);
+ }
+
+ private static bool SleepWithSubstep(SleepSubstepData substepData, TRef<bool> cancelled)
+ {
+ for (int i = 0; i < substepData.SleepCount; i++)
+ {
+ if (Volatile.Read(ref cancelled.Value))
+ {
+ return true;
+ }
+
+ Thread.Sleep(substepData.SleepMilliseconds);
+ }
+
+ if (substepData.SleepRemainderMilliseconds > 0)
+ {
+ if (Volatile.Read(ref cancelled.Value))
+ {
+ return true;
+ }
+
+ Thread.Sleep(substepData.SleepRemainderMilliseconds);
+ }
+
+ return Volatile.Read(ref cancelled.Value);
+ }
+ }
+}