2013-09-16 21:57:40 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
2013-09-16 21:57:40 +00:00
|
|
|
#include "lib/lists.h"
|
2023-01-19 09:49:47 +00:00
|
|
|
#include "lib/locking.h"
|
2013-09-16 21:57:40 +00:00
|
|
|
#include "lib/resource.h"
|
|
|
|
#include "lib/event.h"
|
2017-05-30 17:12:35 +00:00
|
|
|
#include "lib/timer.h"
|
2013-09-16 21:57:40 +00:00
|
|
|
#include "lib/socket.h"
|
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
#include "lib/io-loop.h"
|
|
|
|
#include "sysdep/unix/io-loop.h"
|
|
|
|
#include "conf/conf.h"
|
2023-01-19 09:49:47 +00:00
|
|
|
#include "nest/cli.h"
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2022-07-28 17:49:03 +00:00
|
|
|
#define THREAD_STACK_SIZE 65536 /* To be lowered in near future */
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
/*
|
|
|
|
* Nanosecond time for accounting purposes
|
|
|
|
*
|
|
|
|
* A fixed point on startup is set as zero, all other values are relative to that.
|
|
|
|
* Caution: this overflows after like 500 years or so. If you plan to run
|
|
|
|
* BIRD for such a long time, please implement some means of overflow prevention.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct timespec ns_begin;
|
|
|
|
|
|
|
|
static void ns_init(void)
|
|
|
|
{
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ns_begin))
|
|
|
|
bug("clock_gettime: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64 ns_now(void)
|
|
|
|
{
|
|
|
|
struct timespec ts;
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts))
|
|
|
|
bug("clock_gettime: %m");
|
|
|
|
|
|
|
|
return (u64) (ts.tv_sec - ns_begin.tv_sec) * 1000000000 + ts.tv_nsec - ns_begin.tv_nsec;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-11-19 21:33:48 +00:00
|
|
|
/*
|
|
|
|
* Current thread context
|
|
|
|
*/
|
2013-09-16 21:57:40 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
_Thread_local struct birdloop *birdloop_current;
|
|
|
|
static _Thread_local struct birdloop *birdloop_wakeup_masked;
|
|
|
|
static _Thread_local uint birdloop_wakeup_masked_count;
|
2013-09-16 21:57:40 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
event_list *
|
|
|
|
birdloop_event_list(struct birdloop *loop)
|
2013-09-16 21:57:40 +00:00
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
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;
|
2013-09-16 21:57:40 +00:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
_Bool
|
|
|
|
birdloop_in_this_thread(struct birdloop *loop)
|
|
|
|
{
|
|
|
|
return pthread_equal(pthread_self(), loop->thread->thread_id);
|
|
|
|
}
|
|
|
|
|
2022-09-12 08:24:55 +00:00
|
|
|
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);
|
2023-01-19 09:49:47 +00:00
|
|
|
if (!flags)
|
|
|
|
return 0;
|
2022-09-21 09:49:35 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
loop->flag_handler->hook(loop->flag_handler, flags);
|
2022-09-21 09:49:35 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:33:48 +00:00
|
|
|
/*
|
|
|
|
* Wakeup code for birdloop
|
|
|
|
*/
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2022-09-20 15:01:50 +00:00
|
|
|
void
|
|
|
|
pipe_new(struct pipe *p)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2022-09-20 15:01:50 +00:00
|
|
|
int rv = pipe(p->fd);
|
2013-09-10 10:09:36 +00:00
|
|
|
if (rv < 0)
|
|
|
|
die("pipe: %m");
|
|
|
|
|
2022-09-20 15:01:50 +00:00
|
|
|
if (fcntl(p->fd[0], F_SETFL, O_NONBLOCK) < 0)
|
2013-09-10 10:09:36 +00:00
|
|
|
die("fcntl(O_NONBLOCK): %m");
|
|
|
|
|
2022-09-20 15:01:50 +00:00
|
|
|
if (fcntl(p->fd[1], F_SETFL, O_NONBLOCK) < 0)
|
2013-09-10 10:09:36 +00:00
|
|
|
die("fcntl(O_NONBLOCK): %m");
|
|
|
|
}
|
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
void
|
2022-09-20 15:01:50 +00:00
|
|
|
pipe_drain(struct pipe *p)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2022-09-20 15:01:50 +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;
|
2022-09-20 15:01:50 +00:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
void
|
2022-09-20 15:01:50 +00:00
|
|
|
pipe_kick(struct pipe *p)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2022-09-20 15:01:50 +00:00
|
|
|
char v = 1;
|
2013-09-10 10:09:36 +00:00
|
|
|
int rv;
|
|
|
|
|
2022-09-20 15:01:50 +00:00
|
|
|
while (1) {
|
|
|
|
rv = write(p->fd[1], &v, sizeof(v));
|
2023-01-19 09:49:47 +00:00
|
|
|
if ((rv >= 0) || (errno == EAGAIN))
|
2013-09-10 10:09:36 +00:00
|
|
|
return;
|
2022-09-20 15:01:50 +00:00
|
|
|
if (errno != EINTR)
|
|
|
|
bug("wakeup write: %m");
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-20 15:01:50 +00:00
|
|
|
void
|
|
|
|
pipe_pollin(struct pipe *p, struct pollfd *pfd)
|
|
|
|
{
|
|
|
|
pfd->fd = p->fd[0];
|
|
|
|
pfd->events = POLLIN;
|
|
|
|
pfd->revents = 0;
|
|
|
|
}
|
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
static inline void
|
2023-01-19 09:49:47 +00:00
|
|
|
wakeup_init(struct bird_thread *loop)
|
2013-09-16 21:57:40 +00:00
|
|
|
{
|
2022-09-20 15:01:50 +00:00
|
|
|
pipe_new(&loop->wakeup);
|
2013-09-16 21:57:40 +00:00
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
static inline void
|
2023-01-19 09:49:47 +00:00
|
|
|
wakeup_drain(struct bird_thread *loop)
|
2013-09-16 21:57:40 +00:00
|
|
|
{
|
2022-09-20 15:01:50 +00:00
|
|
|
pipe_drain(&loop->wakeup);
|
2013-09-16 21:57:40 +00:00
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
static inline void
|
2023-01-19 09:49:47 +00:00
|
|
|
wakeup_do_kick(struct bird_thread *loop)
|
2013-09-16 21:57:40 +00:00
|
|
|
{
|
2022-09-20 15:01:50 +00:00
|
|
|
pipe_kick(&loop->wakeup);
|
2013-09-16 21:57:40 +00:00
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2022-07-28 17:49:03 +00:00
|
|
|
static inline void
|
|
|
|
birdloop_do_ping(struct birdloop *loop)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2023-01-19 09:49:47 +00:00
|
|
|
if (!loop->thread)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (atomic_fetch_add_explicit(&loop->thread->ping_sent, 1, memory_order_acq_rel))
|
2021-06-19 18:50:18 +00:00
|
|
|
return;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
if (loop == birdloop_wakeup_masked)
|
|
|
|
birdloop_wakeup_masked_count++;
|
|
|
|
else
|
2023-01-19 09:49:47 +00:00
|
|
|
wakeup_do_kick(loop->thread);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
2022-07-28 17:49:03 +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
|
|
|
|
2013-11-19 21:33:48 +00:00
|
|
|
/*
|
|
|
|
* Sockets
|
|
|
|
*/
|
2013-09-16 21:57:40 +00:00
|
|
|
|
2013-09-10 10:09:36 +00:00
|
|
|
static void
|
|
|
|
sockets_init(struct birdloop *loop)
|
|
|
|
{
|
2013-09-16 21:57:40 +00:00
|
|
|
init_list(&loop->sock_list);
|
|
|
|
loop->sock_num = 0;
|
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;
|
2023-01-19 09:49:47 +00:00
|
|
|
if (loop->thread)
|
|
|
|
atomic_store_explicit(&loop->thread->poll_changed, 1, memory_order_release);
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_ping(loop);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sk_start(sock *s)
|
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
ASSERT_DIE(birdloop_current != &main_birdloop);
|
2021-06-18 16:10:42 +00:00
|
|
|
sockets_add(birdloop_current, s);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sockets_remove(struct birdloop *loop, sock *s)
|
|
|
|
{
|
2022-10-04 11:20:04 +00:00
|
|
|
if (!enlisted(&s->n))
|
|
|
|
return;
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
/* Decouple the socket from the loop at all. */
|
2013-09-10 10:09:36 +00:00
|
|
|
rem_node(&s->n);
|
|
|
|
loop->sock_num--;
|
2023-01-19 09:49:47 +00:00
|
|
|
if (loop->thread)
|
|
|
|
atomic_store_explicit(&loop->thread->poll_changed, 1, memory_order_release);
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
s->index = -1;
|
|
|
|
|
|
|
|
/* Close the filedescriptor. If it ever gets into the poll(), it just returns
|
|
|
|
* POLLNVAL for this fd which then is ignored because nobody checks for
|
|
|
|
* that result. Or some other routine opens another fd, getting this number,
|
|
|
|
* yet also in this case poll() at worst spuriously returns and nobody checks
|
|
|
|
* for the result in this fd. No further precaution is needed. */
|
|
|
|
close(s->fd);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sk_stop(sock *s)
|
|
|
|
{
|
2021-06-18 16:10:42 +00:00
|
|
|
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); }
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
static struct pollfd *
|
|
|
|
sockets_prepare(struct birdloop *loop, struct pollfd *pfd, struct pollfd *end)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
|
|
|
node *n;
|
2023-01-19 09:49:47 +00:00
|
|
|
loop->pfd = pfd;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
WALK_LIST(n, loop->sock_list)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
|
|
|
sock *s = SKIP_BACK(sock, n, n);
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
/* Out of space for pfds. Force reallocation. */
|
|
|
|
if (pfd >= end)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s->index = pfd - loop->pfd;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
|
|
|
pfd->fd = s->fd;
|
|
|
|
pfd->events = sk_want_events(s);
|
|
|
|
pfd->revents = 0;
|
|
|
|
|
|
|
|
pfd++;
|
|
|
|
}
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
return pfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sk_read(sock *s, int revents);
|
|
|
|
int sk_write(sock *s);
|
|
|
|
|
|
|
|
static void
|
|
|
|
sockets_fire(struct birdloop *loop)
|
|
|
|
{
|
|
|
|
struct pollfd *pfd = loop->pfd;
|
|
|
|
|
|
|
|
times_update();
|
|
|
|
|
|
|
|
sock *s; node *n, *nxt;
|
|
|
|
WALK_LIST2_DELSAFE(s, n, nxt, loop->sock_list, n)
|
|
|
|
{
|
|
|
|
if (s->index < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int rev = pfd[s->index].revents;
|
|
|
|
|
|
|
|
if (!rev)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (rev & POLLNVAL)
|
|
|
|
bug("poll: invalid fd %d", s->fd);
|
|
|
|
|
|
|
|
int e = 1;
|
|
|
|
|
|
|
|
if (rev & POLLIN)
|
|
|
|
while (e && s->rx_hook)
|
|
|
|
e = sk_read(s, rev);
|
|
|
|
|
|
|
|
if (rev & POLLOUT)
|
|
|
|
{
|
|
|
|
atomic_store_explicit(&loop->thread->poll_changed, 1, memory_order_release);
|
|
|
|
while (e = sk_write(s))
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Threads
|
|
|
|
*/
|
|
|
|
|
|
|
|
DEFINE_DOMAIN(resource);
|
|
|
|
static DOMAIN(resource) birdloop_domain;
|
|
|
|
static list birdloop_pickup;
|
|
|
|
static list bird_thread_pickup;
|
|
|
|
|
|
|
|
static _Thread_local struct bird_thread *this_thread;
|
|
|
|
|
|
|
|
static void *
|
|
|
|
bird_thread_main(void *arg)
|
|
|
|
{
|
|
|
|
struct bird_thread *thr = this_thread = arg;
|
|
|
|
|
|
|
|
rcu_thread_start(&thr->rcu);
|
|
|
|
synchronize_rcu();
|
|
|
|
|
|
|
|
tmp_init(thr->pool);
|
|
|
|
init_list(&thr->loops);
|
|
|
|
|
|
|
|
u32 refresh_sockets = 1;
|
|
|
|
|
|
|
|
struct pollfd *pfd, *end;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
/* Wakeup at least once a minute. */
|
|
|
|
int timeout = 60000;
|
|
|
|
|
|
|
|
/* Pickup new loops */
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
if (!EMPTY_LIST(birdloop_pickup))
|
|
|
|
{
|
|
|
|
struct birdloop *loop = SKIP_BACK(struct birdloop, n, HEAD(birdloop_pickup));
|
|
|
|
rem_node(&loop->n);
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
add_tail(&thr->loops, &loop->n);
|
|
|
|
|
|
|
|
birdloop_enter(loop);
|
|
|
|
loop->thread = thr;
|
|
|
|
if (!EMPTY_LIST(loop->sock_list))
|
|
|
|
refresh_sockets = 1;
|
|
|
|
birdloop_leave(loop);
|
|
|
|
|
|
|
|
/* If there are more loops to be picked up, wakeup the next thread */
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
rem_node(&thr->n);
|
|
|
|
add_tail(&bird_thread_pickup, &thr->n);
|
|
|
|
|
|
|
|
if (!EMPTY_LIST(birdloop_pickup))
|
|
|
|
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(bird_thread_pickup)));
|
|
|
|
}
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
struct birdloop *loop; node *nn;
|
|
|
|
WALK_LIST2(loop, nn, thr->loops, n)
|
|
|
|
{
|
|
|
|
birdloop_enter(loop);
|
|
|
|
u64 after_enter = ns_now();
|
|
|
|
|
|
|
|
timer *t;
|
|
|
|
|
|
|
|
times_update();
|
|
|
|
timers_fire(&loop->time, 0);
|
|
|
|
int again = birdloop_process_flags(loop) + ev_run_list(&loop->event_list);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (loop->n.next->next)
|
|
|
|
__builtin_prefetch(SKIP_BACK(struct birdloop, n, loop->n.next)->time.domain);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (again)
|
|
|
|
timeout = MIN(0, timeout);
|
|
|
|
else if (t = timers_first(&loop->time))
|
|
|
|
timeout = MIN(((tm_remains(t) TO_MS) + 1), timeout);
|
|
|
|
|
|
|
|
u64 before_leave = ns_now();
|
|
|
|
loop->total_time_spent_ns += (before_leave - after_enter);
|
|
|
|
birdloop_leave(loop);
|
|
|
|
|
|
|
|
ev_run_list(&thr->priority_events);
|
|
|
|
}
|
|
|
|
|
|
|
|
refresh_sockets += atomic_exchange_explicit(&thr->poll_changed, 0, memory_order_acq_rel);
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
if (!refresh_sockets && ((timeout < 0) || (timeout > 5000)))
|
|
|
|
flush_local_pages();
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
while (refresh_sockets)
|
|
|
|
{
|
|
|
|
sock_retry:;
|
|
|
|
end = (pfd = thr->pfd) + thr->pfd_max;
|
|
|
|
|
|
|
|
/* Add internal wakeup fd */
|
|
|
|
pipe_pollin(&thr->wakeup, pfd);
|
|
|
|
pfd++;
|
|
|
|
|
|
|
|
WALK_LIST2(loop, nn, thr->loops, n)
|
|
|
|
{
|
|
|
|
birdloop_enter(loop);
|
|
|
|
pfd = sockets_prepare(loop, pfd, end);
|
|
|
|
birdloop_leave(loop);
|
|
|
|
|
|
|
|
if (!pfd)
|
|
|
|
{
|
|
|
|
mb_free(thr->pfd);
|
|
|
|
thr->pfd = mb_alloc(thr->pool, sizeof(struct pollfd) * (thr->pfd_max *= 2));
|
|
|
|
goto sock_retry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
refresh_sockets = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
poll_retry:;
|
|
|
|
int rv = poll(thr->pfd, pfd - thr->pfd, timeout);
|
|
|
|
if (rv < 0)
|
|
|
|
{
|
|
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
|
|
goto poll_retry;
|
|
|
|
bug("poll in %p: %m", thr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drain wakeup fd */
|
|
|
|
if (thr->pfd[0].revents & POLLIN)
|
|
|
|
{
|
|
|
|
ASSERT_DIE(rv > 0);
|
|
|
|
rv--;
|
|
|
|
wakeup_drain(thr);
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_exchange_explicit(&thr->ping_sent, 0, memory_order_acq_rel);
|
|
|
|
|
|
|
|
if (!rv && !atomic_exchange_explicit(&thr->run_cleanup, 0, memory_order_acq_rel))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Process stops and regular sockets */
|
|
|
|
node *nxt;
|
|
|
|
WALK_LIST2_DELSAFE(loop, nn, nxt, thr->loops, n)
|
|
|
|
{
|
|
|
|
birdloop_enter(loop);
|
|
|
|
|
|
|
|
if (loop->stopped)
|
|
|
|
{
|
|
|
|
/* Flush remaining events */
|
|
|
|
ASSERT_DIE(!ev_run_list(&loop->event_list));
|
|
|
|
|
|
|
|
/* Drop timers */
|
|
|
|
timer *t;
|
|
|
|
while (t = timers_first(&loop->time))
|
|
|
|
tm_stop(t);
|
|
|
|
|
|
|
|
/* No sockets allowed */
|
|
|
|
ASSERT_DIE(EMPTY_LIST(loop->sock_list));
|
|
|
|
|
|
|
|
/* Declare loop stopped */
|
|
|
|
rem_node(&loop->n);
|
|
|
|
birdloop_leave(loop);
|
|
|
|
loop->stopped(loop->stop_data);
|
|
|
|
|
|
|
|
/* Birdloop already left */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (rv)
|
|
|
|
sockets_fire(loop);
|
|
|
|
|
|
|
|
birdloop_leave(loop);
|
|
|
|
}
|
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-01-19 09:49:47 +00:00
|
|
|
bird_thread_cleanup(void *_thr)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2023-01-19 09:49:47 +00:00
|
|
|
struct bird_thread *thr = _thr;
|
|
|
|
ASSERT_DIE(birdloop_inside(&main_birdloop));
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
/* Thread attributes no longer needed */
|
|
|
|
pthread_attr_destroy(&thr->thread_attr);
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
/* Free all remaining memory */
|
|
|
|
rfree(thr->pool);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
static struct bird_thread *
|
|
|
|
bird_thread_start(void)
|
|
|
|
{
|
|
|
|
ASSERT_DIE(birdloop_inside(&main_birdloop));
|
|
|
|
|
|
|
|
pool *p = rp_new(&root_pool, "Thread");
|
|
|
|
|
|
|
|
struct bird_thread *thr = mb_allocz(p, sizeof(*thr));
|
|
|
|
thr->pool = p;
|
|
|
|
thr->pfd = mb_alloc(p, sizeof(struct pollfd) * (thr->pfd_max = 16));
|
|
|
|
thr->cleanup_event = (event) { .hook = bird_thread_cleanup, .data = thr, };
|
|
|
|
|
|
|
|
atomic_store_explicit(&thr->ping_sent, 0, memory_order_relaxed);
|
|
|
|
|
|
|
|
wakeup_init(thr);
|
|
|
|
ev_init_list(&thr->priority_events, NULL, "Thread direct event list");
|
|
|
|
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
add_tail(&bird_thread_pickup, &thr->n);
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
int e = 0;
|
|
|
|
|
|
|
|
if (e = pthread_attr_init(&thr->thread_attr))
|
|
|
|
die("pthread_attr_init() failed: %M", e);
|
|
|
|
|
|
|
|
/* We don't have to worry about thread stack size so much.
|
|
|
|
if (e = pthread_attr_setstacksize(&thr->thread_attr, THREAD_STACK_SIZE))
|
|
|
|
die("pthread_attr_setstacksize(%u) failed: %M", THREAD_STACK_SIZE, e);
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (e = pthread_attr_setdetachstate(&thr->thread_attr, PTHREAD_CREATE_DETACHED))
|
|
|
|
die("pthread_attr_setdetachstate(PTHREAD_CREATE_DETACHED) failed: %M", e);
|
|
|
|
|
|
|
|
if (e = pthread_create(&thr->thread_id, &thr->thread_attr, bird_thread_main, thr))
|
|
|
|
die("pthread_create() failed: %M", e);
|
|
|
|
|
|
|
|
return thr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct birdloop *thread_dropper;
|
|
|
|
static event *thread_dropper_event;
|
|
|
|
static uint thread_dropper_goal;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
|
|
|
static void
|
2023-01-19 09:49:47 +00:00
|
|
|
bird_thread_shutdown(void * _ UNUSED)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2023-01-19 09:49:47 +00:00
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
int dif = list_length(&bird_thread_pickup) - thread_dropper_goal;
|
|
|
|
struct birdloop *tdl_stop = NULL;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
if (dif > 0)
|
|
|
|
ev_send_loop(thread_dropper, thread_dropper_event);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tdl_stop = thread_dropper;
|
|
|
|
thread_dropper = NULL;
|
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
log(L_INFO "Thread pickup size differs from dropper goal by %d%s", dif, tdl_stop ? ", stopping" : "");
|
|
|
|
|
|
|
|
if (tdl_stop)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2023-01-19 09:49:47 +00:00
|
|
|
birdloop_stop_self(tdl_stop, NULL, NULL);
|
|
|
|
return;
|
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
struct bird_thread *thr = this_thread;
|
|
|
|
|
|
|
|
/* Leave the thread-picker list to get no more loops */
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
rem_node(&thr->n);
|
|
|
|
|
|
|
|
/* Drop loops including the thread dropper itself */
|
|
|
|
while (!EMPTY_LIST(thr->loops))
|
|
|
|
{
|
|
|
|
/* Remove loop from this thread's list */
|
|
|
|
struct birdloop *loop = HEAD(thr->loops);
|
|
|
|
rem_node(&loop->n);
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
/* Unset loop's thread */
|
|
|
|
if (birdloop_inside(loop))
|
|
|
|
loop->thread = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
birdloop_enter(loop);
|
|
|
|
loop->thread = NULL;
|
|
|
|
birdloop_leave(loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put loop into pickup list */
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
add_tail(&birdloop_pickup, &loop->n);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Let others know about new loops */
|
|
|
|
if (!EMPTY_LIST(birdloop_pickup))
|
|
|
|
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(bird_thread_pickup)));
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
/* Leave the thread-dropper loop as we aren't going to return. */
|
|
|
|
birdloop_leave(thread_dropper);
|
|
|
|
|
|
|
|
/* Local pages not needed anymore */
|
|
|
|
flush_local_pages();
|
|
|
|
|
|
|
|
/* Unregister from RCU */
|
|
|
|
rcu_thread_stop(&thr->rcu);
|
|
|
|
|
|
|
|
/* Request thread cleanup from main loop */
|
|
|
|
ev_send_loop(&main_birdloop, &thr->cleanup_event);
|
|
|
|
|
|
|
|
/* Exit! */
|
|
|
|
pthread_exit(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
bird_thread_commit(struct config *new, struct config *old UNUSED)
|
|
|
|
{
|
|
|
|
ASSERT_DIE(birdloop_inside(&main_birdloop));
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
if (new->shutdown)
|
|
|
|
return;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
if (!new->thread_count)
|
|
|
|
new->thread_count = 1;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
int dif = list_length(&bird_thread_pickup) - (thread_dropper_goal = new->thread_count);
|
|
|
|
_Bool thread_dropper_running = !!thread_dropper;
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
if (dif < 0)
|
2021-06-19 18:50:18 +00:00
|
|
|
{
|
2023-01-19 09:49:47 +00:00
|
|
|
bird_thread_start();
|
|
|
|
continue;
|
2021-06-19 18:50:18 +00:00
|
|
|
}
|
2023-01-19 09:49:47 +00:00
|
|
|
|
|
|
|
if ((dif > 0) && !thread_dropper_running)
|
|
|
|
{
|
|
|
|
struct birdloop *tdl = birdloop_new(&root_pool, DOMAIN_ORDER(control), "Thread dropper");
|
|
|
|
event *tde = ev_new_init(tdl->pool, bird_thread_shutdown, NULL);
|
|
|
|
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
thread_dropper = tdl;
|
|
|
|
thread_dropper_event = tde;
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
ev_send_loop(thread_dropper, thread_dropper_event);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DEFINE_DOMAIN(control);
|
|
|
|
|
|
|
|
struct bird_thread_show_data {
|
|
|
|
cli *cli;
|
|
|
|
pool *pool;
|
|
|
|
DOMAIN(control) lock;
|
|
|
|
uint total;
|
|
|
|
uint done;
|
|
|
|
u8 show_loops;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
bird_thread_show_cli_cont(struct cli *c UNUSED)
|
|
|
|
{
|
|
|
|
/* Explicitly do nothing to prevent CLI from trying to parse another command. */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bird_thread_show_cli_cleanup(struct cli *c UNUSED)
|
|
|
|
{
|
|
|
|
return 1; /* Defer the cleanup until the writeout is finished. */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bird_thread_show(void *data)
|
|
|
|
{
|
|
|
|
struct bird_thread_show_data *tsd = data;
|
|
|
|
|
|
|
|
LOCK_DOMAIN(control, tsd->lock);
|
|
|
|
if (tsd->show_loops)
|
|
|
|
cli_printf(tsd->cli, -1026, "Thread %p", this_thread);
|
|
|
|
|
|
|
|
u64 total_time_ns = 0;
|
|
|
|
struct birdloop *loop;
|
|
|
|
WALK_LIST(loop, this_thread->loops)
|
|
|
|
{
|
|
|
|
if (tsd->show_loops)
|
|
|
|
cli_printf(tsd->cli, -1026, " Loop %s time: %t", domain_name(loop->time.domain), loop->total_time_spent_ns NS);
|
|
|
|
total_time_ns += loop->total_time_spent_ns;
|
|
|
|
}
|
|
|
|
|
|
|
|
tsd->done++;
|
|
|
|
int last = (tsd->done == tsd->total);
|
|
|
|
|
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
tsd->cli->cont = NULL;
|
|
|
|
tsd->cli->cleanup = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tsd->show_loops)
|
|
|
|
cli_printf(tsd->cli, (last ? 1 : -1) * 1026, " Total time: %t", total_time_ns NS);
|
|
|
|
else
|
|
|
|
cli_printf(tsd->cli, (last ? 1 : -1) * 1026, "Thread %p time %t", this_thread, total_time_ns NS);
|
|
|
|
|
|
|
|
UNLOCK_DOMAIN(control, tsd->lock);
|
|
|
|
|
|
|
|
if (last)
|
|
|
|
{
|
|
|
|
the_bird_lock();
|
|
|
|
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
if (!EMPTY_LIST(birdloop_pickup))
|
|
|
|
if (tsd->show_loops)
|
|
|
|
{
|
|
|
|
cli_printf(tsd->cli, -1026, "Unassigned loops");
|
|
|
|
WALK_LIST(loop, birdloop_pickup)
|
|
|
|
cli_printf(tsd->cli, -1026, " Loop %s time: %t", domain_name(loop->time.domain), loop->total_time_spent_ns NS);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uint count = 0;
|
|
|
|
u64 total_time_ns = 0;
|
|
|
|
WALK_LIST(loop, birdloop_pickup)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
total_time_ns += loop->total_time_spent_ns;
|
|
|
|
}
|
|
|
|
cli_printf(tsd->cli, -1026, "Unassigned loops: %d, total time %t", count, total_time_ns NS);
|
|
|
|
}
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
cli_write_trigger(tsd->cli);
|
|
|
|
DOMAIN_FREE(control, tsd->lock);
|
|
|
|
rfree(tsd->pool);
|
|
|
|
|
|
|
|
the_bird_unlock();
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
void
|
|
|
|
cmd_show_threads(int show_loops)
|
|
|
|
{
|
|
|
|
pool *p = rp_new(&root_pool, "Show Threads");
|
|
|
|
|
|
|
|
struct bird_thread_show_data *tsd = mb_allocz(p, sizeof(struct bird_thread_show_data));
|
|
|
|
tsd->lock = DOMAIN_NEW(control, "Show Threads");
|
|
|
|
tsd->cli = this_cli;
|
|
|
|
tsd->pool = p;
|
|
|
|
tsd->show_loops = show_loops;
|
|
|
|
|
|
|
|
this_cli->cont = bird_thread_show_cli_cont;
|
|
|
|
this_cli->cleanup = bird_thread_show_cli_cleanup;
|
|
|
|
|
|
|
|
LOCK_DOMAIN(control, tsd->lock);
|
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
|
|
|
|
struct bird_thread *thr;
|
|
|
|
WALK_LIST(thr, bird_thread_pickup)
|
|
|
|
{
|
|
|
|
tsd->total++;
|
|
|
|
ev_send(&thr->priority_events, ev_new_init(p, bird_thread_show, tsd));
|
|
|
|
wakeup_do_kick(thr);
|
|
|
|
}
|
|
|
|
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
UNLOCK_DOMAIN(control, tsd->lock);
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:33:48 +00:00
|
|
|
/*
|
|
|
|
* Birdloop
|
|
|
|
*/
|
2013-09-16 21:57:40 +00:00
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
static struct bird_thread main_thread;
|
|
|
|
struct birdloop main_birdloop = { .thread = &main_thread, };
|
2021-06-19 18:50:18 +00:00
|
|
|
|
|
|
|
static void birdloop_enter_locked(struct birdloop *loop);
|
|
|
|
|
|
|
|
void
|
|
|
|
birdloop_init(void)
|
|
|
|
{
|
2023-01-19 09:49:47 +00:00
|
|
|
ns_init();
|
|
|
|
|
|
|
|
birdloop_domain = DOMAIN_NEW(resource, "Loop Pickup");
|
|
|
|
init_list(&birdloop_pickup);
|
|
|
|
init_list(&bird_thread_pickup);
|
|
|
|
|
|
|
|
wakeup_init(main_birdloop.thread);
|
2021-06-19 18:50:18 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-09-10 10:09:36 +00:00
|
|
|
struct birdloop *
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_new(pool *pp, uint order, const char *name)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2021-06-19 18:50:18 +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));
|
2013-09-16 21:57:40 +00:00
|
|
|
loop->pool = p;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
loop->time.domain = dg;
|
|
|
|
loop->time.loop = loop;
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_enter(loop);
|
2013-09-10 10:09:36 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
ev_init_list(&loop->event_list, loop, name);
|
2017-05-30 17:12:35 +00:00
|
|
|
timers_init(&loop->time, p);
|
2013-09-10 10:09:36 +00:00
|
|
|
sockets_init(loop);
|
|
|
|
|
2023-01-19 09:49:47 +00:00
|
|
|
LOCK_DOMAIN(resource, birdloop_domain);
|
|
|
|
add_tail(&birdloop_pickup, &loop->n);
|
|
|
|
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(bird_thread_pickup)));
|
|
|
|
UNLOCK_DOMAIN(resource, birdloop_domain);
|
2021-06-19 18:50:18 +00:00
|
|
|
|
|
|
|
birdloop_leave(loop);
|
|
|
|
|
2013-10-05 18:12:28 +00:00
|
|
|
return loop;
|
|
|
|
}
|
2013-09-16 21:57:40 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
static void
|
|
|
|
birdloop_do_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
|
|
|
|
{
|
|
|
|
loop->stopped = stopped;
|
|
|
|
loop->stop_data = data;
|
2023-01-19 09:49:47 +00:00
|
|
|
if (loop->thread)
|
|
|
|
{
|
|
|
|
atomic_store_explicit(&loop->thread->run_cleanup, 1, memory_order_release);
|
|
|
|
wakeup_do_kick(loop->thread);
|
|
|
|
}
|
2021-06-19 18:50:18 +00:00
|
|
|
}
|
|
|
|
|
2013-10-05 18:12:28 +00:00
|
|
|
void
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
|
2013-10-05 18:12:28 +00:00
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
DG_LOCK(loop->time.domain);
|
|
|
|
birdloop_do_stop(loop, stopped, data);
|
|
|
|
DG_UNLOCK(loop->time.domain);
|
2013-10-05 18:12:28 +00:00
|
|
|
}
|
2013-09-16 21:57:40 +00:00
|
|
|
|
2013-10-05 18:12:28 +00:00
|
|
|
void
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_stop_self(struct birdloop *loop, void (*stopped)(void *data), void *data)
|
2013-10-05 18:12:28 +00:00
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
ASSERT_DIE(loop == birdloop_current);
|
|
|
|
ASSERT_DIE(DG_IS_LOCKED(loop->time.domain));
|
2013-10-05 18:12:28 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_do_stop(loop, stopped, data);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:33:48 +00:00
|
|
|
void
|
|
|
|
birdloop_free(struct birdloop *loop)
|
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
ASSERT_DIE(loop->links == 0);
|
2023-01-19 09:49:47 +00:00
|
|
|
ASSERT_DIE(birdloop_in_this_thread(loop));
|
2022-07-28 17:49:03 +00:00
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
domain_free(loop->time.domain);
|
2013-11-19 21:33:48 +00:00
|
|
|
rfree(loop->pool);
|
|
|
|
}
|
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
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-10-05 18:12:28 +00:00
|
|
|
|
2013-09-10 10:09:36 +00:00
|
|
|
void
|
|
|
|
birdloop_enter(struct birdloop *loop)
|
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
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);
|
|
|
|
|
2022-07-28 17:49:03 +00:00
|
|
|
/* Send pending pings */
|
|
|
|
if (loop->ping_pending)
|
|
|
|
{
|
|
|
|
loop->ping_pending = 0;
|
|
|
|
birdloop_do_ping(loop);
|
|
|
|
}
|
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
/* Restore the old context */
|
|
|
|
birdloop_current = loop->prev_loop;
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
birdloop_leave(struct birdloop *loop)
|
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
birdloop_leave_locked(loop);
|
|
|
|
DG_UNLOCK(loop->time.domain);
|
2013-09-10 10:09:36 +00:00
|
|
|
}
|
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
void
|
|
|
|
birdloop_mask_wakeups(struct birdloop *loop)
|
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
ASSERT_DIE(birdloop_wakeup_masked == NULL);
|
|
|
|
birdloop_wakeup_masked = loop;
|
2013-09-16 21:57:40 +00:00
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
|
|
|
void
|
2013-09-16 21:57:40 +00:00
|
|
|
birdloop_unmask_wakeups(struct birdloop *loop)
|
2013-09-10 10:09:36 +00:00
|
|
|
{
|
2021-06-19 18:50:18 +00:00
|
|
|
ASSERT_DIE(birdloop_wakeup_masked == loop);
|
|
|
|
birdloop_wakeup_masked = NULL;
|
|
|
|
if (birdloop_wakeup_masked_count)
|
2023-01-19 09:49:47 +00:00
|
|
|
wakeup_do_kick(loop->thread);
|
2021-06-19 18:50:18 +00:00
|
|
|
|
|
|
|
birdloop_wakeup_masked_count = 0;
|
2013-09-16 21:57:40 +00:00
|
|
|
}
|
|
|
|
|
2021-06-19 18:50:18 +00:00
|
|
|
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--;
|
|
|
|
}
|
|
|
|
|
2021-11-12 21:58:40 +00:00
|
|
|
void
|
|
|
|
birdloop_yield(void)
|
|
|
|
{
|
|
|
|
usleep(100);
|
|
|
|
}
|