mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 21:53:48 +01:00
180 lines
8.9 KiB
C
180 lines
8.9 KiB
C
/* Copyright (c) 2016-2017, 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 */
|
|
|