diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/birdlib.h | 8 | ||||
-rw-r--r-- | lib/coro.h | 31 | ||||
-rw-r--r-- | lib/event.c | 193 | ||||
-rw-r--r-- | lib/event.h | 71 | ||||
-rw-r--r-- | lib/event_test.c | 7 | ||||
-rw-r--r-- | lib/flowspec_test.c | 11 | ||||
-rw-r--r-- | lib/hash.h | 6 | ||||
-rw-r--r-- | lib/hash_test.c | 6 | ||||
-rw-r--r-- | lib/io-loop.h | 57 | ||||
-rw-r--r-- | lib/lists.c | 24 | ||||
-rw-r--r-- | lib/lists.h | 13 | ||||
-rw-r--r-- | lib/locking.h | 66 | ||||
-rw-r--r-- | lib/mempool.c | 21 | ||||
-rw-r--r-- | lib/rcu.c | 79 | ||||
-rw-r--r-- | lib/rcu.h | 55 | ||||
-rw-r--r-- | lib/resource.c | 133 | ||||
-rw-r--r-- | lib/resource.h | 22 | ||||
-rw-r--r-- | lib/slab.c | 19 | ||||
-rw-r--r-- | lib/socket.h | 4 | ||||
-rw-r--r-- | lib/timer.c | 115 | ||||
-rw-r--r-- | lib/timer.h | 41 |
22 files changed, 781 insertions, 203 deletions
diff --git a/lib/Makefile b/lib/Makefile index 4378a7bd..98c5db3c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,4 +1,4 @@ -src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c +src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c rcu.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c obj := $(src-o-files) $(all-daemon) diff --git a/lib/birdlib.h b/lib/birdlib.h index 431b7c0d..385bf75c 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -9,6 +9,7 @@ #ifndef _BIRD_BIRDLIB_H_ #define _BIRD_BIRDLIB_H_ +#include "sysdep/config.h" #include "lib/alloca.h" /* Ugly structure offset handling macros */ @@ -16,7 +17,7 @@ struct align_probe { char x; long int y; }; #define OFFSETOF(s, i) ((size_t) &((s *)0)->i) -#define SKIP_BACK(s, i, p) ((s *)((char *)p - OFFSETOF(s, i))) +#define SKIP_BACK(s, i, p) ({ s *_ptr = ((s *)((char *)p - OFFSETOF(s, i))); ASSERT_DIE(&_ptr->i == p); _ptr; }) #define BIRD_ALIGN(s, a) (((s)+a-1)&~(a-1)) #define CPU_STRUCT_ALIGN (sizeof(struct align_probe)) @@ -70,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__)))) @@ -77,10 +79,6 @@ static inline int u64_cmp(u64 i1, u64 i2) #define STATIC_ASSERT(EXP) _Static_assert(EXP, #EXP) #define STATIC_ASSERT_MSG(EXP,MSG) _Static_assert(EXP, MSG) -#ifndef HAVE_THREAD_LOCAL -#define _Thread_local -#endif - /* Microsecond time */ typedef s64 btime; diff --git a/lib/coro.h b/lib/coro.h new file mode 100644 index 00000000..b36f1d2c --- /dev/null +++ b/lib/coro.h @@ -0,0 +1,31 @@ +/* + * BIRD Coroutines + * + * (c) 2017 Martin Mares <mj@ucw.cz> + * (c) 2020-2021 Maria Matejka <mq@jmq.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_CORO_H_ +#define _BIRD_CORO_H_ + +#include "lib/resource.h" + +/* A completely opaque coroutine handle. */ +struct coroutine; + +/* Coroutines are independent threads bound to pools. + * You request a coroutine by calling coro_run(). + * It is forbidden to free a running coroutine from outside. + * The running coroutine must free itself by rfree() before returning. + */ +struct coroutine *coro_run(pool *, void (*entry)(void *), void *data); + +/* Get self. */ +extern _Thread_local struct coroutine *this_coro; + +/* Just wait for a little while. Not intended for general use; use events if possible. */ +void coro_yield(void); + +#endif diff --git a/lib/event.c b/lib/event.c index 273447e0..5031f314 100644 --- a/lib/event.c +++ b/lib/event.c @@ -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; - } + rem_node(&e->n); + UNLOCK_DOMAIN(event, el->lock); } static void @@ -95,40 +106,50 @@ 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) -{ - ev_enqueue(&global_event_list, e); -} + e->list = l; -/** - * 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); + struct event_cork *ec = e->cork; + + uint ping = 0; + + if (ec) + { + LOCK_DOMAIN(cork, ec->lock); + LOCK_DOMAIN(event, l->lock); + + if (!enlisted(&e->n)) + if (ec->count) + add_tail(&ec->events, &e->n); + else + { + add_tail(&l->events, &e->n); + ping = 1; + } + + UNLOCK_DOMAIN(event, l->lock); + UNLOCK_DOMAIN(cork, ec->lock); + } + else + { + LOCK_DOMAIN(event, l->lock); + + if (!enlisted(&e->n)) + { + add_tail(&l->events, &e->n); + ping = 1; + } + + UNLOCK_DOMAIN(event, l->lock); + } + + if (ping) + birdloop_ping(l->loop); } void io_log_event(void *hook, void *data); @@ -142,35 +163,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 +229,60 @@ 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); + 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); + } + + int repeat = ! EMPTY_LIST(l->events); + UNLOCK_DOMAIN(event, l->lock); + + return repeat; +} + +void ev_cork(struct event_cork *ec) +{ + LOCK_DOMAIN(cork, ec->lock); + ec->count++; + UNLOCK_DOMAIN(cork, ec->lock); +} + +void ev_uncork(struct event_cork *ec) +{ + LOCK_DOMAIN(cork, ec->lock); + + if (--ec->count) + { + UNLOCK_DOMAIN(cork, ec->lock); + return; } - return !EMPTY_LIST(*l); + node *n; + WALK_LIST_FIRST(n, ec->events) + { + event *e = SKIP_BACK(event, n, n); + event_list *el = e->list; + + rem_node(&e->n); + + LOCK_DOMAIN(event, el->lock); + add_tail(&el->events, &e->n); + UNLOCK_DOMAIN(event, el->lock); + + birdloop_ping(el->loop); + } + + UNLOCK_DOMAIN(cork, ec->lock); + + birdloop_ping(&main_birdloop); } diff --git a/lib/event.h b/lib/event.h index 5f3b78d8..cd85bf78 100644 --- a/lib/event.h +++ b/lib/event.h @@ -10,33 +10,92 @@ #define _BIRD_EVENT_H_ #include "lib/resource.h" +#include "lib/locking.h" + +#include <stdatomic.h> + +DEFINE_DOMAIN(event); +DEFINE_DOMAIN(cork); 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 */ + struct event_cork *cork; /* Event execution limiter */ + node cork_node; } event; -typedef list event_list; +typedef struct event_list { + list events; + pool *pool; + struct birdloop *loop; + DOMAIN(event) lock; +} event_list; + +struct event_cork { + DOMAIN(cork) lock; + u32 count; + list events; +}; 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); +} + +static inline void ev_init_cork(struct event_cork *ec, const char *name) +{ + init_list(&ec->events); + ec->lock = DOMAIN_NEW(cork, name); + ec->count = 0; +}; + +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)) + +void ev_cork(struct event_cork *); +void ev_uncork(struct event_cork *); + +static inline u32 ev_corked(struct event_cork *ec) +{ + if (!ec) + return 0; + + LOCK_DOMAIN(cork, ec->lock); + u32 out = ec->count; + UNLOCK_DOMAIN(cork, ec->lock); + return out; +} + +_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* diff --git a/lib/event_test.c b/lib/event_test.c index e1215bba..f8bb303b 100644 --- a/lib/event_test.c +++ b/lib/event_test.c @@ -48,14 +48,17 @@ init_event_check_points(void) event_check_points[i] = 0; } +void resource_sys_init(void); + static int t_ev_run_list(void) { int i; + resource_sys_init(); resource_init(); + birdloop_init(); olock_init(); - timer_init(); io_init(); rt_init(); if_init(); @@ -82,7 +85,9 @@ main(int argc, char *argv[]) { bt_init(argc, argv); + the_bird_lock(); bt_test_suite(t_ev_run_list, "Schedule and run 3 events in right order."); + the_bird_unlock(); return bt_exit_value(); } diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c index ed4afe51..518b8fc7 100644 --- a/lib/flowspec_test.c +++ b/lib/flowspec_test.c @@ -8,6 +8,7 @@ #include "test/birdtest.h" #include "lib/flowspec.h" +#include "lib/io-loop.h" #define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \ do \ @@ -446,8 +447,6 @@ t_validation6(void) static int t_builder4(void) { - resource_init(); - struct flow_builder *fb = flow_builder_init(&root_pool); linpool *lp = lp_new_default(&root_pool); @@ -529,7 +528,6 @@ t_builder6(void) { net_addr_ip6 ip; - resource_init(); linpool *lp = lp_new_default(&root_pool); struct flow_builder *fb = flow_builder_init(&root_pool); fb->ipv6 = 1; @@ -666,10 +664,16 @@ t_formatting6(void) return 1; } +void resource_sys_init(void); + int main(int argc, char *argv[]) { bt_init(argc, argv); + resource_sys_init(); + resource_init(); + the_bird_lock(); + birdloop_init(); bt_test_suite(t_read_length, "Testing get NLRI length"); bt_test_suite(t_write_length, "Testing set NLRI length"); @@ -685,5 +689,6 @@ main(int argc, char *argv[]) bt_test_suite(t_formatting4, "Formatting Flow Specification (IPv4) into text representation"); bt_test_suite(t_formatting6, "Formatting Flow Specification (IPv6) into text representation"); + the_bird_unlock(); return bt_exit_value(); } @@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s) *h = *h * multiplier + pp[i]; } +static inline void +mem_hash_mix_num(u64 *h, u64 val) +{ + mem_hash_mix(h, &val, sizeof(val)); +} + static inline uint mem_hash_value(u64 *h) { diff --git a/lib/hash_test.c b/lib/hash_test.c index 59beb7c0..7ef54662 100644 --- a/lib/hash_test.c +++ b/lib/hash_test.c @@ -9,7 +9,9 @@ #undef LOCAL_DEBUG #include "test/birdtest.h" +#include "test/bt-utils.h" +#include "lib/io-loop.h" #include "lib/hash.h" struct test_node { @@ -61,8 +63,7 @@ dump_nodes(void) static void init_hash_(uint order) { - resource_init(); - my_pool = rp_new(&root_pool, "Test pool"); + my_pool = rp_new(&root_pool, &main_birdloop, "Test pool"); HASH_INIT(hash, my_pool, order); @@ -290,6 +291,7 @@ int main(int argc, char *argv[]) { bt_init(argc, argv); + bt_bird_init(); bt_test_suite(t_insert_find, "HASH_INSERT and HASH_FIND"); bt_test_suite(t_insert_find_random, "HASH_INSERT pseudo-random keys and HASH_FIND"); diff --git a/lib/io-loop.h b/lib/io-loop.h new file mode 100644 index 00000000..386a31d5 --- /dev/null +++ b/lib/io-loop.h @@ -0,0 +1,57 @@ +/* + * BIRD -- I/O and event loop + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_IO_LOOP_H_ +#define _BIRD_IO_LOOP_H_ + +#include "nest/bird.h" +#include "lib/lists.h" +#include "lib/locking.h" +#include "lib/resource.h" +#include "lib/event.h" +#include "lib/socket.h" + +void sk_start(sock *s); +void sk_stop(sock *s); +void sk_reloop(sock *s, struct birdloop *loop); + +extern struct birdloop main_birdloop; + +/* Start a new birdloop owned by given pool and domain. + * The loop allocates its internal pool for local allocations + * which is freed when the loop itself is stopped. */ +struct birdloop *birdloop_new(pool *p, uint order, const char *name); + +/* Stop the loop. At the end, the @stopped callback is called with locked + * parent to finish cleanup. The loop then frees itself together with its pool. */ +void birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data); +void birdloop_stop_self(struct birdloop *loop, void (*stopped)(void *data), void *data); + +/* Get birdloop's event list */ +event_list *birdloop_event_list(struct birdloop *loop); + +/* Get birdloop's time heap */ +struct timeloop *birdloop_time_loop(struct birdloop *loop); + +/* Get birdloop's resource pool */ +pool *birdloop_pool(struct birdloop *loop); + +/* Enter and exit the birdloop */ +void birdloop_enter(struct birdloop *loop); +void birdloop_leave(struct birdloop *loop); + +_Bool birdloop_inside(struct birdloop *loop); + +void birdloop_mask_wakeups(struct birdloop *loop); +void birdloop_unmask_wakeups(struct birdloop *loop); + +void birdloop_link(struct birdloop *loop); +void birdloop_unlink(struct birdloop *loop); + +void birdloop_ping(struct birdloop *loop); + +void birdloop_init(void); +#endif /* _BIRD_IO_LOOP_H_ */ diff --git a/lib/lists.c b/lib/lists.c index 200576cf..dc2e4cbb 100644 --- a/lib/lists.c +++ b/lib/lists.c @@ -26,7 +26,7 @@ #define _BIRD_LISTS_C_ -#include "nest/bird.h" +#include "lib/birdlib.h" #include "lib/lists.h" LIST_INLINE int @@ -35,11 +35,12 @@ check_list(list *l, node *n) if (!l) { ASSERT_DIE(n); - ASSERT_DIE(n->prev); - do { n = n->prev; } while (n->prev); + node *nn = n; + while (nn->prev) + nn = nn->prev; - l = SKIP_BACK(list, head_node, n); + l = SKIP_BACK(list, head_node, nn); } int seen = 0; @@ -60,7 +61,7 @@ check_list(list *l, node *n) } ASSERT_DIE(cur == &(l->tail_node)); - ASSERT_DIE(!n || (seen == 1)); + ASSERT_DIE(!n || (seen == 1) || (n == &l->head_node) || (n == &l->tail_node)); return 1; } @@ -109,6 +110,15 @@ add_head(list *l, node *n) l->head = n; } +LIST_INLINE void +self_link(node *n) +{ + ASSUME(n->prev == NULL); + ASSUME(n->next == NULL); + + n->prev = n->next = n; +} + /** * insert_node - insert a node to a list * @n: a new list node @@ -120,7 +130,7 @@ add_head(list *l, node *n) LIST_INLINE void insert_node(node *n, node *after) { - EXPENSIVE_CHECK(check_list(l, after)); + EXPENSIVE_CHECK(check_list(NULL, after)); ASSUME(n->prev == NULL); ASSUME(n->next == NULL); @@ -141,7 +151,7 @@ insert_node(node *n, node *after) LIST_INLINE void rem_node(node *n) { - EXPENSIVE_CHECK(check_list(NULL, n)); + EXPENSIVE_CHECK((n == n->prev) && (n == n->next) || check_list(NULL, n)); node *z = n->prev; node *x = n->next; diff --git a/lib/lists.h b/lib/lists.h index 479f4ed1..dc49ec8a 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -68,6 +68,18 @@ typedef union list { /* In fact two overlayed nodes */ #define EMPTY_LIST(list) (!(list).head->next) +static inline _Bool +enlisted(node *n) +{ + switch ((!!n->next) + (!!n->prev)) + { + case 0: return 0; + case 2: return 1; + case 1: bug("Garbled event list node"); + } + + bug("Maths is broken. And you should see a new heaven and a new earth: for the first heaven and the first earth had been passed away."); +} #ifndef _BIRD_LISTS_C_ #define LIST_INLINE static inline @@ -78,6 +90,7 @@ typedef union list { /* In fact two overlayed nodes */ #define LIST_INLINE void add_tail(list *, node *); void add_head(list *, node *); +void self_link(node *); void rem_node(node *); void add_tail_list(list *, list *); void init_list(list *); diff --git a/lib/locking.h b/lib/locking.h new file mode 100644 index 00000000..1a8bdcd4 --- /dev/null +++ b/lib/locking.h @@ -0,0 +1,66 @@ +/* + * BIRD Library -- Locking + * + * (c) 2020--2021 Maria Matejka <mq@jmq.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_LOCKING_H_ +#define _BIRD_LOCKING_H_ + +struct domain_generic; + +/* Here define the global lock order; first to last. */ +struct lock_order { + struct domain_generic *the_bird; + struct domain_generic *proto; + struct domain_generic *rtable; + struct domain_generic *attrs; + struct domain_generic *cork; + struct domain_generic *event; + struct domain_generic *resource; +}; + +extern _Thread_local struct lock_order locking_stack; +extern _Thread_local struct domain_generic **last_locked; + +#define DOMAIN(type) struct domain__##type +#define DEFINE_DOMAIN(type) DOMAIN(type) { struct domain_generic *type; } +#define DOMAIN_ORDER(type) OFFSETOF(struct lock_order, type) + +#define DOMAIN_NEW(type, name) (DOMAIN(type)) { .type = domain_new(name, DOMAIN_ORDER(type)) } +struct domain_generic *domain_new(const char *name, uint order); + +#define DOMAIN_FREE(type, d) domain_free((d).type) +void domain_free(struct domain_generic *); + +#define DOMAIN_NULL(type) (DOMAIN(type)) {} + +#define LOCK_DOMAIN(type, d) do_lock(((d).type), &(locking_stack.type)) +#define UNLOCK_DOMAIN(type, d) do_unlock(((d).type), &(locking_stack.type)) + +#define DOMAIN_IS_LOCKED(type, d) (((d).type) == (locking_stack.type)) +#define DG_IS_LOCKED(d) ((d) == *(DG_LSP(d))) + +/* Internal for locking */ +void do_lock(struct domain_generic *dg, struct domain_generic **lsp); +void do_unlock(struct domain_generic *dg, struct domain_generic **lsp); + +uint dg_order(struct domain_generic *dg); + +#define DG_LSP(d) ((struct domain_generic **) (((void *) &locking_stack) + dg_order(d))) +#define DG_LOCK(d) do_lock(d, DG_LSP(d)) +#define DG_UNLOCK(d) do_unlock(d, DG_LSP(d)) + +/* Use with care. To be removed in near future. */ +DEFINE_DOMAIN(the_bird); +extern DOMAIN(the_bird) the_bird_domain; + +#define the_bird_lock() LOCK_DOMAIN(the_bird, the_bird_domain) +#define the_bird_unlock() UNLOCK_DOMAIN(the_bird, the_bird_domain) +#define the_bird_locked() DOMAIN_IS_LOCKED(the_bird, the_bird_domain) + +#define ASSERT_THE_BIRD_LOCKED ({ if (!the_bird_locked()) bug("The BIRD lock must be locked here: %s:%d", __FILE__, __LINE__); }) + +#endif diff --git a/lib/mempool.c b/lib/mempool.c index 90d7c774..6d157e5c 100644 --- a/lib/mempool.c +++ b/lib/mempool.c @@ -37,9 +37,10 @@ const int lp_chunk_size = sizeof(struct lp_chunk); struct linpool { resource r; byte *ptr, *end; + pool *p; struct lp_chunk *first, *current; /* Normal (reusable) chunks */ struct lp_chunk *first_large; /* Large chunks */ - uint chunk_size, threshold, total, total_large; + uint chunk_size, threshold, total:31, use_pages:1, total_large; }; static void lp_free(resource *); @@ -69,6 +70,13 @@ linpool *lp_new(pool *p, uint blk) { linpool *m = ralloc(p, &lp_class); + m->p = p; + if (!blk) + { + m->use_pages = 1; + blk = page_size - lp_chunk_size; + } + m->chunk_size = blk; m->threshold = 3*blk/4; return m; @@ -121,7 +129,11 @@ lp_alloc(linpool *m, uint size) else { /* Need to allocate a new chunk */ - c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size); + if (m->use_pages) + c = alloc_page(); + else + c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size); + m->total += m->chunk_size; c->next = NULL; c->size = m->chunk_size; @@ -258,7 +270,10 @@ lp_free(resource *r) for(d=m->first; d; d = c) { c = d->next; - xfree(d); + if (m->use_pages) + free_page(d); + else + xfree(d); } for(d=m->first_large; d; d = c) { diff --git a/lib/rcu.c b/lib/rcu.c new file mode 100644 index 00000000..69f3442f --- /dev/null +++ b/lib/rcu.c @@ -0,0 +1,79 @@ +/* + * BIRD Library -- Read-Copy-Update Basic Operations + * + * (c) 2021 Maria Matejka <mq@jmq.cz> + * (c) 2021 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + * Note: all the relevant patents shall be expired. + * + * Using the Supplementary Material for User-Level Implementations of Read-Copy-Update + * by Matthieu Desnoyers, Paul E. McKenney, Alan S. Stern, Michel R. Dagenais and Jonathan Walpole + * obtained from https://www.efficios.com/pub/rcu/urcu-supp-accepted.pdf + */ + +#include "lib/rcu.h" +#include "lib/coro.h" +#include "lib/locking.h" + +_Atomic uint rcu_gp_ctl = RCU_NEST_CNT; +_Thread_local struct rcu_coro *this_rcu_coro = NULL; + +static list rcu_coro_list; + +static struct rcu_coro main_rcu_coro; + +DEFINE_DOMAIN(resource); +static DOMAIN(resource) rcu_domain; + +static int +rcu_gp_ongoing(_Atomic uint *ctl) +{ + uint val = atomic_load(ctl); + return (val & RCU_NEST_CNT) && ((val ^ rcu_gp_ctl) & RCU_GP_PHASE); +} + +static void +update_counter_and_wait(void) +{ + atomic_fetch_xor(&rcu_gp_ctl, RCU_GP_PHASE); + struct rcu_coro *rc; + WALK_LIST(rc, rcu_coro_list) + while (rcu_gp_ongoing(&rc->ctl)) + coro_yield(); +} + +void +synchronize_rcu(void) +{ + LOCK_DOMAIN(resource, rcu_domain); + update_counter_and_wait(); + update_counter_and_wait(); + UNLOCK_DOMAIN(resource, rcu_domain); +} + +void +rcu_coro_start(struct rcu_coro *rc) +{ + LOCK_DOMAIN(resource, rcu_domain); + add_tail(&rcu_coro_list, &rc->n); + this_rcu_coro = rc; + UNLOCK_DOMAIN(resource, rcu_domain); +} + +void +rcu_coro_stop(struct rcu_coro *rc) +{ + LOCK_DOMAIN(resource, rcu_domain); + this_rcu_coro = NULL; + rem_node(&rc->n); + UNLOCK_DOMAIN(resource, rcu_domain); +} + +void +rcu_init(void) +{ + rcu_domain = DOMAIN_NEW(resource, "Read-Copy-Update"); + init_list(&rcu_coro_list); + rcu_coro_start(&main_rcu_coro); +} diff --git a/lib/rcu.h b/lib/rcu.h new file mode 100644 index 00000000..ac8fc9ce --- /dev/null +++ b/lib/rcu.h @@ -0,0 +1,55 @@ +/* + * BIRD Library -- Read-Copy-Update Basic Operations + * + * (c) 2021 Maria Matejka <mq@jmq.cz> + * (c) 2021 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + * Note: all the relevant patents shall be expired. + */ + +#ifndef _BIRD_RCU_H_ +#define _BIRD_RCU_H_ + +#include "lib/birdlib.h" +#include "lib/lists.h" +#include <stdatomic.h> + +#define RCU_GP_PHASE 0x100000 +#define RCU_NEST_MASK 0x0fffff +#define RCU_NEST_CNT 0x000001 + +extern _Atomic uint rcu_gp_ctl; + +struct rcu_coro { + node n; + _Atomic uint ctl; +}; + +extern _Thread_local struct rcu_coro *this_rcu_coro; + +static inline void rcu_read_lock(void) +{ + uint cmp = atomic_load_explicit(&this_rcu_coro->ctl, memory_order_acquire); + + if (cmp & RCU_NEST_MASK) + atomic_store_explicit(&this_rcu_coro->ctl, cmp + RCU_NEST_CNT, memory_order_relaxed); + else + atomic_store(&this_rcu_coro->ctl, atomic_load_explicit(&rcu_gp_ctl, memory_order_acquire)); +} + +static inline void rcu_read_unlock(void) +{ + atomic_fetch_sub(&this_rcu_coro->ctl, RCU_NEST_CNT); +} + +void synchronize_rcu(void); + +/* Registering and unregistering a coroutine. To be called from coroutine implementation */ +void rcu_coro_start(struct rcu_coro *); +void rcu_coro_stop(struct rcu_coro *); + +/* Run this from resource init */ +void rcu_init(void); + +#endif diff --git a/lib/resource.c b/lib/resource.c index 5d4c7780..9a4c7717 100644 --- a/lib/resource.c +++ b/lib/resource.c @@ -14,6 +14,8 @@ #include "nest/bird.h" #include "lib/resource.h" #include "lib/string.h" +#include "lib/rcu.h" +#include "lib/io-loop.h" /** * DOC: Resource pools @@ -29,12 +31,6 @@ * is freed upon shutdown of the module. */ -struct pool { - resource r; - list inside; - const char *name; -}; - static void pool_dump(resource *); static void pool_free(resource *); static resource *pool_lookup(resource *, unsigned long); @@ -56,26 +52,39 @@ static int indent; /** * rp_new - create a resource pool * @p: parent pool + * @l: loop to assign * @name: pool name (to be included in debugging dumps) * * rp_new() creates a new resource pool inside the specified * parent pool. */ pool * -rp_new(pool *p, const char *name) +rp_new(pool *p, struct birdloop *loop, const char *name) { + ASSERT_DIE(birdloop_inside(p->loop)); + ASSERT_DIE(birdloop_inside(loop)); + pool *z = ralloc(p, &pool_class); + z->loop = loop; z->name = name; init_list(&z->inside); return z; } +_Thread_local static pool *pool_parent = NULL; + static void pool_free(resource *P) { + ASSERT_DIE(pool_parent); + pool *p = (pool *) P; - resource *r, *rr; + ASSERT_DIE(birdloop_inside(p->loop)); + + pool *parent = pool_parent; + pool_parent = p; + resource *r, *rr; r = HEAD(p->inside); while (rr = (resource *) r->n.next) { @@ -83,14 +92,25 @@ pool_free(resource *P) xfree(r); r = rr; } + + pool_parent = parent; +} + +void +rp_free(pool *p, pool *parent) +{ + ASSERT_DIE(pool_parent == NULL); + pool_parent = parent; + rfree(p); + ASSERT_DIE(pool_parent == parent); + pool_parent = NULL; } static void -pool_dump(resource *P) +pool_dump_locked(pool *p) { - pool *p = (pool *) P; resource *r; - + debug("%s\n", p->name); indent += 3; WALK_LIST(r, p->inside) @@ -98,10 +118,47 @@ pool_dump(resource *P) indent -= 3; } -static struct resmem -pool_memsize(resource *P) +static void +pool_dump(resource *P) { pool *p = (pool *) P; + + if (p->loop != pool_parent->loop) + birdloop_enter(p->loop); + + pool *parent = pool_parent; + pool_parent = p; + + pool_dump_locked(p); + + pool_parent = parent; + + if (p->loop != pool_parent->loop) + birdloop_leave(p->loop); +} + +void +rp_dump(pool *p) +{ + int inside = birdloop_inside(p->loop); + if (!inside) + birdloop_enter(p->loop); + + ASSERT_DIE(pool_parent == NULL); + pool_parent = p; + + pool_dump_locked(p); + + ASSERT_DIE(pool_parent == p); + pool_parent = NULL; + + if (!inside) + birdloop_leave(p->loop); +} + +static struct resmem +pool_memsize_locked(pool *p) +{ resource *r; struct resmem sum = { .effective = 0, @@ -118,6 +175,46 @@ pool_memsize(resource *P) return sum; } +static struct resmem +pool_memsize(resource *P) +{ + pool *p = (pool *) P; + + pool *parent = pool_parent; + pool_parent = p; + + if (p->loop != parent->loop) + birdloop_enter(p->loop); + + struct resmem sum = pool_memsize_locked(p); + + if (p->loop != parent->loop) + birdloop_leave(p->loop); + + pool_parent = parent; + + return sum; +} + +struct resmem +rp_memsize(pool *p) +{ + int inside = birdloop_inside(p->loop); + if (!inside) + birdloop_enter(p->loop); + + ASSERT_DIE(pool_parent == NULL); + pool_parent = p; + struct resmem sum = pool_memsize_locked(p); + ASSERT_DIE(pool_parent == p); + pool_parent = NULL; + + if (!inside) + birdloop_leave(p->loop); + + return sum; +} + static resource * pool_lookup(resource *P, unsigned long a) { @@ -228,12 +325,15 @@ rmemsize(void *res) void * ralloc(pool *p, struct resclass *c) { + ASSERT_DIE(p); + ASSERT_DIE(birdloop_inside(p->loop)); + resource *r = xmalloc(c->size); bzero(r, c->size); r->class = c; - if (p) - add_tail(&p->inside, &r->n); + add_tail(&p->inside, &r->n); + return r; } @@ -270,6 +370,8 @@ rlookup(unsigned long a) void resource_init(void) { + rcu_init(); + root_pool.r.class = &pool_class; root_pool.name = "Root"; init_list(&root_pool.inside); @@ -440,7 +542,6 @@ mb_free(void *m) } - #define STEP_UP(x) ((x) + (x)/2 + 4) void diff --git a/lib/resource.h b/lib/resource.h index 9ec41ed8..b80ff8c6 100644 --- a/lib/resource.h +++ b/lib/resource.h @@ -40,10 +40,16 @@ struct resclass { /* Generic resource manipulation */ -typedef struct pool pool; +typedef struct pool { + resource r; + list inside; + struct pool_pages *pages; + struct birdloop *loop; + const char *name; +} pool; void resource_init(void); -pool *rp_new(pool *, const char *); /* Create new pool */ + void rfree(void *); /* Free single resource */ void rdump(void *); /* Dump to debug output */ struct resmem rmemsize(void *res); /* Return size of memory used by the resource */ @@ -52,6 +58,11 @@ void rmove(void *, pool *); /* Move to a different pool */ void *ralloc(pool *, struct resclass *); +pool *rp_new(pool *, struct birdloop *loop, const char *); /* Create new pool */ +void rp_free(pool *p, pool *parent); /* Free parent pool */ +struct resmem rp_memsize(pool *p); /* Return size of memory used by the pool */ +void rp_dump(pool *p); /* Dump pool to debug output */ + extern pool root_pool; /* Normal memory blocks */ @@ -82,7 +93,7 @@ void lp_restore(linpool *m, lp_state *p); /* Restore state */ extern const int lp_chunk_size; #define LP_GAS 1024 #define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size) -#define lp_new_default(p) lp_new(p, LP_GOOD_SIZE(LP_GAS*4)) +#define lp_new_default(p) lp_new(p, 0) /* Slabs */ @@ -100,11 +111,12 @@ void sl_free(slab *, void *); void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size); +extern long page_size; + /* Allocator of whole pages; for use in slabs and other high-level allocators. */ -u64 get_page_size(void); void *alloc_page(void); void free_page(void *); -extern uint pages_kept; +#define PAGE_HEAD(x) ((void *) (((intptr_t) (x)) & ~(page_size-1))) #ifdef HAVE_LIBDMALLOC /* @@ -155,6 +155,7 @@ slab_memsize(resource *r) struct slab { resource r; + pool *p; uint obj_size, head_size, head_bitfield_len; uint objs_per_slab, num_empty_heads, data_size; list empty_heads, partial_heads, full_heads; @@ -180,7 +181,7 @@ struct sl_alignment { /* Magic structure for testing of alignment */ int x[0]; }; -#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(get_page_size()-1))) +#define SL_GET_HEAD(x) ((struct sl_head *) PAGE_HEAD(x)) /** * sl_new - create a new Slab @@ -194,6 +195,7 @@ slab * sl_new(pool *p, uint size) { slab *s = ralloc(p, &sl_class); + s->p = p; uint align = sizeof(struct sl_alignment); if (align < sizeof(int)) align = sizeof(int); @@ -202,7 +204,6 @@ sl_new(pool *p, uint size) s->obj_size = size; s->head_size = sizeof(struct sl_head); - u64 page_size = get_page_size(); do { s->objs_per_slab = (page_size - s->head_size) / size; @@ -272,6 +273,9 @@ no_partial: goto okay; } h = alloc_page(); +#ifdef POISON + memset(h, 0xba, page_size); +#endif ASSERT_DIE(SL_GET_HEAD(h) == h); memset(h, 0, s->head_size); add_head(&s->partial_heads, &h->n); @@ -327,7 +331,12 @@ sl_free(slab *s, void *oo) { rem_node(&h->n); if (s->num_empty_heads >= MAX_EMPTY_HEADS) + { +#ifdef POISON + memset(h, 0xde, page_size); +#endif free_page(h); + } else { add_head(&s->empty_heads, &h->n); @@ -391,7 +400,7 @@ slab_memsize(resource *r) return (struct resmem) { .effective = eff, - .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * get_page_size() - eff, + .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * page_size - eff, }; } @@ -402,10 +411,10 @@ slab_lookup(resource *r, unsigned long a) struct sl_head *h; WALK_LIST(h, s->partial_heads) - if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a) + if ((unsigned long) h < a && (unsigned long) h + page_size < a) return r; WALK_LIST(h, s->full_heads) - if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a) + if ((unsigned long) h < a && (unsigned long) h + page_size < a) return r; return NULL; } diff --git a/lib/socket.h b/lib/socket.h index 96fedeeb..ff07660f 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -12,6 +12,7 @@ #include <errno.h> #include "lib/resource.h" +#include "lib/event.h" #ifdef HAVE_LIBSSH #define LIBSSH_LEGACY_0_4 #include <libssh/libssh.h> @@ -56,6 +57,7 @@ typedef struct birdsock { uint fast_rx; /* RX has higher priority in event loop */ uint rbsize; int (*rx_hook)(struct birdsock *, uint size); /* NULL=receiving turned off, returns 1 to clear rx buffer */ + struct event_cork *cork; /* Cork to temporarily stop receiving data */ byte *tbuf, *tpos; /* NULL=allocate automatically */ byte *ttx; /* Internal */ @@ -79,6 +81,7 @@ typedef struct birdsock { const char *password; /* Password for MD5 authentication */ const char *err; /* Error message */ struct ssh_sock *ssh; /* Used in SK_SSH */ + struct event reloop; /* Reloop event */ } sock; sock *sock_new(pool *); /* Allocate new socket */ @@ -128,6 +131,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou #define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */ #define SKF_HDRINCL 0x400 /* Used internally */ #define SKF_PKTINFO 0x800 /* Used internally */ +#define SKF_PASSIVE_THREAD 0x1000 /* Child sockets used in thread, do not add to main loop */ /* * Socket types SA SP DA DP IF TTL SendTo (?=may, -=must not, *=must) diff --git a/lib/timer.c b/lib/timer.c index 381163d0..eb7ea690 100644 --- a/lib/timer.c +++ b/lib/timer.c @@ -32,61 +32,18 @@ #include "nest/bird.h" +#include "lib/coro.h" #include "lib/heap.h" #include "lib/resource.h" #include "lib/timer.h" - -struct timeloop main_timeloop; - - -#ifdef USE_PTHREADS - #include <pthread.h> -/* Data accessed and modified from proto/bfd/io.c */ -pthread_key_t current_time_key; - -static inline struct timeloop * -timeloop_current(void) -{ - return pthread_getspecific(current_time_key); -} - -static inline void -timeloop_init_current(void) -{ - pthread_key_create(¤t_time_key, NULL); - pthread_setspecific(current_time_key, &main_timeloop); -} +_Atomic btime last_time; +_Atomic btime real_time; void wakeup_kick_current(void); -#else - -/* Just use main timelooop */ -static inline struct timeloop * timeloop_current(void) { return &main_timeloop; } -static inline void timeloop_init_current(void) { } - -#endif - -btime -current_time(void) -{ - return timeloop_current()->last_time; -} - -btime -current_real_time(void) -{ - struct timeloop *loop = timeloop_current(); - - if (!loop->real_time) - times_update_real_time(loop); - - return loop->real_time; -} - #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, \ @@ -112,7 +69,7 @@ tm_dump(resource *r) if (t->recurrent) debug("recur %d, ", t->recurrent); if (t->expires) - debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS); + debug("in loop %p expires in %d ms)\n", t->loop, (t->expires - current_time()) TO_MS); else debug("inactive)\n"); } @@ -135,41 +92,40 @@ tm_new(pool *p) return t; } -void -tm_set(timer *t, btime when) +static void +tm_set_in_tl(timer *t, btime when, struct timeloop *local_timeloop) { - struct timeloop *loop = timeloop_current(); - uint tc = timers_count(loop); + uint tc = timers_count(local_timeloop); if (!t->expires) { t->index = ++tc; t->expires = when; - BUFFER_PUSH(loop->timers) = t; - HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP); + BUFFER_PUSH(local_timeloop->timers) = t; + HEAP_INSERT(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP); } else if (t->expires < when) { t->expires = when; - HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index); + HEAP_INCREASE(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index); } else if (t->expires > when) { t->expires = when; - HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index); + HEAP_DECREASE(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index); } -#ifdef CONFIG_BFD - /* Hack to notify BFD loops */ - if ((loop != &main_timeloop) && (t->index == 1)) - wakeup_kick_current(); -#endif + t->loop = local_timeloop; + + if ((t->index == 1) && (local_timeloop->coro != this_coro)) + birdloop_ping(local_timeloop->loop); } void -tm_start(timer *t, btime after) +tm_set_in(timer *t, btime when, struct birdloop *loop) { - tm_set(t, current_time() + MAX(after, 0)); + ASSERT_DIE(birdloop_inside(loop)); + tm_set_in_tl(t, when, birdloop_time_loop(loop)); } void @@ -178,20 +134,22 @@ tm_stop(timer *t) if (!t->expires) return; - struct timeloop *loop = timeloop_current(); - uint tc = timers_count(loop); + TLOCK_TIMER_ASSERT(t->loop); - HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index); - BUFFER_POP(loop->timers); + uint tc = timers_count(t->loop); + + HEAP_DELETE(t->loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index); + BUFFER_POP(t->loop->timers); t->index = -1; t->expires = 0; + t->loop = NULL; } void timers_init(struct timeloop *loop, pool *p) { - times_init(loop); + TLOCK_TIMER_ASSERT(loop); BUFFER_INIT(loop->timers, p, 4); BUFFER_PUSH(loop->timers) = NULL; @@ -200,13 +158,15 @@ timers_init(struct timeloop *loop, pool *p) void io_log_event(void *hook, void *data); void -timers_fire(struct timeloop *loop) +timers_fire(struct timeloop *loop, int io_log) { + TLOCK_TIMER_ASSERT(loop); + btime base_time; timer *t; - times_update(loop); - base_time = loop->last_time; + times_update(); + base_time = current_time(); while (t = timers_first(loop)) { @@ -217,32 +177,25 @@ timers_fire(struct timeloop *loop) { btime when = t->expires + t->recurrent; - if (when <= loop->last_time) - when = loop->last_time + t->recurrent; + if (when <= base_time) + when = base_time + t->recurrent; if (t->randomize) when += random() % (t->randomize + 1); - tm_set(t, when); + tm_set_in_tl(t, when, loop); } else tm_stop(t); /* This is ugly hack, we want to log just timers executed from the main I/O loop */ - if (loop == &main_timeloop) + if (io_log) io_log_event(t->hook, t->data); t->hook(t); } } -void -timer_init(void) -{ - timers_init(&main_timeloop, &root_pool); - timeloop_init_current(); -} - /** * tm_parse_time - parse a date and time diff --git a/lib/timer.h b/lib/timer.h index c5ea430c..04544ace 100644 --- a/lib/timer.h +++ b/lib/timer.h @@ -12,8 +12,14 @@ #include "nest/bird.h" #include "lib/buffer.h" +#include "lib/io-loop.h" +#include "lib/locking.h" #include "lib/resource.h" +#include <stdatomic.h> + +extern _Atomic btime last_time; +extern _Atomic btime real_time; typedef struct timer { @@ -25,36 +31,43 @@ typedef struct timer uint randomize; /* Amount of randomization */ uint recurrent; /* Timer recurrence */ + struct timeloop *loop; /* Loop where the timer is active */ + int index; } timer; struct timeloop { BUFFER_(timer *) timers; - btime last_time; - btime real_time; + struct domain_generic *domain; + struct birdloop *loop; + struct coroutine *coro; }; +#define TLOCK_TIMER_ASSERT(loop) ASSERT_DIE((loop)->domain && DG_IS_LOCKED((loop)->domain)) +#define TLOCK_LOCAL_ASSERT(loop) ASSERT_DIE(!(loop)->domain || DG_IS_LOCKED((loop)->domain)) + static inline uint timers_count(struct timeloop *loop) -{ return loop->timers.used - 1; } +{ TLOCK_TIMER_ASSERT(loop); return loop->timers.used - 1; } static inline timer *timers_first(struct timeloop *loop) -{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; } +{ TLOCK_TIMER_ASSERT(loop); return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; } -extern struct timeloop main_timeloop; - -btime current_time(void); -btime current_real_time(void); +#define current_time() atomic_load_explicit(&last_time, memory_order_acquire) +#define current_real_time() atomic_load_explicit(&real_time, memory_order_acquire) //#define now (current_time() TO_S) //#define now_real (current_real_time() TO_S) extern btime boot_time; timer *tm_new(pool *p); -void tm_set(timer *t, btime when); -void tm_start(timer *t, btime after); +#define tm_set(t, when) tm_set_in((t), (when), &main_birdloop) +#define tm_start(t, after) tm_start_in((t), (after), &main_birdloop) void tm_stop(timer *t); +void tm_set_in(timer *t, btime when, struct birdloop *loop); +#define tm_start_in(t, after, loop) tm_set_in((t), (current_time() + MAX_((after), 0)), loop) + static inline int tm_active(timer *t) { @@ -94,15 +107,11 @@ tm_start_max(timer *t, btime after) } /* In sysdep code */ -void times_init(struct timeloop *loop); -void times_update(struct timeloop *loop); -void times_update_real_time(struct timeloop *loop); +void times_update(void); /* For I/O loop */ void timers_init(struct timeloop *loop, pool *p); -void timers_fire(struct timeloop *loop); - -void timer_init(void); +void timers_fire(struct timeloop *loop, int io_log); struct timeformat { |