Files
smarttime/ts/smarttime.classes.cronmanager.ts

107 lines
3.0 KiB
TypeScript
Raw Permalink Normal View History

2022-11-21 09:14:32 +01:00
import * as plugins from './smarttime.plugins.js';
2023-07-21 03:39:32 +02:00
import { CronJob, type TJobFunction } from './smarttime.classes.cronjob.js';
2019-04-10 11:34:30 +02:00
2019-04-10 14:06:20 +02:00
export class CronManager {
2020-05-27 16:59:26 +00:00
public executionTimeout: plugins.smartdelay.Timeout<void>;
2020-05-25 21:45:43 +00:00
2019-04-10 14:06:20 +02:00
public status: 'started' | 'stopped' = 'stopped';
2020-07-11 23:36:24 +00:00
public cronjobs = new plugins.lik.ObjectMap<CronJob>();
2019-04-10 14:06:20 +02:00
private cycleWakeDeferred: plugins.smartpromise.Deferred<void> | null = null;
2020-05-27 16:59:26 +00:00
constructor() {}
2019-04-10 14:06:20 +02:00
/**
* Resolves the current wake deferred, causing the sleeping cycle
* to immediately recalculate instead of waiting for its timeout.
*/
private wakeCycle() {
if (this.cycleWakeDeferred && this.cycleWakeDeferred.status === 'pending') {
this.cycleWakeDeferred.resolve();
}
}
2023-01-06 20:05:02 +01:00
public addCronjob(cronIdentifierArg: string, cronFunctionArg: TJobFunction) {
2020-05-25 21:45:43 +00:00
const newCronJob = new CronJob(this, cronIdentifierArg, cronFunctionArg);
2020-07-11 23:36:24 +00:00
this.cronjobs.add(newCronJob);
2019-04-10 14:06:20 +02:00
if (this.status === 'started') {
newCronJob.start();
this.wakeCycle();
2019-04-10 14:06:20 +02:00
}
2020-07-11 23:31:09 +00:00
return newCronJob;
2019-06-17 16:54:39 +02:00
}
2019-04-10 14:06:20 +02:00
2020-07-11 23:36:24 +00:00
public removeCronjob(cronjobArg: CronJob) {
cronjobArg.stop();
this.cronjobs.remove(cronjobArg);
if (this.status === 'started') {
this.wakeCycle();
}
2020-07-11 23:36:24 +00:00
}
2019-04-10 14:06:20 +02:00
/**
* starts the cronjob
*/
public start() {
2020-07-12 00:25:55 +00:00
if (this.status !== 'started') {
this.status = 'started';
for (const cronJob of this.cronjobs.getArray()) {
cronJob.start();
}
2023-10-20 12:50:47 +02:00
this.runCronCycle();
2020-07-12 00:25:55 +00:00
}
2019-04-10 14:06:20 +02:00
}
2023-10-20 12:50:47 +02:00
private async runCronCycle() {
while (this.status === 'started') {
// Create a fresh wake signal for this iteration
this.cycleWakeDeferred = new plugins.smartpromise.Deferred<void>();
// Check all cronjobs and find the soonest next execution
let soonestMs = Infinity;
2023-10-20 12:50:47 +02:00
for (const cronJob of this.cronjobs.getArray()) {
cronJob.checkExecution();
const msToNext = cronJob.getTimeToNextExecution();
if (msToNext < soonestMs) {
soonestMs = msToNext;
2023-10-20 12:50:47 +02:00
}
}
// Sleep until the next job is due or until woken by a lifecycle event
if (soonestMs < Infinity && soonestMs > 0) {
this.executionTimeout = new plugins.smartdelay.Timeout(soonestMs);
await Promise.race([
this.executionTimeout.promise,
this.cycleWakeDeferred.promise,
]);
// Cancel the timeout to avoid lingering timers
this.executionTimeout.cancel();
} else if (soonestMs <= 0) {
// Job is overdue, loop immediately to execute it
continue;
2023-10-20 12:50:47 +02:00
} else {
// No jobs — wait indefinitely until woken by addCronjob or stop
await this.cycleWakeDeferred.promise;
2023-10-20 12:50:47 +02:00
}
}
this.cycleWakeDeferred = null;
}
2023-10-20 12:50:47 +02:00
2019-04-10 14:06:20 +02:00
/**
* stops all cronjobs
*/
public stop() {
2020-09-02 14:09:21 +00:00
if (this.status === 'started') {
this.status = 'stopped';
if (this.executionTimeout) {
this.executionTimeout.cancel();
}
this.wakeCycle();
2020-09-02 14:09:21 +00:00
}
2020-07-11 23:36:24 +00:00
for (const cron of this.cronjobs.getArray()) {
2019-04-10 14:06:20 +02:00
cron.stop();
}
}
}