diff --git a/src/Cron.c b/src/Cron.c new file mode 100644 index 0000000..cb4cfe1 --- /dev/null +++ b/src/Cron.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +#include +#include +#include + +#include + +struct Cron +{ + unsigned long tick; + Array *jobs; + pthread_mutex_t lock; + volatile unsigned int stop:1; + pthread_t thread; +}; + +typedef struct Job +{ + unsigned long interval; + unsigned long lastExec; + void (*func) (void *); + void *args; +} Job; + +static Job * + JobCreate(long interval, void (*func) (void *), void *args) +{ + Job *job; + + if (!func) + { + return NULL; + } + + job = Malloc(sizeof(Job)); + if (!job) + { + return NULL; + } + + job->interval = interval; + job->lastExec = 0; + job->func = func; + job->args = args; + + return job; +} + +static void * +CronThread(void *args) +{ + Cron *cron = args; + + while (!cron->stop) + { + size_t i; + unsigned long ts = UtilServerTs(); + + pthread_mutex_lock(&cron->lock); + + for (i = 0; i < ArraySize(cron->jobs); i++) + { + Job *job = ArrayGet(cron->jobs, i); + + if (ts - job->lastExec > job->interval) + { + job->func(job->args); + job->lastExec = ts; + } + + if (!job->interval) + { + ArrayDelete(cron->jobs, i); + Free(job); + } + } + + pthread_mutex_unlock(&cron->lock); + UtilSleepMillis(cron->tick - (UtilServerTs() - ts)); + } + + return NULL; +} + +Cron * +CronCreate(unsigned long tick) +{ + Cron *cron = Malloc(sizeof(Cron)); + + if (!cron) + { + return NULL; + } + + cron->jobs = ArrayCreate(); + if (!cron->jobs) + { + Free(cron); + return NULL; + } + + cron->tick = tick; + + pthread_mutex_init(&cron->lock, NULL); + + return cron; +} + +void + CronOnce(Cron * cron, void (*func) (void *), void *args) +{ + Job *job; + + if (!cron || !func) + { + return; + } + + job = JobCreate(0, func, args); + if (!job) + { + return; + } + + pthread_mutex_lock(&cron->lock); + ArrayAdd(cron->jobs, job); + pthread_mutex_unlock(&cron->lock); +} + +void + CronEvery(Cron * cron, unsigned long interval, void (*func) (void *), void *args) +{ + Job *job; + + if (!cron || !func) + { + return; + } + + job = JobCreate(interval, func, args); + if (!job) + { + return; + } + + pthread_mutex_lock(&cron->lock); + ArrayAdd(cron->jobs, job); + pthread_mutex_unlock(&cron->lock); +} + +void +CronStart(Cron * cron) +{ + if (!cron || !cron->stop) + { + return; + } + + cron->stop = 0; + + pthread_create(&cron->thread, NULL, CronThread, cron); +} + +void +CronStop(Cron * cron) +{ + if (!cron || cron->stop) + { + return; + } + + cron->stop = 1; + + pthread_join(cron->thread, NULL); +} + +void +CronFree(Cron * cron) +{ + size_t i; + + if (!cron) + { + return; + } + + CronStop(cron); + + pthread_mutex_lock(&cron->lock); + for (i = 0; i < ArraySize(cron->jobs); i++) + { + Free(ArrayGet(cron->jobs, i)); + } + + ArrayFree(cron->jobs); + pthread_mutex_unlock(&cron->lock); + pthread_mutex_destroy(&cron->lock); + + Free(cron); +} diff --git a/src/Telodendria.c b/src/Telodendria.c index a8ade89..249f8c2 100644 --- a/src/Telodendria.c +++ b/src/Telodendria.c @@ -40,6 +40,7 @@ #include #include #include +#include static void TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args) @@ -267,6 +268,7 @@ main(int argc, char **argv) struct sigaction sigAction; MatrixHttpHandlerArgs matrixArgs; + Cron *cron = NULL; memset(&matrixArgs, 0, sizeof(matrixArgs)); @@ -595,6 +597,21 @@ main(int argc, char **argv) goto finish; } + cron = CronCreate(60 * 1000); /* 1-minute tick */ + if (!cron) + { + Log(lc, LOG_ERR, "Unable to set up job scheduler."); + exit = EXIT_FAILURE; + goto finish; + } + + Log(lc, LOG_DEBUG, "Registering jobs..."); + + /* TODO: Register jobs here */ + + Log(lc, LOG_NOTICE, "Starting job scheduler..."); + CronStart(cron); + Log(lc, LOG_NOTICE, "Starting server..."); if (!HttpServerStart(httpServer)) @@ -629,6 +646,13 @@ finish: Log(lc, LOG_DEBUG, "Freed HTTP Server."); } + if (cron) + { + CronStop(cron); + CronFree(cron); + Log(lc, LOG_DEBUG, "Stopped and freed job scheduler."); + } + /* * If we're not logging to standard output, then we can close it. Otherwise, * if we are logging to stdout, LogConfigFree() will close it for us. diff --git a/src/include/Cron.h b/src/include/Cron.h new file mode 100644 index 0000000..fe2ed15 --- /dev/null +++ b/src/include/Cron.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TELODENDRIA_CRON_H +#define TELODENDRIA_CRON_H + +typedef struct Cron Cron; + +extern Cron * + CronCreate(unsigned long); + +extern void + CronOnce(Cron *, void (*) (void *), void *); + +extern void + CronEvery(Cron *, unsigned long, void (*) (void *), void *); + +extern void + CronStart(Cron *); + +extern void + CronStop(Cron *); + +extern void + CronFree(Cron *); + +#endif /* TELODENDRIA_CRON_H */