Commit 94eb0858 authored by Maria Matejka's avatar Maria Matejka
Browse files

Converting the former BFD loop to a universal IO loop and protocol loop.

There is a simple universal IO loop, taking care of events, timers and
sockets. Primarily, one instance of a protocol should use exactly one IO
loop to do all its work, as is now done in BFD.

Contrary to previous versions, the loop is now launched and cleaned by
the nest/proto.c code, allowing for a protocol to just request its own
loop by setting the loop's lock order in config higher than the_bird.

It is not supported nor checked if any protocol changed the requested
lock order in reconfigure. No protocol should do it at all.
parent c84ed603
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ int
main(int argc, char *argv[])
{
  bt_init(argc, argv);

  bt_bird_init();

  bt_assert_hook = bt_assert_filter;
+1 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ static inline int u64_cmp(u64 i1, u64 i2)
/* Macros for gcc attributes */

#define NORET __attribute__((noreturn))
#define USE_RESULT __atribute__((warn_unused_result))
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
+4 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
 *	BIRD Coroutines
 *
 *	(c) 2017 Martin Mares <mj@ucw.cz>
 *	(c) 2020 Maria Matejka <mq@jmq.cz>
 *	(c) 2020-2021 Maria Matejka <mq@jmq.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */
@@ -22,5 +22,8 @@ struct coroutine;
 */
struct coroutine *coro_run(pool *, void (*entry)(void *), void *data);

/* Get self. */
extern _Thread_local struct coroutine *this_coro;


#endif
+79 −52
Original line number Diff line number Diff line
@@ -19,8 +19,14 @@
 * events in them and explicitly ask to run them.
 */

#undef LOCAL_DEBUG

#include "nest/bird.h"
#include "lib/event.h"
#include "lib/locking.h"
#include "lib/io-loop.h"

extern _Thread_local struct coroutine *this_coro;

event_list global_event_list;
event_list global_work_list;
@@ -28,11 +34,16 @@ event_list global_work_list;
inline void
ev_postpone(event *e)
{
  event_list *el = e->list;
  if (!el)
    return;

  ASSERT_DIE(birdloop_inside(el->loop));

  LOCK_DOMAIN(event, el->lock);
  if (ev_active(e))
    {
    rem_node(&e->n);
      e->n.next = NULL;
    }
  UNLOCK_DOMAIN(event, el->lock);
}

static void
@@ -95,40 +106,25 @@ ev_run(event *e)
 * list @l which can be run by calling ev_run_list().
 */
inline void
ev_enqueue(event_list *l, event *e)
ev_send(event_list *l, event *e)
{
  ev_postpone(e);
  add_tail(l, &e->n);
}
  DBG("ev_send(%p, %p)\n", l, e);
  ASSERT_DIE(e->hook);
  ASSERT_DIE(!e->list || (e->list == l) || (e->list->loop == l->loop));

/**
 * ev_schedule - schedule an event
 * @e: an event
 *
 * This function schedules an event by enqueueing it to a system-wide
 * event list which is run by the platform dependent code whenever
 * appropriate.
 */
void
ev_schedule(event *e)
  e->list = l;

  LOCK_DOMAIN(event, l->lock);
  if (enlisted(&e->n))
  {
  ev_enqueue(&global_event_list, e);
    UNLOCK_DOMAIN(event, l->lock);
    return;
  }

/**
 * ev_schedule_work - schedule a work-event.
 * @e: an event
 *
 * This function schedules an event by enqueueing it to a system-wide work-event
 * list which is run by the platform dependent code whenever appropriate. This
 * is designated for work-events instead of regular events. They are executed
 * less often in order to not clog I/O loop.
 */
void
ev_schedule_work(event *e)
{
  if (!ev_active(e))
    add_tail(&global_work_list, &e->n);
  add_tail(&l->events, &e->n);
  UNLOCK_DOMAIN(event, l->lock);

  birdloop_ping(l->loop);
}

void io_log_event(void *hook, void *data);
@@ -142,35 +138,64 @@ void io_log_event(void *hook, void *data);
int
ev_run_list(event_list *l)
{
  const _Bool legacy = LEGACY_EVENT_LIST(l);

  if (legacy)
    ASSERT_THE_BIRD_LOCKED;

  node *n;
  list tmp_list;

  list tmp_list;
  init_list(&tmp_list);
  add_tail_list(&tmp_list, l);
  init_list(l);

  /* Move the event list contents to a local list to avoid executing repeatedly added events */
  LOCK_DOMAIN(event, l->lock);
  add_tail_list(&tmp_list, &l->events);
  init_list(&l->events);
  UNLOCK_DOMAIN(event, l->lock);

  WALK_LIST_FIRST(n, tmp_list)
    {
      event *e = SKIP_BACK(event, n, n);

      /* This is ugly hack, we want to log just events executed from the main I/O loop */
      if ((l == &global_event_list) || (l == &global_work_list))
      if (legacy)
      {
	/* The legacy way of event execution */
	io_log_event(e->hook, e->data);

      ev_run(e);
	ev_postpone(e);
	e->hook(e->data);
      }
      else
      {
	// io_log_event(e->hook, e->data); /* TODO: add support for event logging in other io loops */
	ASSERT_DIE(e->list == l);
	LOCK_DOMAIN(event, l->lock);
	rem_node(&e->n);
	UNLOCK_DOMAIN(event, l->lock);
	e->hook(e->data);
      }
    }

  return !EMPTY_LIST(*l);
  LOCK_DOMAIN(event, l->lock);
  int repeat = ! EMPTY_LIST(l->events);
  UNLOCK_DOMAIN(event, l->lock);
  return repeat;
}

int
ev_run_list_limited(event_list *l, uint limit)
{
  ASSERT_DIE(LEGACY_EVENT_LIST(l));
  ASSERT_THE_BIRD_LOCKED;

  node *n;
  list tmp_list;

  LOCK_DOMAIN(event, l->lock);
  init_list(&tmp_list);
  add_tail_list(&tmp_list, l);
  init_list(l);
  add_tail_list(&tmp_list, &l->events);
  init_list(&l->events);
  UNLOCK_DOMAIN(event, l->lock);

  WALK_LIST_FIRST(n, tmp_list)
    {
@@ -179,21 +204,23 @@ ev_run_list_limited(event_list *l, uint limit)
      if (!limit)
	break;

      /* This is ugly hack, we want to log just events executed from the main I/O loop */
      if ((l == &global_event_list) || (l == &global_work_list))
      io_log_event(e->hook, e->data);

      ev_run(e);
      limit--;
    }

  LOCK_DOMAIN(event, l->lock);
  if (!EMPTY_LIST(tmp_list))
  {
    /* Attach new items after the unprocessed old items */
    add_tail_list(&tmp_list, l);
    init_list(l);
    add_tail_list(l, &tmp_list);
    add_tail_list(&tmp_list, &l->events);
    init_list(&l->events);
    add_tail_list(&l->events, &tmp_list);
  }

  return !EMPTY_LIST(*l);
  int repeat = ! EMPTY_LIST(l->events);
  UNLOCK_DOMAIN(event, l->lock);

  return repeat;
}
+35 −6
Original line number Diff line number Diff line
@@ -10,33 +10,62 @@
#define _BIRD_EVENT_H_

#include "lib/resource.h"
#include "lib/locking.h"

#include <stdatomic.h>

DEFINE_DOMAIN(event);

typedef struct event {
  resource r;
  void (*hook)(void *);
  void *data;
  node n;				/* Internal link */
  struct event_list *list;		/* List where this event is put in */
} event;

typedef list event_list;
typedef struct event_list {
  list events;
  pool *pool;
  struct birdloop *loop;
  DOMAIN(event) lock;
} event_list;

extern event_list global_event_list;
extern event_list global_work_list;

event *ev_new(pool *);
void ev_run(event *);
#define ev_init_list(el) init_list(el)
void ev_enqueue(event_list *, event *);
void ev_schedule(event *);
void ev_schedule_work(event *);

static inline void ev_init_list(event_list *el, struct birdloop *loop, const char *name)
{
  init_list(&el->events);
  el->loop = loop;
  el->lock = DOMAIN_NEW(event, name);
}

void ev_send(event_list *, event *);
#define ev_send_loop(l, e) ev_send(birdloop_event_list((l)), (e))

#define ev_schedule(e) ({ ASSERT_THE_BIRD_LOCKED; if (!ev_active((e))) ev_send(&global_event_list, (e)); })
#define ev_schedule_work(e) ({ ASSERT_THE_BIRD_LOCKED; if (!ev_active((e))) ev_send(&global_work_list, (e)); })

void ev_postpone(event *);
int ev_run_list(event_list *);
int ev_run_list_limited(event_list *, uint);

#define LEGACY_EVENT_LIST(l)  (((l) == &global_event_list) || ((l) == &global_work_list))

_Bool birdloop_inside(struct birdloop *loop);

static inline int
ev_active(event *e)
{
  return e->n.next != NULL;
  if (e->list == NULL)
    return 0;

  ASSERT_DIE(birdloop_inside(e->list->loop));
  return enlisted(&e->n);
}

static inline event*
Loading