aboutsummaryrefslogtreecommitdiff
path: root/src/lib/pubsub/pubsub_macros.h
blob: e5ffbe501af7f560af683b0c91136602f2a8c855 (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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/* Copyright (c) 2001, Matej Pfajfar.
 * Copyright (c) 2001-2004, Roger Dingledine.
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file pubsub_macros.h
 * \brief Macros to help with the publish/subscribe dispatch API.
 *
 * The dispatch API allows different subsystems of Tor to communicate with
 * another asynchronously via a shared "message" system.  Some subsystems
 * declare that they publish a given message, and others declare that they
 * subscribe to it.  Both subsystems depend on the message, but not upon one
 * another.
 *
 * To declare a message, use DECLARE_MESSAGE() (for messages that take their
 * data as a pointer) or DECLARE_MESSAGE_INT() (for messages that take their
 * data as an integer.  For example, you might say
 *
 *     DECLARE_MESSAGE(new_circuit, circ, circuit_handle_t *);
 * or
 *     DECLARE_MESSAGE_INT(shutdown_requested, boolean, bool);
 *
 * Every message has a unique name, a "type name" that the dispatch system
 * uses to manage associated data, and a C type name.  You'll want to put
 * these declarations in a header, to be included by all publishers and all
 * subscribers.
 *
 * When a subsystem wants to publish a message, it uses DECLARE_PUBLISH() at
 * file scope to create necessary static functions.  Then, in its subsystem
 * initialization (in the "bind to dispatcher" callback) (TODO: name this
 * properly!), it calls DISPATCH_ADD_PUB() to tell the dispatcher about its
 * intent to publish.  When it actually wants to publish, it uses the
 * PUBLISH() macro.  For example:
 *
 *     // At file scope
 *     DECLARE_PUBLISH(shutdown_requested);
 *
 *     static void bind_to_dispatcher(pubsub_connector_t *con)
 *     {
 *         DISPATCH_ADD_PUB(con, mainchannel, shutdown_requested);
 *     }
 *
 *     // somewhere in a function
 *        {
 *            PUBLISH(shutdown_requested, true);
 *        }
 *
 * When a subsystem wants to subscribe to a message, it uses
 * DECLARE_SUBSCRIBE() at file scope to declare static functions.  It must
 * declare a hook function that receives the message type.  Then, in its "bind
 * to dispatcher" function, it calls DISPATCHER_ADD_SUB() to tell the
 * dispatcher about its intent to subscribe.  When another module publishes
 * the message, the dispatcher will call the provided hook function.
 *
 *     // At file scope.  The first argument is the message that you're
 *     // subscribing to; the second argument is the hook function to declare.
 *     DECLARE_SUBSCRIBE(shutdown_requested, on_shutdown_req_cb);
 *
 *     // You need to declare this function.
 *     static void on_shutdown_req_cb(const msg_t *msg,
 *                                    bool value)
 *     {
 *         // (do something here.)
 *     }
 *
 *     static void bind_to_dispatcher(pubsub_connector_t *con)
 *     {
 *         DISPATCH_ADD_SUB(con, mainchannel, shutdown_requested);
 *     }
 *
 * Where did these types come from?  Somewhere in the code, you need to call
 * DISPATCH_REGISTER_TYPE() to make sure that the dispatcher can manage the
 * message auxiliary data.  It associates a vtbl-like structure with the
 * type name, so that the dispatcher knows how to manipulate the type you're
 * giving it.
 *
 * For example, the "boolean" type we're using above could be defined as:
 *
 *    static char *boolean_fmt(msg_aux_data_t d)
 *    {
 *        // This is used for debugging and dumping messages.
 *        if (d.u64)
 *            return tor_strdup("true");
 *        else
 *            return tor_strdup("false");
 *    }
 *
 *    static void boolean_free(msg_aux_data_t d)
 *    {
 *        // We don't actually need to do anything to free a boolean.
 *        // We could use "NULL" instead of this function, but I'm including
 *        // it as an example.
 *    }
 *
 *    static void bind_to_dispatcher(pubsub_connector_t *con)
 *    {
 *        dispatch_typefns_t boolean_fns = {
 *            .fmt_fn = boolean_fmt,
 *            .free_fn = boolean_free,
 *        };
 *        DISPATCH_REGISTER_TYPE(con, boolean, &boolean_fns);
 *    }
 *
 *
 *
 * So, how does this all work?  (You can stop reading here, unless you're
 * debugging something.)
 *
 * When you declare a message in a header with DECLARE_MESSAGE() or
 * DECLARE_MESSAGE_INT(), it creates five things:
 *
 *    * two typedefs for the message argument (constant and non-constant
 *      variants).
 *    * a constant string to hold the declared message type name
 *    * two inline functions, to coerce the message argument type to and from
 *      a "msg_aux_data_t" union.
 *
 * All of these declarations have names based on the message name.
 *
 * Later, when you say DECLARE_PUBLISH() or DECLARE_SUBSCRIBE(), we use the
 * elements defined by DECLARE_MESSAGE() to make sure that the publish
 * function takes the correct argument type, and that the subscription hook is
 * declared with the right argument type.
 **/

#ifndef TOR_DISPATCH_MSG_H
#define TOR_DISPATCH_MSG_H

#include "lib/cc/compat_compiler.h"
#include "lib/dispatch/dispatch_naming.h"
#include "lib/pubsub/pub_binding_st.h"
#include "lib/pubsub/pubsub_connect.h"
#include "lib/pubsub/pubsub_flags.h"
#include "lib/pubsub/pubsub_publish.h"

/* Implemenation notes:
 *
 * For a messagename "foo", the DECLARE_MESSAGE*() macros must declare:
 *
 * msg_arg_type__foo -- a typedef for the argument type of the foo message.
 * msg_arg_consttype__foo -- a typedef for the const argument type of the
 *     foo message.
 * msg_arg_name__foo[] -- a static string constant holding the unique
 *      identifier for the type of the foo message.
 * msg_arg_get__foo() -- an inline function taking a msg_aux_data_t and
 *      returning the C data type.
 * msg_arg_set__foo() -- an inline function taking a msg_aux_data_t and
 *      the C type, setting the msg_aux_data_t to hold the C type.
 *
 * For a messagename "foo", the DECLARE_PUBLISH() macro must declare:
 *
 * pub_binding__foo -- A static pub_binding_t object used to send messages
 *      from this module.
 * publish_fn__foo -- A function taking an argument of the appropriate
 *      C type, to be invoked by PUBLISH().
 *
 * For a messagename "foo", the DECLARE_SUBSCRIBE() macro must declare:
 *
 * hookfn -- A user-provided function name, with the correct signature.
 * recv_fn__foo -- A wrapper callback that takes a msg_t *, and calls
 *      hookfn with the appropriate arguments.
 */

/** Macro to declare common elements shared by DECLARE_MESSAGE and
 * DECLARE_MESSAGE_INT.  Don't call this directly.
 *
 * Note that the "msg_arg_name" string constant is defined in each
 * translation unit.  This might be undesirable; we can tweak it in the
 * future if need be.
 */
#define DECLARE_MESSAGE_COMMON__(messagename, typename, c_type)         \
  typedef c_type msg_arg_type__ ##messagename;                          \
  typedef const c_type msg_arg_consttype__ ##messagename;               \
  ATTR_UNUSED static const char msg_arg_name__ ##messagename[] = # typename;

/**
 * Use this macro in a header to declare the existence of a given message,
 * taking a pointer as auxiliary data.
 *
 * "messagename" is a unique identifier for the message; it must be a valid
 * C identifier.
 *
 * "typename" is a unique identifier for the type of the auxiliary data.
 * It needs to be defined somewhere in Tor, using
 * "DISPATCH_REGISTER_TYPE."
 *
 * "c_ptr_type" is a C pointer type (like "char *" or "struct foo *").
 * The "*" needs to be included.
 */
#define DECLARE_MESSAGE(messagename, typename, c_ptr_type)              \
  DECLARE_MESSAGE_COMMON__(messagename, typename, c_ptr_type)           \
  ATTR_UNUSED static inline c_ptr_type                                  \
  msg_arg_get__ ##messagename(msg_aux_data_t m)                         \
  {                                                                     \
    return m.ptr;                                                       \
  }                                                                     \
  ATTR_UNUSED static inline void                                        \
  msg_arg_set__ ##messagename(msg_aux_data_t *m, c_ptr_type v)          \
  {                                                                     \
    m->ptr = v;                                                         \
  }                                                                     \
  EAT_SEMICOLON

/**
 * Use this macro in a header to declare the existence of a given message,
 * taking an integer as auxiliary data.
 *
 * "messagename" is a unique identifier for the message; it must be a valid
 * C identifier.
 *
 * "typename" is a unique identifier for the type of the auxiliary data.  It
 * needs to be defined somewhere in Tor, using "DISPATCH_REGISTER_TYPE."
 *
 * "c_type" is a C integer type, like "int" or "bool".  It needs to fit inside
 * a uint64_t.
 */
#define DECLARE_MESSAGE_INT(messagename, typename, c_type)              \
  DECLARE_MESSAGE_COMMON__(messagename, typename, c_type)               \
  ATTR_UNUSED static inline c_type                                      \
  msg_arg_get__ ##messagename(msg_aux_data_t m)                         \
  {                                                                     \
    return (c_type)m.u64;                                               \
  }                                                                     \
  ATTR_UNUSED static inline void                                        \
  msg_arg_set__ ##messagename(msg_aux_data_t *m, c_type v)              \
  {                                                                     \
    m->u64 = (uint64_t)v;                                               \
  }                                                                     \
  EAT_SEMICOLON

/**
 * Use this macro inside a C module declare that we'll be publishing a given
 * message type from within this module.
 *
 * It creates necessary functions and wrappers to publish a message whose
 * unique identifier is "messagename".
 *
 * Before you use this, you need to include the header where DECLARE_MESSAGE*()
 * was used for this message.
 *
 * You can only use this once per message in each subsystem.
 */
#define DECLARE_PUBLISH(messagename)                                    \
  static pub_binding_t pub_binding__ ##messagename;                     \
  static void                                                           \
  publish_fn__ ##messagename(msg_arg_type__ ##messagename arg)          \
  {                                                                     \
    msg_aux_data_t data;                                                \
    msg_arg_set__ ##messagename(&data, arg);                            \
    pubsub_pub_(&pub_binding__ ##messagename, data);                    \
  }                                                                     \
  EAT_SEMICOLON

/**
 * Use this macro inside a C file to declare that we're subscribing to a
 * given message and associating it with a given "hook function".  It
 * declares the hook function static, and helps with strong typing.
 *
 * Before you use this, you need to include the header where
 * DECLARE_MESSAGE*() was used for the message whose unique identifier is
 * "messagename".
 *
 * You will need to define a function with the name that you provide for
 * "hookfn".  The type of this function will be:
 *     static void hookfn(const msg_t *, const c_type)
 * where c_type is the c type that you declared in the header.
 *
 * You can only use this once per message in each subsystem.
 */
#define DECLARE_SUBSCRIBE(messagename, hookfn) \
  static void hookfn(const msg_t *,                             \
                     const msg_arg_consttype__ ##messagename);  \
  static void recv_fn__ ## messagename(const msg_t *m)          \
  {                                                             \
    msg_arg_type__ ## messagename arg;                          \
    arg = msg_arg_get__ ##messagename(m->aux_data__);           \
    hookfn(m, arg);                                             \
  }                                                             \
  EAT_SEMICOLON

/**
 * Add a fake use of the publish function for 'messagename', so that
 * the compiler does not call it unused.
 */
#define DISPATCH__FAKE_USE_OF_PUBFN_(messagename)                       \
  ( 0 ? (publish_fn__ ##messagename((msg_arg_type__##messagename)0), 1) \
    : 1)

/**
 * This macro is for internal use.  It backs DISPATCH_ADD_PUB*()
 */
#define DISPATCH_ADD_PUB_(connector, channel, messagename, flags)       \
  (                                                                     \
    DISPATCH__FAKE_USE_OF_PUBFN_(messagename),                          \
    pubsub_add_pub_((connector),                                        \
                      &pub_binding__ ##messagename,                     \
                      get_channel_id(# channel),                        \
                    get_message_id(# messagename),                      \
                      get_msg_type_id(msg_arg_name__ ## messagename),   \
                      (flags),                                          \
                      __FILE__,                                         \
                      __LINE__)                                         \
    )

/**
 * Use a given connector and channel name to declare that this subsystem will
 * publish a given message type.
 *
 * Call this macro from within the add_subscriptions() function of a module.
 */
#define DISPATCH_ADD_PUB(connector, channel, messagename)       \
    DISPATCH_ADD_PUB_(connector, channel, messagename, 0)

/**
 * Use a given connector and channel name to declare that this subsystem will
 * publish a given message type, and that no other subsystem is allowed to.
 *
 * Call this macro from within the add_subscriptions() function of a module.
 */
#define DISPATCH_ADD_PUB_EXCL(connector, channel, messagename)  \
    DISPATCH_ADD_PUB_(connector, channel, messagename, DISP_FLAG_EXCL)

/**
 * This macro is for internal use. It backs DISPATCH_ADD_SUB*()
 */
#define DISPATCH_ADD_SUB_(connector, channel, messagename, flags)       \
  pubsub_add_sub_((connector),                                          \
                    recv_fn__ ##messagename,                            \
                    get_channel_id(#channel),                           \
                    get_message_id(# messagename),                      \
                    get_msg_type_id(msg_arg_name__ ##messagename),      \
                    (flags),                                            \
                    __FILE__,                                           \
                    __LINE__)
/**
 * Use a given connector and channel name to declare that this subsystem will
 * receive a given message type.
 *
 * Call this macro from within the add_subscriptions() function of a module.
 */
#define DISPATCH_ADD_SUB(connector, channel, messagename)       \
    DISPATCH_ADD_SUB_(connector, channel, messagename, 0)
/**
 * Use a given connector and channel name to declare that this subsystem will
 * receive a given message type, and that no other subsystem is allowed to do
 * so.
 *
 * Call this macro from within the add_subscriptions() function of a module.
 */
#define DISPATCH_ADD_SUB_EXCL(connector, channel, messagename)  \
    DISPATCH_ADD_SUB_(connector, channel, messagename, DISP_FLAG_EXCL)

/**
 * Publish a given message with a given argument.  (Takes ownership of the
 * argument if it is a pointer.)
 */
#define PUBLISH(messagename, arg)               \
  publish_fn__ ##messagename(arg)

/**
 * Use a given connector to declare that the functions to be used to manipuate
 * a certain C type.
 **/
#define DISPATCH_REGISTER_TYPE(con, type, fns)                  \
  pubsub_connector_register_type_((con),                        \
                                  get_msg_type_id(#type),       \
                                  (fns),                        \
                                  __FILE__,                     \
                                  __LINE__)

#endif /* !defined(TOR_DISPATCH_MSG_H) */