aboutsummaryrefslogtreecommitdiff
path: root/src/common/pubsub.h
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2016-03-24 11:15:19 -0400
committerNick Mathewson <nickm@torproject.org>2016-05-11 13:25:11 -0400
commit80a6c8caa3910b18ce50ef870ef0af546c64faa2 (patch)
tree2f48426164336a059e8f3ff02ae631463017d78c /src/common/pubsub.h
parent4e76b206b5fac18805a5cb2c3440dd367bd3b92d (diff)
downloadtor-80a6c8caa3910b18ce50ef870ef0af546c64faa2.tar.gz
tor-80a6c8caa3910b18ce50ef870ef0af546c64faa2.zip
Basic work on a publish/subscribe abstraction
The goal here is to provide a way to decouple pieces of the code that want to learn "when something happens" from those that realize that it has happened. The implementation here consists of a generic backend, plus a set of macros to define and implement a set of type-safe frontends.
Diffstat (limited to 'src/common/pubsub.h')
-rw-r--r--src/common/pubsub.h177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/common/pubsub.h b/src/common/pubsub.h
new file mode 100644
index 0000000000..09e492ec4f
--- /dev/null
+++ b/src/common/pubsub.h
@@ -0,0 +1,177 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file pubsub.h
+ * \brief Macros to implement publish/subscribe abstractions.
+ *
+ * To use these macros, call DECLARE_PUBSUB_TOPIC() with an identifier to use
+ * as your topic. Below, I'm going to assume you say DECLARE_PUBSUB_TOPIC(T).
+ *
+ * Doing this will declare the following types:
+ * typedef struct T_event_data_t T_event_data_t; // you define this struct
+ * typedef struct T_subscriber_data_t T_subscriber_data_t; // this one too.
+ * typedef struct T_subscriber_t T_subscriber_t; // opaque
+ * typedef int (*T_subscriber_fn_t)(T_event_data_t*, T_subscriber_data_t*);
+ *
+ * and it will declare the following functions:
+ * const T_subscriber_t *T_subscribe(T_subscriber_fn_t,
+ * T_subscriber_data_t *,
+ * unsigned flags,
+ * unsigned priority);
+ * int T_unsubscribe(const T_subscriber_t *)
+ *
+ * Elsewhere you can say DECLARE_NOTIFY_PUBSUB_TOPIC(static, T), which declares:
+ * static int T_notify(T_event_data_t *, unsigned notify_flags);
+ * static void T_clear(void);
+ *
+ * And in some C file, you would define these functions with:
+ * IMPLEMENT_PUBSUB_TOPIC(static, T).
+ *
+ * The implementations will be small typesafe wrappers over generic versions
+ * of the above functions.
+ *
+ * To use the typesafe functions, you add any number of subscribers with
+ * T_subscribe(). Each has an associated function pointer, data pointer,
+ * and priority. Later, you can invoke T_notify() to declare that the
+ * event has occurred. Each of the subscribers will be invoked once.
+ **/
+
+#ifndef TOR_PUBSUB_H
+#define TOR_PUBSUB_H
+
+#include "torint.h"
+
+/**
+ * Flag for T_subscribe: die with an assertion failure if the event
+ * have ever been published before. Used when a subscriber must absolutely
+ * never have missed an event.
+ */
+#define SUBSCRIBE_ATSTART (1u<<0)
+
+#define DECLARE_PUBSUB_STRUCT_TYPES(name) \
+ /* You define this type. */ \
+ typedef struct name ## _event_data_t name ## _event_data_t; \
+ /* You define this type. */ \
+ typedef struct name ## _subscriber_data_t name ## _subscriber_data_t;
+
+#define DECLARE_PUBSUB_TOPIC(name) \
+ /* This type is opaque. */ \
+ typedef struct name ## _subscriber_t name ## _subscriber_t; \
+ /* You declare functions matching this type. */ \
+ typedef int (*name ## _subscriber_fn_t)( \
+ name ## _event_data_t *data, \
+ name ## _subscriber_data_t *extra); \
+ /* Call this function to subscribe to a topic. */ \
+ const name ## _subscriber_t *name ## _subscribe( \
+ name##_subscriber_fn_t subscriber, \
+ name##_subscriber_data_t *extra_data, \
+ unsigned flags, \
+ unsigned priority); \
+ /* Call this function to unsubscribe from a topic. */ \
+ int name ## _unsubscribe(const name##_subscriber_t *s);
+
+#define DECLARE_NOTIFY_PUBSUB_TOPIC(linkage, name) \
+ /* Call this function to notify all subscribers. Flags not yet used. */ \
+ linkage int name ## _notify(name ## _event_data_t *data, unsigned flags); \
+ /* Call this function to release storage held by the topic. */ \
+ linkage void name ## _clear(void);
+
+/**
+ * Type used to hold a generic function for a subscriber.
+ *
+ * [Yes, it is safe to cast to this, so long as we cast back to the original
+ * type before calling. From C99: "A pointer to a function of one type may be
+ * converted to a pointer to a function of another type and back again; the
+ * result shall compare equal to the original pointer."]
+*/
+typedef int (*pubsub_subscriber_fn_t)(void *, void *);
+
+/**
+ * Helper type to implement pubsub abstraction. Don't use this directly.
+ * It represents a subscriber.
+ */
+typedef struct pubsub_subscriber_t {
+ /** Function to invoke when the event triggers. */
+ pubsub_subscriber_fn_t fn;
+ /** Data associated with this subscriber. */
+ void *subscriber_data;
+ /** Priority for this subscriber. Low priorities happen first. */
+ unsigned priority;
+ /** Flags set on this subscriber. Not yet used.*/
+ unsigned subscriber_flags;
+} pubsub_subscriber_t;
+
+/**
+ * Helper type to implement pubsub abstraction. Don't use this directly.
+ * It represents a topic, and keeps a record of subscribers.
+ */
+typedef struct pubsub_topic_t {
+ /** List of subscribers to this topic. May be NULL. */
+ struct smartlist_t *subscribers;
+ /** Total number of times that pubsub_notify_() has ever been called on this
+ * topic. */
+ uint64_t n_events_fired;
+ /** True iff we're running 'notify' on this topic, and shouldn't allow
+ * any concurrent modifications or events. */
+ unsigned locked;
+} pubsub_topic_t;
+
+const pubsub_subscriber_t *pubsub_subscribe_(pubsub_topic_t *topic,
+ pubsub_subscriber_fn_t fn,
+ void *subscriber_data,
+ unsigned subscribe_flags,
+ unsigned priority);
+int pubsub_unsubscribe_(pubsub_topic_t *topic, const pubsub_subscriber_t *sub);
+void pubsub_clear_(pubsub_topic_t *topic);
+typedef int (*pubsub_notify_fn_t)(pubsub_subscriber_t *subscriber,
+ void *notify_data);
+int pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn,
+ void *notify_data, unsigned notify_flags);
+
+#define IMPLEMENT_PUBSUB_TOPIC(notify_linkage, name) \
+ static pubsub_topic_t name ## _topic_ = { NULL, 0, 0 }; \
+ const name ## _subscriber_t * \
+ name ## _subscribe(name##_subscriber_fn_t subscriber, \
+ name##_subscriber_data_t *extra_data, \
+ unsigned flags, \
+ unsigned priority) \
+ { \
+ const pubsub_subscriber_t *s; \
+ s = pubsub_subscribe_(&name##_topic_, \
+ (pubsub_subscriber_fn_t)subscriber, \
+ extra_data, \
+ flags, \
+ priority); \
+ return (const name##_subscriber_t *)s; \
+ } \
+ int \
+ name ## _unsubscribe(const name##_subscriber_t *subscriber) \
+ { \
+ return pubsub_unsubscribe_(&name##_topic_, \
+ (const pubsub_subscriber_t *)subscriber); \
+ } \
+ static int \
+ name##_call_the_notify_fn_(pubsub_subscriber_t *subscriber, \
+ void *notify_data) \
+ { \
+ name ## _subscriber_fn_t fn; \
+ fn = (name ## _subscriber_fn_t) subscriber->fn; \
+ return fn(notify_data, subscriber->subscriber_data); \
+ } \
+ notify_linkage int \
+ name ## _notify(name ## _event_data_t *event_data, unsigned flags) \
+ { \
+ return pubsub_notify_(&name##_topic_, \
+ name##_call_the_notify_fn_, \
+ event_data, \
+ flags); \
+ } \
+ notify_linkage void \
+ name ## _clear(void) \
+ { \
+ pubsub_clear_(&name##_topic_); \
+ }
+
+#endif /* TOR_PUBSUB_H */
+