summaryrefslogtreecommitdiffstats
path: root/src/knot/common/fdset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/knot/common/fdset.c')
-rw-r--r--src/knot/common/fdset.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/knot/common/fdset.c b/src/knot/common/fdset.c
new file mode 100644
index 0000000..bb83670
--- /dev/null
+++ b/src/knot/common/fdset.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include "knot/common/fdset.h"
+#include "contrib/time.h"
+#include "libknot/errcode.h"
+
+/* Realloc memory or return error (part of fdset_resize). */
+#define MEM_RESIZE(tmp, p, n) \
+ if ((tmp = realloc((p), (n) * sizeof(*p))) == NULL) \
+ return KNOT_ENOMEM; \
+ (p) = tmp;
+
+static int fdset_resize(fdset_t *set, unsigned size)
+{
+ void *tmp = NULL;
+ MEM_RESIZE(tmp, set->ctx, size);
+ MEM_RESIZE(tmp, set->pfd, size);
+ MEM_RESIZE(tmp, set->timeout, size);
+ set->size = size;
+ return KNOT_EOK;
+}
+
+int fdset_init(fdset_t *set, unsigned size)
+{
+ if (set == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(set, 0, sizeof(fdset_t));
+ return fdset_resize(set, size);
+}
+
+int fdset_clear(fdset_t* set)
+{
+ if (set == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ free(set->ctx);
+ free(set->pfd);
+ free(set->timeout);
+ memset(set, 0, sizeof(fdset_t));
+ return KNOT_EOK;
+}
+
+int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx)
+{
+ if (set == NULL || fd < 0) {
+ return KNOT_EINVAL;
+ }
+
+ /* Realloc needed. */
+ if (set->n == set->size && fdset_resize(set, set->size + FDSET_INIT_SIZE))
+ return KNOT_ENOMEM;
+
+ /* Initialize. */
+ int i = set->n++;
+ set->pfd[i].fd = fd;
+ set->pfd[i].events = events;
+ set->pfd[i].revents = 0;
+ set->ctx[i] = ctx;
+ set->timeout[i] = 0;
+
+ /* Return index to this descriptor. */
+ return i;
+}
+
+int fdset_remove(fdset_t *set, unsigned i)
+{
+ if (set == NULL || i >= set->n) {
+ return KNOT_EINVAL;
+ }
+
+ /* Decrement number of elms. */
+ --set->n;
+
+ /* Nothing else if it is the last one.
+ * Move last -> i if some remain. */
+ unsigned last = set->n; /* Already decremented */
+ if (i < last) {
+ set->pfd[i] = set->pfd[last];
+ set->timeout[i] = set->timeout[last];
+ set->ctx[i] = set->ctx[last];
+ }
+
+ return KNOT_EOK;
+}
+
+int fdset_set_watchdog(fdset_t* set, int i, int interval)
+{
+ if (set == NULL || i >= set->n) {
+ return KNOT_EINVAL;
+ }
+
+ /* Lift watchdog if interval is negative. */
+ if (interval < 0) {
+ set->timeout[i] = 0;
+ return KNOT_EOK;
+ }
+
+ /* Update clock. */
+ struct timespec now = time_now();
+
+ set->timeout[i] = now.tv_sec + interval; /* Only seconds precision. */
+ return KNOT_EOK;
+}
+
+int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data)
+{
+ if (set == NULL || cb == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Get time threshold. */
+ struct timespec now = time_now();
+
+ unsigned i = 0;
+ while (i < set->n) {
+
+ /* Check sweep state, remove if requested. */
+ if (set->timeout[i] > 0 && set->timeout[i] <= now.tv_sec) {
+ if (cb(set, i, data) == FDSET_SWEEP) {
+ if (fdset_remove(set, i) == KNOT_EOK)
+ continue; /* Stay on the index. */
+ }
+ }
+
+ /* Next descriptor. */
+ ++i;
+ }
+
+ return KNOT_EOK;
+}