summaryrefslogtreecommitdiff
path: root/proto/bfd/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bfd/io.c')
-rw-r--r--proto/bfd/io.c253
1 files changed, 193 insertions, 60 deletions
diff --git a/proto/bfd/io.c b/proto/bfd/io.c
index f7fbc67a..c5f2d1b0 100644
--- a/proto/bfd/io.c
+++ b/proto/bfd/io.c
@@ -1,18 +1,44 @@
+/*
+ * BIRD -- I/O and event loop
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "nest/bird.h"
+#include "proto/bfd/io.h"
#include "lib/buffer.h"
#include "lib/heap.h"
+#include "lib/lists.h"
+#include "lib/resource.h"
+#include "lib/event.h"
+#include "lib/socket.h"
+
struct birdloop
{
pool *pool;
+ pthread_t thread;
+ pthread_mutex_t mutex;
- int wakeup_fds[2];
- u8 poll_active;
-
- xxx_time last_time;
- xxx_time real_time;
+ btime last_time;
+ btime real_time;
u8 use_monotonic_clock;
+ u8 poll_active;
+ u8 wakeup_masked;
+ int wakeup_fds[2];
+
BUFFER(timer2 *) timers;
list event_list;
list sock_list;
@@ -22,13 +48,35 @@ struct birdloop
BUFFER(struct pollfd) poll_fd;
u8 poll_changed;
u8 close_scheduled;
-
};
+
+static pthread_key_t current_loop_key;
+
+static inline struct birdloop *
+birdloop_current(void)
+{
+ return pthread_getspecific(current_loop_key);
+}
+
+static inline void
+birdloop_set_current(struct birdloop *loop)
+{
+ pthread_setspecific(current_loop_key, loop);
+}
+
+static inline void
+birdloop_init_current(void)
+{
+ pthread_key_create(&current_loop_key, NULL);
+}
+
+
+
static void times_update_alt(struct birdloop *loop);
-static int
+static void
times_init(struct birdloop *loop)
{
struct timespec ts;
@@ -47,12 +95,12 @@ times_init(struct birdloop *loop)
}
/*
- if ((tv.tv_sec < 0) || (((s64) tv.tv_sec) > ((s64) 1 << 40)))
+ if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
log(L_WARN "Monotonic clock is crazy");
*/
loop->use_monotonic_clock = 1;
- loop->last_time = (tv.tv_sec S) + (tv.tv_nsec / 1000);
+ loop->last_time = (ts.tv_sec S) + (ts.tv_nsec / 1000);
loop->real_time = 0;
}
@@ -66,7 +114,7 @@ times_update_pri(struct birdloop *loop)
if (rv < 0)
die("clock_gettime: %m");
- xxx_time new_time = (tv.tv_sec S) + (tv.tv_nsec / 1000);
+ btime new_time = (ts.tv_sec S) + (ts.tv_nsec / 1000);
/*
if (new_time < loop->last_time)
@@ -87,8 +135,8 @@ times_update_alt(struct birdloop *loop)
if (rv < 0)
die("gettimeofday: %m");
- xxx_time new_time = (tv.tv_sec S) + tv.tv_usec;
- xxx_time delta = new_time - loop->real_time;
+ btime new_time = (tv.tv_sec S) + tv.tv_usec;
+ btime delta = new_time - loop->real_time;
if ((delta < 0) || (delta > (60 S)))
{
@@ -113,15 +161,18 @@ times_update(struct birdloop *loop)
times_update_alt(loop);
}
+btime
+current_time(void)
+{
+ return birdloop_current()->last_time;
+}
+
static void
pipe_new(int *pfds)
{
- int pfds[2], rv;
- sock *sk;
-
- rv = pipe(pfds);
+ int rv = pipe(pfds);
if (rv < 0)
die("pipe: %m");
@@ -132,20 +183,14 @@ pipe_new(int *pfds)
die("fcntl(O_NONBLOCK): %m");
}
-static void
-wakeup_init(struct birdloop *loop)
-{
- pipe_new(loop->wakeup_fds);
-}
-
-static void
-wakeup_drain(struct birdloop *loop)
+void
+pipe_drain(int fd)
{
char buf[64];
int rv;
try:
- rv = read(loop->wakeup_fds[0], buf, 64);
+ rv = read(fd, buf, 64);
if (rv < 0)
{
if (errno == EINTR)
@@ -158,14 +203,14 @@ wakeup_drain(struct birdloop *loop)
goto try;
}
-static void
-wakeup_kick(struct birdloop *loop)
+void
+pipe_kick(int fd)
{
u64 v = 1;
int rv;
try:
- rv = write(loop->wakeup_fds[1], &v, sizeof(u64));
+ rv = write(fd, &v, sizeof(u64));
if (rv < 0)
{
if (errno == EINTR)
@@ -176,16 +221,45 @@ wakeup_kick(struct birdloop *loop)
}
}
+static inline void
+wakeup_init(struct birdloop *loop)
+{
+ pipe_new(loop->wakeup_fds);
+}
+static inline void
+wakeup_drain(struct birdloop *loop)
+{
+ pipe_drain(loop->wakeup_fds[0]);
+}
+static inline void
+wakeup_do_kick(struct birdloop *loop)
+{
+ pipe_kick(loop->wakeup_fds[1]);
+}
-static inline uint events_waiting(struct birdloop *loop)
-{ return !EMPTY_LIST(loop->event_list); }
+static inline void
+wakeup_kick(struct birdloop *loop)
+{
+ if (!loop->wakeup_masked)
+ wakeup_do_kick(loop);
+ else
+ loop->wakeup_masked = 2;
+}
-static void
+
+
+static inline uint
+events_waiting(struct birdloop *loop)
+{
+ return !EMPTY_LIST(loop->event_list);
+}
+
+static inline void
events_init(struct birdloop *loop)
{
- list_init(&poll->event_list);
+ init_list(&loop->event_list);
}
static void
@@ -198,6 +272,8 @@ events_fire(struct birdloop *loop)
void
ev2_schedule(event *e)
{
+ struct birdloop *loop = birdloop_current();
+
if (loop->poll_active && EMPTY_LIST(loop->event_list))
wakeup_kick(loop);
@@ -208,6 +284,7 @@ ev2_schedule(event *e)
}
+
#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
heap[a]->index = (a), heap[b]->index = (b))
@@ -239,14 +316,15 @@ tm2_dump(resource *r)
if (t->recurrent)
debug("recur %d, ", t->recurrent);
if (t->expires)
- debug("expires in %d sec)\n", t->expires - xxx_now);
+ debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
else
debug("inactive)\n");
}
+
static struct resclass tm2_class = {
"Timer",
- sizeof(timer),
+ sizeof(timer2),
tm2_free,
tm2_dump,
NULL,
@@ -262,9 +340,9 @@ tm2_new(pool *p)
}
void
-tm2_start(timer2 *t, xxx_time after)
+tm2_set(timer2 *t, btime when)
{
- xxx_time when = loop->last_time + after;
+ struct birdloop *loop = birdloop_current();
uint tc = timers_count(loop);
if (!t->expires)
@@ -290,12 +368,20 @@ tm2_start(timer2 *t, xxx_time after)
}
void
+tm2_start(timer2 *t, btime after)
+{
+ tm2_set(t, current_time() + MAX(after, 0));
+}
+
+void
tm2_stop(timer2 *t)
{
if (!t->expires)
return;
- uint tc = timers_count(XXX);
+ struct birdloop *loop = birdloop_current();
+ uint tc = timers_count(loop);
+
HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
BUFFER_POP(loop->timers);
@@ -313,7 +399,7 @@ timers_init(struct birdloop *loop)
static void
timers_fire(struct birdloop *loop)
{
- xxx_time base_time;
+ btime base_time;
timer2 *t;
times_update(loop);
@@ -326,16 +412,15 @@ timers_fire(struct birdloop *loop)
if (t->recurrent)
{
- xxx_time after = t->recurrent;
- xxx_time delta = loop->last_time - t->expires;
+ btime when = t->expires + t->recurrent;
- if (t->randomize)
- after += random() % (t->randomize + 1);
+ if (when <= loop->last_time)
+ when = loop->last_time + t->recurrent;
- if (delta > after)
- delta = 0;
+ if (t->randomize)
+ when += random() % (t->randomize + 1);
- tm2_start(t, after - delta);
+ tm2_set(t, when);
}
else
tm2_stop(t);
@@ -345,15 +430,16 @@ timers_fire(struct birdloop *loop)
}
+
static void
sockets_init(struct birdloop *loop)
{
- list_init(&poll->sock_list);
- poll->sock_num = 0;
+ init_list(&loop->sock_list);
+ loop->sock_num = 0;
BUFFER_INIT(loop->poll_sk, loop->pool, 4);
BUFFER_INIT(loop->poll_fd, loop->pool, 4);
- poll_changed = 0;
+ loop->poll_changed = 1; /* add wakeup fd */
}
static void
@@ -372,7 +458,9 @@ sockets_add(struct birdloop *loop, sock *s)
void
sk_start(sock *s)
{
- sockets_add(xxx_loop, s);
+ struct birdloop *loop = birdloop_current();
+
+ sockets_add(loop, s);
}
static void
@@ -382,7 +470,7 @@ sockets_remove(struct birdloop *loop, sock *s)
loop->sock_num--;
if (s->index >= 0)
- s->poll_sk.data[sk->index] = NULL;
+ loop->poll_sk.data[s->index] = NULL;
s->index = -1;
loop->poll_changed = 1;
@@ -393,7 +481,9 @@ sockets_remove(struct birdloop *loop, sock *s)
void
sk_stop(sock *s)
{
- sockets_remove(xxx_loop, s);
+ struct birdloop *loop = birdloop_current();
+
+ sockets_remove(loop, s);
if (loop->poll_active)
{
@@ -413,7 +503,7 @@ static void
sockets_update(struct birdloop *loop, sock *s)
{
if (s->index >= 0)
- s->poll_fd.data[s->index].events = sk_want_events(s);
+ loop->poll_fd.data[s->index].events = sk_want_events(s);
}
static void
@@ -427,7 +517,7 @@ sockets_prepare(struct birdloop *loop)
int i = 0;
node *n;
- WALK_LIST(n, &loop->sock_list)
+ WALK_LIST(n, loop->sock_list)
{
sock *s = SKIP_BACK(sock, n, n);
@@ -470,6 +560,8 @@ sockets_close_fds(struct birdloop *loop)
loop->close_scheduled = 0;
}
+int sk_read(sock *s);
+int sk_write(sock *s);
static void
sockets_fire(struct birdloop *loop)
@@ -507,11 +599,20 @@ sockets_fire(struct birdloop *loop)
}
+
+static void * birdloop_main(void *arg);
+
struct birdloop *
birdloop_new(pool *p)
{
+ /* FIXME: this init should be elsewhere and thread-safe */
+ static int init = 0;
+ if (!init)
+ { birdloop_init_current(); init = 1; }
+
struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
- p->pool = p;
+ loop->pool = p;
+ pthread_mutex_init(&loop->mutex, NULL);
times_init(loop);
wakeup_init(loop);
@@ -520,28 +621,58 @@ birdloop_new(pool *p)
timers_init(loop);
sockets_init(loop);
+
+ int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
+ if (rv < 0)
+ die("pthread_create(): %m");
+
return loop;
}
void
birdloop_enter(struct birdloop *loop)
{
- pthread_mutex_lock(loop->mutex);
+ /* TODO: these functions could save and restore old context */
+ pthread_mutex_lock(&loop->mutex);
+ birdloop_set_current(loop);
}
void
birdloop_leave(struct birdloop *loop)
{
- pthread_mutex_unlock(loop->mutex);
+ /* TODO: these functions could save and restore old context */
+ birdloop_set_current(NULL);
+ pthread_mutex_unlock(&loop->mutex);
}
+void
+birdloop_mask_wakeups(struct birdloop *loop)
+{
+ pthread_mutex_lock(&loop->mutex);
+ loop->wakeup_masked = 1;
+ pthread_mutex_unlock(&loop->mutex);
+}
void
-birdloop_main(struct birdloop *loop)
+birdloop_unmask_wakeups(struct birdloop *loop)
{
+ pthread_mutex_lock(&loop->mutex);
+ if (loop->wakeup_masked == 2)
+ wakeup_do_kick(loop);
+ loop->wakeup_masked = 0;
+ pthread_mutex_unlock(&loop->mutex);
+}
+
+static void *
+birdloop_main(void *arg)
+{
+ struct birdloop *loop = arg;
timer2 *t;
- int timeout;
+ int rv, timeout;
+ birdloop_set_current(loop);
+
+ pthread_mutex_lock(&loop->mutex);
while (1)
{
events_fire(loop);
@@ -559,7 +690,7 @@ birdloop_main(struct birdloop *loop)
sockets_prepare(loop);
loop->poll_active = 1;
- pthread_mutex_unlock(loop->mutex);
+ pthread_mutex_unlock(&loop->mutex);
try:
rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
@@ -570,7 +701,7 @@ birdloop_main(struct birdloop *loop)
die("poll: %m");
}
- pthread_mutex_lock(loop->mutex);
+ pthread_mutex_lock(&loop->mutex);
loop->poll_active = 0;
if (loop->close_scheduled)
@@ -581,6 +712,8 @@ birdloop_main(struct birdloop *loop)
timers_fire(loop);
}
+
+ return NULL;
}