diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2013-07-10 15:50:43 +0800 |
---|---|---|
committer | Geoff Levand <geoff@infradead.org> | 2013-07-23 09:44:45 -0700 |
commit | 45c7385acbd1299cacfa5cc335ffa6a0f0523980 (patch) | |
tree | 3b82b5611556d352078373c04360b754ea5a845d | |
parent | d92fff7d8898acc1a56607fbb5a6897a70041344 (diff) | |
download | petitboot-45c7385acbd1299cacfa5cc335ffa6a0f0523980.zip petitboot-45c7385acbd1299cacfa5cc335ffa6a0f0523980.tar.gz |
lib/waiter: Defer free of removed waiters
We may end up calling remove()-d time waiters if the timeout expires as
we're processing an IO waiter. Instead of freeing the waiter in
waiter_remove, mark the waiter as inactive, and defer the free until the
end of waiter_poll().
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
-rw-r--r-- | lib/waiter/waiter.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/lib/waiter/waiter.c b/lib/waiter/waiter.c index 513ab60..89fa9cb 100644 --- a/lib/waiter/waiter.c +++ b/lib/waiter/waiter.c @@ -6,6 +6,7 @@ #include <sys/time.h> #include <talloc/talloc.h> +#include <list/list.h> #include "waiter.h" @@ -20,6 +21,9 @@ struct waiter { struct timeval timeout; waiter_cb callback; void *arg; + + bool active; + struct list_item list; }; struct waitset { @@ -38,11 +42,14 @@ struct waitset { int n_io_waiters; struct waiter **time_waiters; int n_time_waiters; + + struct list free_list; }; struct waitset *waitset_create(void *ctx) { struct waitset *set = talloc_zero(ctx, struct waitset); + list_init(&set->free_list); return set; } @@ -72,6 +79,7 @@ static struct waiter *waiter_new(struct waitset *set) set->n_waiters++; set->waiters[set->n_waiters - 1] = waiter; + waiter->active = true; return waiter; } @@ -130,7 +138,8 @@ void waiter_remove(struct waiter *waiter) struct waiter *, set->n_waiters); set->waiters_changed = true; - talloc_free(waiter); + waiter->active = false; + list_add(&set->free_list, &waiter->list); } static void update_waiters(struct waitset *set) @@ -197,6 +206,7 @@ static void update_waiters(struct waitset *set) int waiter_poll(struct waitset *set) { struct timeval now, timeout; + struct waiter *waiter, *tmp; int timeout_ms; int i, rc; @@ -219,11 +229,14 @@ int waiter_poll(struct waitset *set) rc = poll(set->pollfds, set->n_io_waiters, timeout_ms); if (rc < 0) - return rc; + goto out; for (i = 0; i < set->n_io_waiters; i++) { struct waiter *waiter = set->io_waiters[i]; + if (!waiter->active) + continue; + if (!set->pollfds[i].revents) continue; rc = waiter->callback(waiter->arg); @@ -238,6 +251,9 @@ int waiter_poll(struct waitset *set) for (i = 0; i < set->n_time_waiters; i++) { struct waiter *waiter = set->time_waiters[i]; + if (!waiter->active) + continue; + if (timercmp(&waiter->timeout, &now, >)) continue; @@ -246,5 +262,13 @@ int waiter_poll(struct waitset *set) waiter_remove(waiter); } - return 0; + rc = 0; + +out: + /* free any waiters that have been removed */ + list_for_each_entry_safe(&set->free_list, waiter, tmp, list) + talloc_free(waiter); + list_init(&set->free_list); + + return rc; } |