0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-10 13:18:42 +00:00
bird/sysdep/unix/io-loop.c

647 lines
12 KiB
C
Raw Normal View History

/*
* 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"
2013-09-10 10:09:36 +00:00
#include "lib/buffer.h"
#include "lib/lists.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/socket.h"
#include "lib/io-loop.h"
#include "sysdep/unix/io-loop.h"
#include "conf/conf.h"
2013-09-10 10:09:36 +00:00
#define THREAD_STACK_SIZE 65536 /* To be lowered in near future */
/*
* Current thread context
*/
_Thread_local struct birdloop *birdloop_current;
static _Thread_local struct birdloop *birdloop_wakeup_masked;
static _Thread_local uint birdloop_wakeup_masked_count;
event_list *
birdloop_event_list(struct birdloop *loop)
{
return &loop->event_list;
}
struct timeloop *
birdloop_time_loop(struct birdloop *loop)
{
return &loop->time;
}
_Bool
birdloop_inside(struct birdloop *loop)
{
for (struct birdloop *c = birdloop_current; c; c = c->prev_loop)
if (loop == c)
return 1;
return 0;
}
void
birdloop_flag(struct birdloop *loop, u32 flag)
{
atomic_fetch_or_explicit(&loop->flags, flag, memory_order_acq_rel);
birdloop_ping(loop);
}
void
birdloop_flag_set_handler(struct birdloop *loop, struct birdloop_flag_handler *fh)
{
ASSERT_DIE(birdloop_inside(loop));
loop->flag_handler = fh;
}
static int
birdloop_process_flags(struct birdloop *loop)
{
if (!loop->flag_handler)
return 0;
u32 flags = atomic_exchange_explicit(&loop->flags, 0, memory_order_acq_rel);
loop->flag_handler->hook(loop->flag_handler, flags);
return !!flags;
}
2022-09-21 09:49:35 +00:00
static int
birdloop_run_events(struct birdloop *loop)
{
btime begin = current_time();
while (current_time() - begin < 5 MS)
{
if (!ev_run_list(&loop->event_list))
return 0;
times_update();
}
return 1;
}
/*
* Wakeup code for birdloop
*/
2013-09-10 10:09:36 +00:00
void
pipe_new(struct pipe *p)
2013-09-10 10:09:36 +00:00
{
int rv = pipe(p->fd);
2013-09-10 10:09:36 +00:00
if (rv < 0)
die("pipe: %m");
if (fcntl(p->fd[0], F_SETFL, O_NONBLOCK) < 0)
2013-09-10 10:09:36 +00:00
die("fcntl(O_NONBLOCK): %m");
if (fcntl(p->fd[1], F_SETFL, O_NONBLOCK) < 0)
2013-09-10 10:09:36 +00:00
die("fcntl(O_NONBLOCK): %m");
}
void
pipe_drain(struct pipe *p)
2013-09-10 10:09:36 +00:00
{
while (1) {
char buf[64];
int rv = read(p->fd[0], buf, sizeof(buf));
if ((rv < 0) && (errno == EAGAIN))
2013-09-10 10:09:36 +00:00
return;
if (rv == 0)
bug("wakeup read eof");
if ((rv < 0) && (errno != EINTR))
bug("wakeup read: %m");
}
}
int
pipe_read_one(struct pipe *p)
{
while (1) {
char v;
int rv = read(p->fd[0], &v, sizeof(v));
if (rv == 1)
return 1;
if ((rv < 0) && (errno == EAGAIN))
return 0;
if (rv > 1)
bug("wakeup read more bytes than expected: %d", rv);
if (rv == 0)
bug("wakeup read eof");
if (errno != EINTR)
bug("wakeup read: %m");
2013-09-10 10:09:36 +00:00
}
}
void
pipe_kick(struct pipe *p)
2013-09-10 10:09:36 +00:00
{
char v = 1;
2013-09-10 10:09:36 +00:00
int rv;
while (1) {
rv = write(p->fd[1], &v, sizeof(v));
if ((rv >= 0) || (errno == EAGAIN))
2013-09-10 10:09:36 +00:00
return;
if (errno != EINTR)
bug("wakeup write: %m");
2013-09-10 10:09:36 +00:00
}
}
void
pipe_pollin(struct pipe *p, struct pollfd *pfd)
{
pfd->fd = p->fd[0];
pfd->events = POLLIN;
pfd->revents = 0;
}
static inline void
wakeup_init(struct birdloop *loop)
{
pipe_new(&loop->wakeup);
}
2013-09-10 10:09:36 +00:00
static inline void
wakeup_drain(struct birdloop *loop)
{
pipe_drain(&loop->wakeup);
}
2013-09-10 10:09:36 +00:00
static inline void
wakeup_do_kick(struct birdloop *loop)
{
pipe_kick(&loop->wakeup);
}
2013-09-10 10:09:36 +00:00
static inline void
birdloop_do_ping(struct birdloop *loop)
2013-09-10 10:09:36 +00:00
{
if (atomic_fetch_add_explicit(&loop->ping_sent, 1, memory_order_acq_rel))
return;
2013-09-10 10:09:36 +00:00
if (loop == birdloop_wakeup_masked)
birdloop_wakeup_masked_count++;
else
wakeup_do_kick(loop);
2013-09-10 10:09:36 +00:00
}
void
birdloop_ping(struct birdloop *loop)
{
if (birdloop_inside(loop) && !loop->ping_pending)
loop->ping_pending++;
else
birdloop_do_ping(loop);
}
2013-09-10 10:09:36 +00:00
/*
* Sockets
*/
2013-09-10 10:09:36 +00:00
static void
sockets_init(struct birdloop *loop)
{
init_list(&loop->sock_list);
loop->sock_num = 0;
2013-09-10 10:09:36 +00:00
BUFFER_INIT(loop->poll_sk, loop->pool, 4);
BUFFER_INIT(loop->poll_fd, loop->pool, 4);
loop->poll_changed = 1; /* add wakeup fd */
2013-09-10 10:09:36 +00:00
}
static void
sockets_add(struct birdloop *loop, sock *s)
{
add_tail(&loop->sock_list, &s->n);
loop->sock_num++;
s->index = -1;
loop->poll_changed = 1;
birdloop_ping(loop);
2013-09-10 10:09:36 +00:00
}
void
sk_start(sock *s)
{
ASSERT_DIE(birdloop_current != &main_birdloop);
sockets_add(birdloop_current, s);
2013-09-10 10:09:36 +00:00
}
static void
sockets_remove(struct birdloop *loop, sock *s)
{
if (!enlisted(&s->n))
return;
2013-09-10 10:09:36 +00:00
rem_node(&s->n);
loop->sock_num--;
if (s->index >= 0)
{
loop->poll_sk.data[s->index] = NULL;
s->index = -1;
loop->poll_changed = 1;
loop->close_scheduled = 1;
birdloop_ping(loop);
}
else
close(s->fd);
2013-09-10 10:09:36 +00:00
}
void
sk_stop(sock *s)
{
sockets_remove(birdloop_current, s);
2013-09-10 10:09:36 +00:00
}
static inline uint sk_want_events(sock *s)
{ return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
/*
FIXME: this should be called from sock code
2013-09-10 10:09:36 +00:00
static void
sockets_update(struct birdloop *loop, sock *s)
{
if (s->index >= 0)
loop->poll_fd.data[s->index].events = sk_want_events(s);
2013-09-10 10:09:36 +00:00
}
*/
2013-09-10 10:09:36 +00:00
static void
sockets_prepare(struct birdloop *loop)
{
BUFFER_SET(loop->poll_sk, loop->sock_num + 1);
BUFFER_SET(loop->poll_fd, loop->sock_num + 1);
struct pollfd *pfd = loop->poll_fd.data;
sock **psk = loop->poll_sk.data;
2017-12-16 15:31:43 +00:00
uint i = 0;
2013-09-10 10:09:36 +00:00
node *n;
WALK_LIST(n, loop->sock_list)
2013-09-10 10:09:36 +00:00
{
sock *s = SKIP_BACK(sock, n, n);
ASSERT(i < loop->sock_num);
s->index = i;
*psk = s;
pfd->fd = s->fd;
pfd->events = sk_want_events(s);
pfd->revents = 0;
pfd++;
psk++;
i++;
}
ASSERT(i == loop->sock_num);
/* Add internal wakeup fd */
*psk = NULL;
pipe_pollin(&loop->wakeup, pfd);
2013-09-10 10:09:36 +00:00
loop->poll_changed = 0;
}
static void
sockets_close_fds(struct birdloop *loop)
{
struct pollfd *pfd = loop->poll_fd.data;
sock **psk = loop->poll_sk.data;
int poll_num = loop->poll_fd.used - 1;
int i;
for (i = 0; i < poll_num; i++)
if (psk[i] == NULL)
close(pfd[i].fd);
loop->close_scheduled = 0;
}
int sk_read(sock *s, int revents);
int sk_write(sock *s);
2013-09-10 10:09:36 +00:00
static void
sockets_fire(struct birdloop *loop)
{
struct pollfd *pfd = loop->poll_fd.data;
sock **psk = loop->poll_sk.data;
int poll_num = loop->poll_fd.used - 1;
times_update();
2013-09-10 10:09:36 +00:00
/* Last fd is internal wakeup fd */
if (pfd[poll_num].revents & POLLIN)
2013-09-10 10:09:36 +00:00
wakeup_drain(loop);
int i;
for (i = 0; i < poll_num; pfd++, psk++, i++)
{
int e = 1;
if (! pfd->revents)
continue;
if (pfd->revents & POLLNVAL)
bug("poll: invalid fd %d", pfd->fd);
2013-09-10 10:09:36 +00:00
if (pfd->revents & POLLIN)
while (e && *psk && (*psk)->rx_hook)
e = sk_read(*psk, pfd->revents);
2013-09-10 10:09:36 +00:00
e = 1;
if (pfd->revents & POLLOUT)
{
loop->poll_changed = 1;
2013-09-10 10:09:36 +00:00
while (e && *psk)
e = sk_write(*psk);
}
2013-09-10 10:09:36 +00:00
}
}
/*
* Birdloop
*/
struct birdloop main_birdloop;
static void birdloop_enter_locked(struct birdloop *loop);
void
birdloop_init(void)
{
wakeup_init(&main_birdloop);
main_birdloop.time.domain = the_bird_domain.the_bird;
main_birdloop.time.loop = &main_birdloop;
times_update();
timers_init(&main_birdloop.time, &root_pool);
birdloop_enter_locked(&main_birdloop);
}
static void *birdloop_main(void *arg);
2013-09-10 10:09:36 +00:00
struct birdloop *
birdloop_new(pool *pp, uint order, const char *name)
2013-09-10 10:09:36 +00:00
{
struct domain_generic *dg = domain_new(name, order);
pool *p = rp_new(pp, name);
2013-09-10 10:09:36 +00:00
struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
loop->pool = p;
2013-09-10 10:09:36 +00:00
loop->time.domain = dg;
loop->time.loop = loop;
2013-09-10 10:09:36 +00:00
birdloop_enter(loop);
2013-09-10 10:09:36 +00:00
wakeup_init(loop);
ev_init_list(&loop->event_list, loop, name);
timers_init(&loop->time, p);
2013-09-10 10:09:36 +00:00
sockets_init(loop);
int e = 0;
if (e = pthread_attr_init(&loop->thread_attr))
die("pthread_attr_init() failed: %M", e);
if (e = pthread_attr_setstacksize(&loop->thread_attr, THREAD_STACK_SIZE))
die("pthread_attr_setstacksize(%u) failed: %M", THREAD_STACK_SIZE, e);
if (e = pthread_attr_setdetachstate(&loop->thread_attr, PTHREAD_CREATE_DETACHED))
die("pthread_attr_setdetachstate(PTHREAD_CREATE_DETACHED) failed: %M", e);
if (e = pthread_create(&loop->thread_id, &loop->thread_attr, birdloop_main, loop))
die("pthread_create() failed: %M", e);
birdloop_leave(loop);
return loop;
}
static void
birdloop_do_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
{
loop->stopped = stopped;
loop->stop_data = data;
wakeup_do_kick(loop);
}
void
birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
{
DG_LOCK(loop->time.domain);
birdloop_do_stop(loop, stopped, data);
DG_UNLOCK(loop->time.domain);
}
void
birdloop_stop_self(struct birdloop *loop, void (*stopped)(void *data), void *data)
{
ASSERT_DIE(loop == birdloop_current);
ASSERT_DIE(DG_IS_LOCKED(loop->time.domain));
birdloop_do_stop(loop, stopped, data);
2013-09-10 10:09:36 +00:00
}
void
birdloop_free(struct birdloop *loop)
{
ASSERT_DIE(loop->links == 0);
ASSERT_DIE(pthread_equal(pthread_self(), loop->thread_id));
rcu_birdloop_stop(&loop->rcu);
pthread_attr_destroy(&loop->thread_attr);
domain_free(loop->time.domain);
rfree(loop->pool);
}
static void
birdloop_enter_locked(struct birdloop *loop)
{
ASSERT_DIE(DG_IS_LOCKED(loop->time.domain));
ASSERT_DIE(!birdloop_inside(loop));
/* Store the old context */
loop->prev_loop = birdloop_current;
/* Put the new context */
birdloop_current = loop;
}
2013-09-10 10:09:36 +00:00
void
birdloop_enter(struct birdloop *loop)
{
DG_LOCK(loop->time.domain);
return birdloop_enter_locked(loop);
}
static void
birdloop_leave_locked(struct birdloop *loop)
{
/* Check the current context */
ASSERT_DIE(birdloop_current == loop);
/* Send pending pings */
if (loop->ping_pending)
{
loop->ping_pending = 0;
birdloop_do_ping(loop);
}
/* Restore the old context */
birdloop_current = loop->prev_loop;
2013-09-10 10:09:36 +00:00
}
void
birdloop_leave(struct birdloop *loop)
{
birdloop_leave_locked(loop);
DG_UNLOCK(loop->time.domain);
2013-09-10 10:09:36 +00:00
}
void
birdloop_mask_wakeups(struct birdloop *loop)
{
ASSERT_DIE(birdloop_wakeup_masked == NULL);
birdloop_wakeup_masked = loop;
}
2013-09-10 10:09:36 +00:00
void
birdloop_unmask_wakeups(struct birdloop *loop)
2013-09-10 10:09:36 +00:00
{
ASSERT_DIE(birdloop_wakeup_masked == loop);
birdloop_wakeup_masked = NULL;
if (birdloop_wakeup_masked_count)
wakeup_do_kick(loop);
birdloop_wakeup_masked_count = 0;
}
void
birdloop_link(struct birdloop *loop)
{
ASSERT_DIE(birdloop_inside(loop));
loop->links++;
}
void
birdloop_unlink(struct birdloop *loop)
{
ASSERT_DIE(birdloop_inside(loop));
loop->links--;
}
static void *
birdloop_main(void *arg)
{
struct birdloop *loop = arg;
timer *t;
int rv, timeout;
2013-09-10 10:09:36 +00:00
rcu_birdloop_start(&loop->rcu);
btime loop_begin = current_time();
tmp_init(loop->pool);
birdloop_enter(loop);
2013-09-10 10:09:36 +00:00
while (1)
{
timers_fire(&loop->time, 0);
2022-09-21 09:49:35 +00:00
if (birdloop_process_flags(loop) + birdloop_run_events(loop))
2013-09-10 10:09:36 +00:00
timeout = 0;
else if (t = timers_first(&loop->time))
timeout = (tm_remains(t) TO_MS) + 1;
2013-09-10 10:09:36 +00:00
else
timeout = -1;
if (loop->poll_changed)
sockets_prepare(loop);
2022-09-21 09:49:35 +00:00
else
if ((timeout < 0) || (timeout > 5000))
flush_local_pages();
2013-09-10 10:09:36 +00:00
btime duration = current_time() - loop_begin;
if (duration > config->watchdog_warning)
log(L_WARN "I/O loop cycle took %d ms", (int) (duration TO_MS));
birdloop_leave(loop);
2013-09-10 10:09:36 +00:00
try:
rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
if (rv < 0)
{
if (errno == EINTR || errno == EAGAIN)
goto try;
bug("poll: %m");
2013-09-10 10:09:36 +00:00
}
birdloop_enter(loop);
2013-09-10 10:09:36 +00:00
if (loop->close_scheduled)
sockets_close_fds(loop);
if (loop->stopped)
break;
loop_begin = current_time();
if (rv && !loop->poll_changed)
2013-09-10 10:09:36 +00:00
sockets_fire(loop);
atomic_exchange_explicit(&loop->ping_sent, 0, memory_order_acq_rel);
2013-09-10 10:09:36 +00:00
}
/* Flush remaining events */
ASSERT_DIE(!ev_run_list(&loop->event_list));
2022-09-11 20:26:24 +00:00
/* Drop timers */
while (t = timers_first(&loop->time))
tm_stop(t);
/* No sockets allowed */
ASSERT_DIE(EMPTY_LIST(loop->sock_list));
ASSERT_DIE(loop->sock_num == 0);
birdloop_leave(loop);
loop->stopped(loop->stop_data);
2022-09-21 09:49:35 +00:00
flush_local_pages();
return NULL;
2013-09-10 10:09:36 +00:00
}
void
birdloop_yield(void)
{
usleep(100);
}