1 /* $OpenBSD: runq.c,v 1.4 2021/06/14 17:58:16 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2013,2019 Eric Faurot <eric@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <stdlib.h> 20 21 #include "smtpd.h" 22 23 struct job { 24 TAILQ_ENTRY(job) entry; 25 time_t when; 26 void *arg; 27 }; 28 29 struct runq { 30 TAILQ_HEAD(, job) jobs; 31 void (*cb)(struct runq *, void *); 32 struct event ev; 33 }; 34 35 static void runq_timeout(int, short, void *); 36 37 static struct runq *active; 38 39 static void 40 runq_reset(struct runq *runq) 41 { 42 struct timeval tv; 43 struct job *job; 44 time_t now; 45 46 job = TAILQ_FIRST(&runq->jobs); 47 if (job == NULL) 48 return; 49 50 now = time(NULL); 51 if (job->when <= now) 52 tv.tv_sec = 0; 53 else 54 tv.tv_sec = job->when - now; 55 tv.tv_usec = 0; 56 evtimer_add(&runq->ev, &tv); 57 } 58 59 static void 60 runq_timeout(int fd, short ev, void *arg) 61 { 62 struct runq *runq = arg; 63 struct job *job; 64 time_t now; 65 66 active = runq; 67 now = time(NULL); 68 69 while((job = TAILQ_FIRST(&runq->jobs))) { 70 if (job->when > now) 71 break; 72 TAILQ_REMOVE(&runq->jobs, job, entry); 73 runq->cb(runq, job->arg); 74 free(job); 75 } 76 77 active = NULL; 78 runq_reset(runq); 79 } 80 81 int 82 runq_init(struct runq **runqp, void (*cb)(struct runq *, void *)) 83 { 84 struct runq *runq; 85 86 runq = malloc(sizeof(*runq)); 87 if (runq == NULL) 88 return (0); 89 90 runq->cb = cb; 91 TAILQ_INIT(&runq->jobs); 92 evtimer_set(&runq->ev, runq_timeout, runq); 93 94 *runqp = runq; 95 96 return (1); 97 } 98 99 int 100 runq_schedule(struct runq *runq, time_t delay, void *arg) 101 { 102 time_t t; 103 104 time(&t); 105 return runq_schedule_at(runq, t + delay, arg); 106 } 107 108 int 109 runq_schedule_at(struct runq *runq, time_t when, void *arg) 110 { 111 struct job *job, *tmpjob; 112 113 job = malloc(sizeof(*job)); 114 if (job == NULL) 115 return (0); 116 117 job->arg = arg; 118 job->when = when; 119 120 TAILQ_FOREACH(tmpjob, &runq->jobs, entry) { 121 if (tmpjob->when > job->when) { 122 TAILQ_INSERT_BEFORE(tmpjob, job, entry); 123 goto done; 124 } 125 } 126 TAILQ_INSERT_TAIL(&runq->jobs, job, entry); 127 128 done: 129 if (runq != active && job == TAILQ_FIRST(&runq->jobs)) { 130 evtimer_del(&runq->ev); 131 runq_reset(runq); 132 } 133 return (1); 134 } 135 136 int 137 runq_cancel(struct runq *runq, void *arg) 138 { 139 struct job *job, *first; 140 141 first = TAILQ_FIRST(&runq->jobs); 142 TAILQ_FOREACH(job, &runq->jobs, entry) { 143 if (job->arg == arg) { 144 TAILQ_REMOVE(&runq->jobs, job, entry); 145 free(job); 146 if (runq != active && job == first) { 147 evtimer_del(&runq->ev); 148 runq_reset(runq); 149 } 150 return (1); 151 } 152 } 153 154 return (0); 155 } 156 157 int 158 runq_pending(struct runq *runq, void *arg, time_t *when) 159 { 160 struct job *job; 161 162 TAILQ_FOREACH(job, &runq->jobs, entry) { 163 if (job->arg == arg) { 164 if (when) 165 *when = job->when; 166 return (1); 167 } 168 } 169 170 return (0); 171 } 172