#include <stdlib.h>
#include <time.h>
#include "smtpd.h"
struct job {
TAILQ_ENTRY(job) entry;
time_t when;
void *arg;
};
struct runq {
TAILQ_HEAD(, job) jobs;
void (*cb)(struct runq *, void *);
struct event ev;
};
static void runq_timeout(int, short, void *);
static struct runq *active;
static void
runq_reset(struct runq *runq)
{
struct timeval tv;
struct job *job;
time_t now;
job = TAILQ_FIRST(&runq->jobs);
if (job == NULL)
return;
now = time(NULL);
if (job->when <= now)
tv.tv_sec = 0;
else
tv.tv_sec = job->when - now;
tv.tv_usec = 0;
evtimer_add(&runq->ev, &tv);
}
static void
runq_timeout(int fd, short ev, void *arg)
{
struct runq *runq = arg;
struct job *job;
time_t now;
active = runq;
now = time(NULL);
while((job = TAILQ_FIRST(&runq->jobs))) {
if (job->when > now)
break;
TAILQ_REMOVE(&runq->jobs, job, entry);
runq->cb(runq, job->arg);
free(job);
}
active = NULL;
runq_reset(runq);
}
int
runq_init(struct runq **runqp, void (*cb)(struct runq *, void *))
{
struct runq *runq;
runq = malloc(sizeof(*runq));
if (runq == NULL)
return (0);
runq->cb = cb;
TAILQ_INIT(&runq->jobs);
evtimer_set(&runq->ev, runq_timeout, runq);
*runqp = runq;
return (1);
}
int
runq_schedule(struct runq *runq, time_t delay, void *arg)
{
time_t t;
time(&t);
return runq_schedule_at(runq, t + delay, arg);
}
int
runq_schedule_at(struct runq *runq, time_t when, void *arg)
{
struct job *job, *tmpjob;
job = malloc(sizeof(*job));
if (job == NULL)
return (0);
job->arg = arg;
job->when = when;
TAILQ_FOREACH(tmpjob, &runq->jobs, entry) {
if (tmpjob->when > job->when) {
TAILQ_INSERT_BEFORE(tmpjob, job, entry);
goto done;
}
}
TAILQ_INSERT_TAIL(&runq->jobs, job, entry);
done:
if (runq != active && job == TAILQ_FIRST(&runq->jobs)) {
evtimer_del(&runq->ev);
runq_reset(runq);
}
return (1);
}
int
runq_cancel(struct runq *runq, void *arg)
{
struct job *job, *first;
first = TAILQ_FIRST(&runq->jobs);
TAILQ_FOREACH(job, &runq->jobs, entry) {
if (job->arg == arg) {
TAILQ_REMOVE(&runq->jobs, job, entry);
free(job);
if (runq != active && job == first) {
evtimer_del(&runq->ev);
runq_reset(runq);
}
return (1);
}
}
return (0);
}
int
runq_pending(struct runq *runq, void *arg, time_t *when)
{
struct job *job;
TAILQ_FOREACH(job, &runq->jobs, entry) {
if (job->arg == arg) {
if (when)
*when = job->when;
return (1);
}
}
return (0);
}