Document Cron

This commit is contained in:
Jordan Bancino 2022-12-24 23:07:46 +00:00
parent c18a9a96e6
commit 4f48f9a5cc
3 changed files with 112 additions and 2 deletions

96
man/man3/Cron.3 Normal file
View file

@ -0,0 +1,96 @@
.Dd $Mdocdate: December 24 2022 $
.Dt CRON 3
.Os Telodendria Project
.Sh NAME
.Nm Cron
.Nd Basic periodic job scheduler.
.Sh SYNOPSIS
.In Cron.h
.Ft Cron *
.Fn CronCreate "unsigned long"
.Ft void
.Fn CronOnce "Cron *" "void (*) (void *)" "void *"
.Ft void
.Fn CronEvery "Cron *" "unsigned long" "void (*) (void *)" "void *"
.Ft void
.Fn CronStart "Cron *"
.Ft void
.Fn CronStop "Cron *"
.Ft void
.Fn CronFree "Cron *"
.Sh DESCRIPTION
.Pp
.Nm
is an extremely basic job scheduler. So basic, in fact,
that it runs all jobs on a single thread, which means that it
is intended for short-lived jobs. In the future,
.Nm
might be extended to support a one-thread-per-job model, but
for now, jobs should consider that they are sharing their
thread, so they should not be long-running jobs.
.Pp
.Nm
works by "ticking" at an interval defined by the caller of
.Fn CronCreate .
At each tick, all the jobs are queried, and if they are due
to run again, their function is executed. As much as possible,
.Nm
tries to tick at constant intervals, however it is possible that
a job may overrun the tick duration. If this happens,
.Nm
ticks again immediately after all the jobs for the previous tick
have completed. This is in an effort to compensate for the lost
time, however it is important to note that when jobs overrun the
tick interval, the interval is pushed back. In this way,
.Nm
is best suited for scheduling jobs that should happen
"approximately" every so often; it is not a real-time scheduler
by any means.
.Pp
.Fn CronCreate
creates a new
.Nm
object that all the other functions use. Like most of the other
APIs in this project, it must be freed with
.Fn CronFree
when it is no longer needed.
.Pp
Jobs can be scheduled with
.Fn CronOnce
and
.Fn CronEvery .
.Fn CronOnce
schedules a one-off job to be executed only at the next tick, and
then discarded. This is useful for scheduling tasks that only have
to happen once, or very infrequently depending on conditions other
than the current time, but don't have to happen immediately. The
caller simply indicates that it wishes for the task to execute at
some time in the future. How far into the future this practically
ends up being is determined by how long it takes other jobs to
finish, and what the tick interval is.
.Pp
.Fn CronEvery
schedules a repetitive task to be executed at approximately the
given interval. As stated above, this is a fuzzy interval; depending
on the jobs being run and the tick interval, tasks may not happen
at exactly the scheduled time, but they will eventually happen.
.Pp
.Fn CronStart
and
.Fn CronStop
start and stop the ticking, respectively.
.Fn CronFree
discards all the job references and frees all memory associated
with the given instance of the
.Nm
instance.
.Sh RETURN VALUES
.Pp
.Fn CronCreate
returns a reference to a
.Nm ,
or NULL if it was unable to allocate memory for it.
.Pp
The other functions in this header don't return anything. They
are assumed to usually work, unless their input is obviously
wrong.

View file

@ -279,6 +279,12 @@ Extremely simple HTTP server.
A simple implementation of the SHA2 hashing functions. A simple implementation of the SHA2 hashing functions.
</td> </td>
</tr> </tr>
<tr>
<td><a href="man/man3/Cron.3.html">Cron(3)</a></td>
<td>
A basic periodic job scheduler.
</td>
</tr>
</table> </table>
</details> </details>
<hr> <hr>

View file

@ -78,10 +78,12 @@ CronThread(void *args)
while (!cron->stop) while (!cron->stop)
{ {
size_t i; size_t i;
unsigned long ts = UtilServerTs(); unsigned long ts; /* tick start */
unsigned long te; /* tick end */
pthread_mutex_lock(&cron->lock); pthread_mutex_lock(&cron->lock);
ts = UtilServerTs();
for (i = 0; i < ArraySize(cron->jobs); i++) for (i = 0; i < ArraySize(cron->jobs); i++)
{ {
Job *job = ArrayGet(cron->jobs, i); Job *job = ArrayGet(cron->jobs, i);
@ -98,9 +100,15 @@ CronThread(void *args)
Free(job); Free(job);
} }
} }
te = UtilServerTs();
pthread_mutex_unlock(&cron->lock); pthread_mutex_unlock(&cron->lock);
UtilSleepMillis(cron->tick - (UtilServerTs() - ts));
/* Only sleep if the jobs didn't overrun the tick */
if (cron->tick > (te - ts))
{
UtilSleepMillis(cron->tick - (te - ts));
}
} }
return NULL; return NULL;