summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-22 20:20:08 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-22 20:20:08 +0200
commit3d7ae10a541629727844163f7d64507baedd6c78 (patch)
treea31d9274c64c743cd944414ae4833b880283ffcc
parentdc83b9bf92439f0472333dca0bfa1f7edda689b4 (diff)
Add a shared timer, remove timerfd usage from idle and announce
-rw-r--r--announce.c91
-rw-r--r--idle.c48
-rw-r--r--main.c4
-rw-r--r--main.h1
-rw-r--r--meson.build1
-rw-r--r--ptimer.c208
-rw-r--r--ptimer.h25
-rw-r--r--signal-handler.c2
-rw-r--r--utils.h4
9 files changed, 289 insertions, 95 deletions
diff --git a/announce.c b/announce.c
index 2510d5d..eea668c 100644
--- a/announce.c
+++ b/announce.c
@@ -1,48 +1,38 @@
#include <inttypes.h>
-#include <sys/timerfd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
-#include <time.h>
#include "main.h"
#include "uring.h"
#include "announce.h"
#include "server.h"
+#include "ptimer.h"
struct announce {
uint64_t value;
struct uring_task task;
+ struct ptimer_task ptask;
int mcast_fd;
- time_t until;
+ bool active;
};
+#define ANNOUNCE_INTERVAL 3
+
static void
-announce_cb(struct uring_task *task, int res)
+announce_cb(struct ptimer_task *ptask)
{
- struct announce *announce = container_of(task, struct announce, task);
+ struct announce *announce = container_of(ptask, struct announce, ptask);
struct server *server;
- assert_return(task);
- assert_task_alive(DBG_ANN, task);
-
- if (res != sizeof(announce->value)) {
- error("timerfd_read: %m");
- return;
- }
+ assert_return(ptask);
+ assert_task_alive(DBG_ANN, &announce->task);
- if (announce->until != 0 && time(NULL) > announce->until) {
- debug(DBG_ANN, "stopping announcements");
- announce_stop();
- } else {
- debug(DBG_ANN, "announcing servers");
- list_for_each_entry(server, &cfg->servers, list)
- server_announce(server, announce->mcast_fd);
- }
-
- uring_read(&announce->task, &announce->value, sizeof(announce->value), announce_cb);
+ debug(DBG_ANN, "announcing servers");
+ list_for_each_entry(server, &cfg->servers, list)
+ server_announce(server, announce->mcast_fd);
}
static void
@@ -70,7 +60,8 @@ announce_delete()
{
assert_return(cfg->announce);
- debug(DBG_ANN, "closing fd %i", cfg->announce->task.fd);
+ debug(DBG_ANN, "called");
+ announce_stop();
uring_task_destroy(&cfg->announce->task);
cfg->announce = NULL;
}
@@ -78,53 +69,38 @@ announce_delete()
void
announce_stop()
{
- struct itimerspec tspec = {
- .it_interval = {
- .tv_sec = 0,
- .tv_nsec = 0
- },
- .it_value = {
- .tv_sec = 0,
- .tv_nsec = 0
- }
- };
+ struct announce *announce = cfg->announce;
- assert_return(cfg->announce);
+ assert_return_silent(announce && announce->active);
- if (timerfd_settime(cfg->announce->task.fd, 0, &tspec, NULL) != 0)
- error("timerfd_settime: %m");
+ ptimer_del_task(&announce->ptask);
}
void
announce_start(unsigned duration)
{
- struct itimerspec tspec = {
- .it_interval = {
- .tv_sec = 3,
- .tv_nsec = 0
- },
- .it_value = {
- .tv_sec = 3,
- .tv_nsec = 0
- }
- };
+ struct announce *announce = cfg->announce;
+ unsigned times;
- assert_return(cfg->announce);
+ assert_return_silent(announce);
- if (duration > 0)
- cfg->announce->until = time(NULL) + duration;
+ if (duration == 0)
+ times = 0;
else
- cfg->announce->until = 0;
+ times = MAX(announce->ptask.times,
+ DIV_ROUND_UP(duration, ANNOUNCE_INTERVAL));
- if (timerfd_settime(cfg->announce->task.fd, 0, &tspec, NULL) != 0)
- error("timerfd_settime: %m");
+ announce->ptask.times = times;
+ if (!announce->active) {
+ ptimer_add_task(&announce->ptask);
+ announce->active = true;
+ }
}
void
announce_init()
{
struct announce *announce;
- int afd;
int sfd;
assert_return(!cfg->announce);
@@ -133,18 +109,15 @@ announce_init()
if (!announce)
die("malloc: %m");
- afd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
- if (afd < 0)
- die("timerfd_create: %m");
-
sfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sfd < 0)
die("socket: %m");
uring_task_init(&announce->task, "announce", uring_parent(), announce_free);
- uring_task_set_fd(&announce->task, afd);
+ announce->ptask.interval = ANNOUNCE_INTERVAL;
+ announce->ptask.cb = announce_cb;
+ announce->active = false;
announce->mcast_fd = sfd;
cfg->announce = announce;
- uring_read(&announce->task, &announce->value, sizeof(announce->value), announce_cb);
}
diff --git a/idle.c b/idle.c
index e0eb525..0d13b31 100644
--- a/idle.c
+++ b/idle.c
@@ -1,6 +1,5 @@
#define _GNU_SOURCE
#include <inttypes.h>
-#include <sys/timerfd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
@@ -12,10 +11,11 @@
#include "uring.h"
#include "server.h"
#include "idle.h"
+#include "ptimer.h"
struct idle {
+ struct ptimer_task ptask;
struct uring_task task;
- uint64_t value;
};
static inline void
@@ -315,25 +315,18 @@ idle_check_get_player_count(struct server *server, struct connection *conn)
}
static void
-idle_cb(struct uring_task *task, int res)
+idle_cb(struct ptimer_task *ptask)
{
- struct idle *idle = container_of(task, struct idle, task);
+ struct idle *idle = container_of(ptask, struct idle, ptask);
struct server *server;
- assert_return(task);
- assert_task_alive(DBG_IDLE, task);
-
- if (res != sizeof(idle->value)) {
- error("timerfd_read: %i", res);
- return;
- }
+ assert_return(ptask);
+ assert_task_alive(DBG_IDLE, &idle->task);
- debug(DBG_IDLE, "timer fired (value: %" PRIu64 ")", idle->value);
+ debug(DBG_IDLE, "timer fired");
list_for_each_entry(server, &cfg->servers, list)
server_idle_check(server);
-
- uring_read(&idle->task, &idle->value, sizeof(idle->value), idle_cb);
}
static void
@@ -360,6 +353,7 @@ idle_delete()
assert_return(cfg->idle);
debug(DBG_IDLE, "closing fd %i", cfg->idle->task.fd);
+ ptimer_del_task(&cfg->idle->ptask);
uring_task_destroy(&cfg->idle->task);
cfg->idle = NULL;
}
@@ -368,18 +362,6 @@ void
idle_init()
{
struct idle *idle;
- int ifd;
- struct itimerspec tspec = {
- .it_interval = {
- .tv_sec = 60,
- .tv_nsec = 0
- },
- .it_value = {
- /* FIXME: change to 60 */
- .tv_sec = 4,
- .tv_nsec = 0
- }
- };
assert_return(!cfg->idle);
@@ -387,17 +369,11 @@ idle_init()
if (!idle)
die("malloc: %m");
- ifd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
- if (ifd < 0)
- die("timerfd_create: %m");
-
- if (timerfd_settime(ifd, 0, &tspec, NULL) != 0)
- die("timerfd_settime: %m");
-
+ idle->ptask.interval = 60;
+ idle->ptask.times = 0;
+ idle->ptask.cb = idle_cb;
uring_task_init(&idle->task, "idle", uring_parent(), idle_free);
- uring_task_set_fd(&idle->task, ifd);
+ ptimer_add_task(&idle->ptask);
cfg->idle = idle;
-
- uring_read(&idle->task, &idle->value, sizeof(idle->value), idle_cb);
}
diff --git a/main.c b/main.c
index 6a8ab3a..22c14af 100644
--- a/main.c
+++ b/main.c
@@ -27,6 +27,7 @@
#include "systemd.h"
#include "igmp.h"
#include "idle.h"
+#include "ptimer.h"
#define DEFAULT_HOMEDIR_PATH "/home/david/intest"
#define DEFAULT_MAIN_CONFIG_FILE_PATH "./mcproxy.conf"
@@ -553,6 +554,7 @@ dump_tree()
uring_task_refdump(&cfg->task);
uring_refdump();
signal_refdump();
+ ptimer_refdump();
idle_refdump();
igmp_refdump();
announce_refdump();
@@ -595,6 +597,8 @@ main(int argc, char **argv)
uring_init();
+ ptimer_init();
+
igmp_init();
/* Drop CAP_NET_RAW (if we have it), only used for igmp */
diff --git a/main.h b/main.h
index 84d0a1c..c94954a 100644
--- a/main.h
+++ b/main.h
@@ -152,6 +152,7 @@ struct cfg {
struct server_cfg_monitor *server_cfg_monitor;
struct signal_ev *signal;
struct announce *announce;
+ struct ptimer *ptimer;
struct igmp *igmp;
struct idle *idle;
struct sd_bus *sd_bus;
diff --git a/meson.build b/meson.build
index d07a923..d58cb36 100644
--- a/meson.build
+++ b/meson.build
@@ -15,6 +15,7 @@ mcproxy_sources = [
'config-parser.c',
'rcon.c',
'idle.c',
+ 'ptimer.c',
'igmp.c',
'systemd.c',
'utils.c']
diff --git a/ptimer.c b/ptimer.c
new file mode 100644
index 0000000..7d8662d
--- /dev/null
+++ b/ptimer.c
@@ -0,0 +1,208 @@
+#include <inttypes.h>
+#include <sys/timerfd.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "main.h"
+#include "uring.h"
+#include "ptimer.h"
+
+struct ptimer {
+ uint64_t value;
+ time_t previous_time;
+ struct uring_task task;
+ unsigned task_count;
+ struct list_head ptasks;
+};
+
+static void
+ptimer_set(unsigned value, unsigned interval)
+{
+ struct itimerspec tspec = {
+ .it_interval = {
+ .tv_sec = interval,
+ .tv_nsec = 0
+ },
+ .it_value = {
+ .tv_sec = value,
+ .tv_nsec = 0
+ }
+ };
+
+ assert_return(cfg->ptimer && cfg->ptimer->task.fd >= 0);
+
+ if (timerfd_settime(cfg->ptimer->task.fd, 0, &tspec, NULL) != 0)
+ error("timerfd_settime: %m");
+}
+
+static unsigned
+gcd(unsigned a, unsigned b)
+{
+ if (a == 0)
+ return b;
+ return gcd(b % a, a);
+}
+
+static unsigned
+array_gcd(unsigned arr[], unsigned n)
+{
+ unsigned result = arr[0];
+
+ for (unsigned i = 1; i < n; i++)
+ result = gcd(arr[i], result);
+
+ return result;
+}
+
+static void
+ptimer_tick(struct ptimer *ptimer)
+{
+ time_t now = time(NULL);
+ unsigned diff = (unsigned)(now - ptimer->previous_time);
+ struct ptimer_task *ptask, *ptmp;
+
+ fprintf(stderr, "Got a tick of %u secs\n", diff);
+ list_for_each_entry_safe(ptask, ptmp, &ptimer->ptasks, list) {
+ if (ptask->remain > diff) {
+ ptask->remain -= diff;
+ continue;
+ }
+
+ ptask->cb(ptask);
+ ptask->remain = ptask->interval;
+ if (ptask->times > 0) {
+ ptask->times--;
+ if (ptask->times == 0)
+ list_del(&ptask->list);
+ }
+ }
+
+ ptimer->previous_time = now;
+}
+
+static void
+ptimer_reconfig(struct ptimer *ptimer)
+{
+ struct ptimer_task *ptask;
+ unsigned i = 0;
+ unsigned lowest = ~0;
+
+ if (list_empty(&ptimer->ptasks)) {
+ ptimer_set(0, 0);
+ return;
+ }
+
+ unsigned intervals[ptimer->task_count];
+
+ list_for_each_entry(ptask, &ptimer->ptasks, list) {
+ if (ptask->remain < lowest)
+ lowest = ptask->remain;
+ intervals[i++] = ptask->interval;
+ }
+
+ fprintf(stderr, "Lowest %u GCD is %u\n", lowest, array_gcd(intervals, i));
+ ptimer_set(lowest, array_gcd(intervals, i));
+}
+
+void
+ptimer_del_task(struct ptimer_task *ptask)
+{
+ struct ptimer *ptimer = cfg->ptimer;
+
+ assert_return(ptask && ptimer && ptimer->task_count > 0);
+
+ list_del(&ptask->list);
+ ptimer->task_count--;
+ ptimer_tick(ptimer);
+ ptimer_reconfig(ptimer);
+ uring_task_put(&ptimer->task);
+}
+
+void
+ptimer_add_task(struct ptimer_task *ptask)
+{
+ struct ptimer *ptimer = cfg->ptimer;
+
+ assert_return(ptask && ptask->interval > 0 && ptask->cb && ptimer);
+
+ uring_task_get(&ptimer->task);
+ ptask->remain = ptask->interval;
+ ptimer_tick(ptimer);
+ list_add(&ptask->list, &ptimer->ptasks);
+ ptimer->task_count++;
+ ptimer_reconfig(ptimer);
+}
+
+void
+ptimer_refdump()
+{
+ assert_return(cfg->ptimer);
+
+ uring_task_refdump(&cfg->ptimer->task);
+}
+
+static void
+ptimer_free(struct uring_task *task)
+{
+ struct ptimer *ptimer = container_of(task, struct ptimer, task);
+
+ assert_return(task);
+
+ debug(DBG_ANN, "task %p, ptimer %p", task, ptimer);
+ xfree(ptimer);
+ cfg->ptimer = NULL;
+}
+
+void
+ptimer_delete()
+{
+ assert_return(cfg->ptimer);
+
+ debug(DBG_ANN, "closing fd %i", cfg->ptimer->task.fd);
+ uring_task_destroy(&cfg->ptimer->task);
+}
+
+static void
+ptimer_cb(struct uring_task *task, int res)
+{
+ struct ptimer *ptimer = container_of(task, struct ptimer, task);
+
+ assert_return(task);
+ assert_task_alive(DBG_IGMP, task);
+
+ if (res != sizeof(ptimer->value)) {
+ error("timerfd_read: res: %i, %m", res);
+ return;
+ }
+
+ debug(DBG_IGMP, "Called");
+ ptimer_tick(ptimer);
+ uring_read(&ptimer->task, &ptimer->value, sizeof(ptimer->value), ptimer_cb);
+}
+
+void
+ptimer_init()
+{
+ struct ptimer *ptimer;
+ int tfd;
+
+ assert_return(!cfg->ptimer);
+
+ ptimer = zmalloc(sizeof(*ptimer));
+ if (!ptimer)
+ die("malloc: %m");
+
+ tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (tfd < 0)
+ die("timerfd_create: %m");
+
+ ptimer->task_count = 0;
+ ptimer->previous_time = time(NULL);
+ list_init(&ptimer->ptasks);
+ uring_task_init(&ptimer->task, "ptimer", uring_parent(), ptimer_free);
+ uring_task_set_fd(&ptimer->task, tfd);
+ cfg->ptimer = ptimer;
+ uring_read(&ptimer->task, &ptimer->value, sizeof(ptimer->value), ptimer_cb);
+}
+
diff --git a/ptimer.h b/ptimer.h
new file mode 100644
index 0000000..9ac53f7
--- /dev/null
+++ b/ptimer.h
@@ -0,0 +1,25 @@
+#ifndef fooptimerhfoo
+#define fooptimerhfoo
+
+struct ptimer_task {
+ /* to be set by caller */
+ unsigned interval;
+ unsigned times;
+ void (*cb)(struct ptimer_task *);
+
+ /* internal */
+ unsigned remain;
+ struct list_head list;
+};
+
+void ptimer_del_task(struct ptimer_task *ptask);
+
+void ptimer_add_task(struct ptimer_task *ptask);
+
+void ptimer_refdump();
+
+void ptimer_delete();
+
+void ptimer_init();
+
+#endif
diff --git a/signal-handler.c b/signal-handler.c
index 13747dd..67c2e0b 100644
--- a/signal-handler.c
+++ b/signal-handler.c
@@ -14,6 +14,7 @@
#include "igmp.h"
#include "announce.h"
#include "idle.h"
+#include "ptimer.h"
struct signal_ev {
struct uring_task task;
@@ -90,6 +91,7 @@ signalfd_read(struct uring_task *task, int res)
sd_notifyf(0, "STOPPING=1\nSTATUS=Received signal, exiting");
dump_tree();
signal_delete();
+ ptimer_delete();
igmp_delete();
announce_delete();
idle_delete();
diff --git a/utils.h b/utils.h
index ff29d6c..c36a36c 100644
--- a/utils.h
+++ b/utils.h
@@ -160,6 +160,10 @@ static inline bool list_empty(struct list_head *list)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \