Updating the threading model for timed keep-awake

This commit is contained in:
Den Delimarsky
2021-04-24 09:59:52 -07:00
parent 5e897a546a
commit f277832188
2 changed files with 86 additions and 42 deletions

View File

@@ -4,6 +4,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Espresso.Shell.Core namespace Espresso.Shell.Core
{ {
@@ -22,6 +24,9 @@ namespace Espresso.Shell.Core
/// </summary> /// </summary>
public class APIHelper public class APIHelper
{ {
private static CancellationTokenSource TokenSource = new CancellationTokenSource();
private static CancellationToken ThreadToken;
// More details about the API used: https://docs.microsoft.com/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate // More details about the API used: https://docs.microsoft.com/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
@@ -58,6 +63,7 @@ namespace Espresso.Shell.Core
public static bool SetNormalKeepAwake() public static bool SetNormalKeepAwake()
{ {
//TokenSource.Cancel();
return SetAwakeState(EXECUTION_STATE.ES_CONTINUOUS); return SetAwakeState(EXECUTION_STATE.ES_CONTINUOUS);
} }
@@ -73,43 +79,74 @@ namespace Espresso.Shell.Core
} }
} }
public static bool SetTimedKeepAwake(long seconds, bool keepDisplayOn = true) public static void SetTimedKeepAwake(long seconds, Action<bool> callback, bool keepDisplayOn = true)
{ {
ThreadToken = TokenSource.Token;
try
{
Task.Run(() => RunTimedLoop(seconds, keepDisplayOn), ThreadToken).ContinueWith((result) => callback(result.Result));
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
}
finally
{
TokenSource.Dispose();
}
}
private static bool RunTimedLoop(long seconds, bool keepDisplayOn = true)
{
bool success;
// In case cancellation was already requested.
//ThreadToken.ThrowIfCancellationRequested();
if (keepDisplayOn) if (keepDisplayOn)
{ {
var success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
if (success) if (success)
{ {
RunTimedLoop(seconds); Console.WriteLine("Timed keep-awake with display on.");
return true; var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(seconds))
{
if (ThreadToken.IsCancellationRequested)
{
ThreadToken.ThrowIfCancellationRequested();
}
}
return success;
} }
else else
{ {
return false; return success;
} }
} }
else else
{ {
var success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
if (success) if (success)
{ {
RunTimedLoop(seconds); Console.WriteLine("Timed keep-awake with display off.");
return true; var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(seconds))
{
if (ThreadToken.IsCancellationRequested)
{
ThreadToken.ThrowIfCancellationRequested();
}
}
return success;
} }
else else
{ {
return false; return success;
} }
} }
} }
private static void RunTimedLoop(long seconds)
{
var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(seconds))
{
// We do nothing.
}
}
} }
} }

View File

@@ -141,20 +141,20 @@ namespace Espresso.Shell
else else
{ {
// Timed keep-awake. // Timed keep-awake.
bool success = APIHelper.SetTimedKeepAwake(timeLimit, displayOn); APIHelper.SetTimedKeepAwake(timeLimit, LogTimedKeepAwakeCompletion, displayOn);
if (success) //if (success)
{ //{
Console.WriteLine($"Finished execution of timed keep-awake."); // Console.WriteLine($"Finished execution of timed keep-awake.");
// Because the timed keep-awake execution completed, there is no reason for // // Because the timed keep-awake execution completed, there is no reason for
// Espresso to stay alive - I will just shut down the application until it's // // Espresso to stay alive - I will just shut down the application until it's
// launched again by the user. // // launched again by the user.
Environment.Exit(0); // Environment.Exit(0);
} //}
else //else
{ //{
Console.WriteLine("Could not set up the state to be timed keep awake."); // Console.WriteLine("Could not set up the state to be timed keep awake.");
} //}
} }
} }
@@ -163,6 +163,8 @@ namespace Espresso.Shell
private static void HandleEspressoConfigChange(object sender, FileSystemEventArgs e) private static void HandleEspressoConfigChange(object sender, FileSystemEventArgs e)
{ {
Console.WriteLine("Resetting keep-awake to normal state due to settings change.");
ResetNormalPowerState();
Console.WriteLine("Detected a file change. Reacting..."); Console.WriteLine("Detected a file change. Reacting...");
ProcessSettings(e.FullPath); ProcessSettings(e.FullPath);
} }
@@ -210,22 +212,22 @@ namespace Espresso.Shell
long computedTime = (settings.Properties.Hours.Value * 60 * 60) + (settings.Properties.Minutes.Value * 60); long computedTime = (settings.Properties.Hours.Value * 60 * 60) + (settings.Properties.Minutes.Value * 60);
Console.WriteLine($"In timed keep-awake mode. Expecting to be awake for {computedTime} seconds."); Console.WriteLine($"In timed keep-awake mode. Expecting to be awake for {computedTime} seconds.");
bool success = APIHelper.SetTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn.Value); APIHelper.SetTimedKeepAwake(computedTime, LogTimedKeepAwakeCompletion, settings.Properties.KeepDisplayOn.Value);
if (success) //if (success)
{ //{
Console.WriteLine($"Finished execution of timed keep-awake."); // Console.WriteLine($"Finished execution of timed keep-awake.");
ResetNormalPowerState(); // ResetNormalPowerState();
} //}
else //else
{ //{
Console.WriteLine("Could not set up the state to be timed keep awake."); // Console.WriteLine("Could not set up the state to be timed keep awake.");
} //}
break; break;
} }
default: default:
{ {
ForceExit("Could not select the right mode of operation. Existing...", 1); Console.WriteLine("Unknown mode of operation. Check config file.");
break; break;
} }
} }
@@ -246,6 +248,11 @@ namespace Espresso.Shell
} }
} }
private static void LogTimedKeepAwakeCompletion(bool result)
{
Console.Write($"Completed timed keep-awake successfully: {result}");
}
private static void ResetNormalPowerState() private static void ResetNormalPowerState()
{ {
bool success = APIHelper.SetNormalKeepAwake(); bool success = APIHelper.SetNormalKeepAwake();