/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file pubsub.c * * \brief DOCDOC */ #include "orconfig.h" #include "pubsub.h" #include "container.h" /** Helper: insert s into topic's list of subscribers, keeping * them sorted in priority order. */ static void subscriber_insert(pubsub_topic_t *topic, pubsub_subscriber_t *s) { int i; smartlist_t *sl = topic->subscribers; for (i = 0; i < smartlist_len(sl); ++i) { pubsub_subscriber_t *other = smartlist_get(sl, i); if (s->priority < other->priority) { break; } } smartlist_insert(sl, i, s); } /** * Add a new subscriber to topic, where (when an event is triggered), * we'll notify the function fn by passing it subscriber_data. * Return a handle to the subscribe which can later be passed to * pubsub_unsubscribe_(). * * Functions are called in priority order, from lowest to highest. * * See pubsub.h for subscribe_flags. */ const pubsub_subscriber_t * pubsub_subscribe_(pubsub_topic_t *topic, pubsub_subscriber_fn_t fn, void *subscriber_data, unsigned subscribe_flags, unsigned priority) { tor_assert(! topic->locked); if (subscribe_flags & SUBSCRIBE_ATSTART) { tor_assert(topic->n_events_fired == 0); } pubsub_subscriber_t *r = tor_malloc_zero(sizeof(*r)); r->priority = priority; r->subscriber_flags = subscribe_flags; r->fn = fn; r->subscriber_data = subscriber_data; if (topic->subscribers == NULL) { topic->subscribers = smartlist_new(); } subscriber_insert(topic, r); return r; } /** * Remove the subscriber s from topic. After calling this * function, s may no longer be used. */ int pubsub_unsubscribe_(pubsub_topic_t *topic, const pubsub_subscriber_t *s) { tor_assert(! topic->locked); smartlist_t *sl = topic->subscribers; if (sl == NULL) return -1; int i = smartlist_pos(sl, s); if (i == -1) return -1; pubsub_subscriber_t *tmp = smartlist_get(sl, i); tor_assert(tmp == s); smartlist_del_keeporder(sl, i); tor_free(tmp); return 0; } /** * For every subscriber s in topic, invoke notify_fn on s and * event_data. Return 0 if there were no nonzero return values, and -1 if * there were any. */ int pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn, void *event_data, unsigned notify_flags) { tor_assert(! topic->locked); (void) notify_flags; smartlist_t *sl = topic->subscribers; int n_bad = 0; ++topic->n_events_fired; if (sl == NULL) return -1; topic->locked = 1; SMARTLIST_FOREACH_BEGIN(sl, pubsub_subscriber_t *, s) { int r = notify_fn(s, event_data); if (r != 0) ++n_bad; } SMARTLIST_FOREACH_END(s); topic->locked = 0; return (n_bad == 0) ? 0 : -1; } /** * Release all storage held by topic. */ void pubsub_clear_(pubsub_topic_t *topic) { tor_assert(! topic->locked); smartlist_t *sl = topic->subscribers; if (sl == NULL) return; SMARTLIST_FOREACH_BEGIN(sl, pubsub_subscriber_t *, s) { tor_free(s); } SMARTLIST_FOREACH_END(s); smartlist_free(sl); topic->subscribers = NULL; topic->n_events_fired = 0; }