aboutsummaryrefslogtreecommitdiff
path: root/src/common/pubsub.h
blob: bbb4f02a42f00793319b304af5a22b46f3e26317 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* 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 */