Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delay a job on startup #795

Merged
merged 5 commits into from
Nov 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 71 additions & 12 deletions framework/OpenMod.Core/Jobs/JobScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
public class JobScheduler : IJobScheduler, IDisposable
{
private const string c_DataStoreKey = "autoexec";
private const string c_JobDelayDelimiter = ":";
private readonly IRuntime m_Runtime;
private readonly ILogger<JobScheduler> m_Logger;
private readonly IDataStore m_DataStore;
Expand Down Expand Up @@ -80,7 +81,7 @@
{
foreach (var job in m_File.Jobs.ToList())
{
await ScheduleJobInternalAsync(job, execStartup: !isFromFileChange, execReboot: s_RunRebootJobs);
await ScheduleJobInternalAsync(job, isCalledFromStartup: !isFromFileChange, isCalledFromReboot: s_RunRebootJobs);
}
}

Expand Down Expand Up @@ -112,7 +113,7 @@
m_File.Jobs.Add(job);

await WriteJobsFileAsync();
await ScheduleJobInternalAsync(job, execStartup: false, execReboot: false);
await ScheduleJobInternalAsync(job, isCalledFromStartup: false, isCalledFromReboot: false);
return job;
}

Expand Down Expand Up @@ -200,7 +201,7 @@
await m_DataStore.SaveAsync(c_DataStoreKey, m_File);
}

private async Task ScheduleJobInternalAsync(ScheduledJob job, bool execStartup, bool execReboot)
private async Task ScheduleJobInternalAsync(ScheduledJob job, bool isCalledFromStartup, bool isCalledFromReboot)
{
if (job == null)
{
Expand Down Expand Up @@ -230,28 +231,27 @@
return;
}

if (job.Schedule!.Equals("@single_exec", StringComparison.OrdinalIgnoreCase))
if (job.Schedule!.StartsWith("@single_exec", StringComparison.OrdinalIgnoreCase))
{
await ExecuteJobAsync(job);
await RemoveJobAsync(job);
await ScheduleDelayedOrExecuteJob(job, shouldBeRemovedAfterExecution: true);
return;
}

if (job.Schedule.Equals("@reboot", StringComparison.OrdinalIgnoreCase))
if (job.Schedule.StartsWith("@reboot", StringComparison.OrdinalIgnoreCase))
{
if (execReboot)
if (isCalledFromReboot)
{
await ExecuteJobAsync(job);
await ScheduleDelayedOrExecuteJob(job, shouldBeRemovedAfterExecution: false);
}

return;
}

if (job.Schedule.Equals("@startup", StringComparison.OrdinalIgnoreCase))
if (job.Schedule.StartsWith("@startup", StringComparison.OrdinalIgnoreCase))
{
if (execStartup)
if (isCalledFromStartup)
{
await ExecuteJobAsync(job);
await ScheduleDelayedOrExecuteJob(job, shouldBeRemovedAfterExecution: false);
}

return;
Expand Down Expand Up @@ -311,6 +311,65 @@
});
}

private async Task ScheduleDelayedOrExecuteJob(ScheduledJob job, bool shouldBeRemovedAfterExecution)
{
if (job is null)
{
throw new ArgumentNullException(nameof(job));
}

var delayDelimiterIndex = job.Schedule!.IndexOf(c_JobDelayDelimiter);
var isNotDelayable = delayDelimiterIndex == -1;

if (isNotDelayable)
{
await ExecuteJobAsync(job);

if (shouldBeRemovedAfterExecution)
{
await RemoveJobAsync(job);
}

return;
}

var unparsedDelay = job.Schedule[(delayDelimiterIndex + 1)..];

TimeSpan delay;

try
{
delay = TimeSpanHelper.Parse(unparsedDelay);
}
catch (Exception ex)
{
m_Logger.LogError(ex, "Invalid time span format \"{JobDelayUnparsed}\" for \"{JobName}\" job",
unparsedDelay, job.Name);
return;
}
Dismissed Show dismissed Hide dismissed

m_ScheduledJobs.Add(job);

m_Logger.LogInformation("Delaying job \"{JobName}\" with delay of \"{JobDelay}\"",
job.Name, $"{delay:c}");

AsyncHelper.Schedule($"Execution of job \"{job.Name}\"", async () => {
await Task.Delay(delay);

if (!(job.Enabled ?? true) || !m_ScheduledJobs.Contains(job) || !m_Runtime.IsComponentAlive)
{
return;
}

await ExecuteJobAsync(job);

if (shouldBeRemovedAfterExecution)
{
await RemoveJobAsync(job);
}
});
}

private async Task ExecuteJobAsync(ScheduledJob job)
{
if (job == null)
Expand Down
Loading