pubsub_macros.h (16235B)
1 /* Copyright (c) 2001, Matej Pfajfar. 2 * Copyright (c) 2001-2004, Roger Dingledine. 3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 4 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 5 /* See LICENSE for licensing information */ 6 7 /** 8 * \file pubsub_macros.h 9 * \brief Macros to help with the publish/subscribe dispatch API. 10 * 11 * The dispatch API allows different subsystems of Tor to communicate with 12 * another asynchronously via a shared "message" system. Some subsystems 13 * declare that they publish a given message, and others declare that they 14 * subscribe to it. Both subsystems depend on the message, but not upon one 15 * another. 16 * 17 * To declare a message, use DECLARE_MESSAGE() (for messages that take their 18 * data as a pointer) or DECLARE_MESSAGE_INT() (for messages that take their 19 * data as an integer. For example, you might say 20 * 21 * DECLARE_MESSAGE(new_circuit, circ, circuit_handle_t *); 22 * or 23 * DECLARE_MESSAGE_INT(shutdown_requested, boolean, bool); 24 * 25 * Every message has a unique name, a "type name" that the dispatch system 26 * uses to manage associated data, and a C type name. You'll want to put 27 * these declarations in a header, to be included by all publishers and all 28 * subscribers. 29 * 30 * When a subsystem wants to publish a message, it uses DECLARE_PUBLISH() at 31 * file scope to create necessary static functions. Then, in its subsystem 32 * initialization (in the "bind to dispatcher" callback) (TODO: name this 33 * properly!), it calls DISPATCH_ADD_PUB() to tell the dispatcher about its 34 * intent to publish. When it actually wants to publish, it uses the 35 * PUBLISH() macro. For example: 36 * 37 * // At file scope 38 * DECLARE_PUBLISH(shutdown_requested); 39 * 40 * static void bind_to_dispatcher(pubsub_connector_t *con) 41 * { 42 * DISPATCH_ADD_PUB(con, mainchannel, shutdown_requested); 43 * } 44 * 45 * // somewhere in a function 46 * { 47 * PUBLISH(shutdown_requested, true); 48 * } 49 * 50 * When a subsystem wants to subscribe to a message, it uses 51 * DECLARE_SUBSCRIBE() at file scope to declare static functions. It must 52 * declare a hook function that receives the message type. Then, in its "bind 53 * to dispatcher" function, it calls DISPATCHER_ADD_SUB() to tell the 54 * dispatcher about its intent to subscribe. When another module publishes 55 * the message, the dispatcher will call the provided hook function. 56 * 57 * // At file scope. The first argument is the message that you're 58 * // subscribing to; the second argument is the hook function to declare. 59 * DECLARE_SUBSCRIBE(shutdown_requested, on_shutdown_req_cb); 60 * 61 * // You need to declare this function. 62 * static void on_shutdown_req_cb(const msg_t *msg, 63 * bool value) 64 * { 65 * // (do something here.) 66 * } 67 * 68 * static void bind_to_dispatcher(pubsub_connector_t *con) 69 * { 70 * DISPATCH_ADD_SUB(con, mainchannel, shutdown_requested); 71 * } 72 * 73 * Where did these types come from? Somewhere in the code, you need to call 74 * DISPATCH_REGISTER_TYPE() to make sure that the dispatcher can manage the 75 * message auxiliary data. It associates a vtbl-like structure with the 76 * type name, so that the dispatcher knows how to manipulate the type you're 77 * giving it. 78 * 79 * For example, the "boolean" type we're using above could be defined as: 80 * 81 * static char *boolean_fmt(msg_aux_data_t d) 82 * { 83 * // This is used for debugging and dumping messages. 84 * if (d.u64) 85 * return tor_strdup("true"); 86 * else 87 * return tor_strdup("false"); 88 * } 89 * 90 * static void boolean_free(msg_aux_data_t d) 91 * { 92 * // We don't actually need to do anything to free a boolean. 93 * // We could use "NULL" instead of this function, but I'm including 94 * // it as an example. 95 * } 96 * 97 * static void bind_to_dispatcher(pubsub_connector_t *con) 98 * { 99 * dispatch_typefns_t boolean_fns = { 100 * .fmt_fn = boolean_fmt, 101 * .free_fn = boolean_free, 102 * }; 103 * DISPATCH_REGISTER_TYPE(con, boolean, &boolean_fns); 104 * } 105 * 106 * 107 * 108 * So, how does this all work? (You can stop reading here, unless you're 109 * debugging something.) 110 * 111 * When you declare a message in a header with DECLARE_MESSAGE() or 112 * DECLARE_MESSAGE_INT(), it creates five things: 113 * 114 * * two typedefs for the message argument (constant and non-constant 115 * variants). 116 * * a constant string to hold the declared message type name 117 * * two inline functions, to coerce the message argument type to and from 118 * a "msg_aux_data_t" union. 119 * 120 * All of these declarations have names based on the message name. 121 * 122 * Later, when you say DECLARE_PUBLISH() or DECLARE_SUBSCRIBE(), we use the 123 * elements defined by DECLARE_MESSAGE() to make sure that the publish 124 * function takes the correct argument type, and that the subscription hook is 125 * declared with the right argument type. 126 **/ 127 128 #ifndef TOR_DISPATCH_MSG_H 129 #define TOR_DISPATCH_MSG_H 130 131 #include "lib/cc/compat_compiler.h" 132 #include "lib/dispatch/dispatch_naming.h" 133 #include "lib/pubsub/pub_binding_st.h" 134 #include "lib/pubsub/pubsub_connect.h" 135 #include "lib/pubsub/pubsub_flags.h" 136 #include "lib/pubsub/pubsub_publish.h" 137 138 /* Implementation notes: 139 * 140 * For a messagename "foo", the DECLARE_MESSAGE*() macros must declare: 141 * 142 * msg_arg_type__foo -- a typedef for the argument type of the foo message. 143 * msg_arg_consttype__foo -- a typedef for the const argument type of the 144 * foo message. 145 * msg_arg_name__foo[] -- a static string constant holding the unique 146 * identifier for the type of the foo message. 147 * msg_arg_get__foo() -- an inline function taking a msg_aux_data_t and 148 * returning the C data type. 149 * msg_arg_set__foo() -- an inline function taking a msg_aux_data_t and 150 * the C type, setting the msg_aux_data_t to hold the C type. 151 * 152 * For a messagename "foo", the DECLARE_PUBLISH() macro must declare: 153 * 154 * pub_binding__foo -- A static pub_binding_t object used to send messages 155 * from this module. 156 * publish_fn__foo -- A function taking an argument of the appropriate 157 * C type, to be invoked by PUBLISH(). 158 * 159 * For a messagename "foo", the DECLARE_SUBSCRIBE() macro must declare: 160 * 161 * hookfn -- A user-provided function name, with the correct signature. 162 * recv_fn__foo -- A wrapper callback that takes a msg_t *, and calls 163 * hookfn with the appropriate arguments. 164 */ 165 166 /** Macro to declare common elements shared by DECLARE_MESSAGE and 167 * DECLARE_MESSAGE_INT. Don't call this directly. 168 * 169 * Note that the "msg_arg_name" string constant is defined in each 170 * translation unit. This might be undesirable; we can tweak it in the 171 * future if need be. 172 */ 173 #define DECLARE_MESSAGE_COMMON__(messagename, typename, c_type) \ 174 typedef c_type msg_arg_type__ ##messagename; \ 175 typedef const c_type msg_arg_consttype__ ##messagename; \ 176 ATTR_UNUSED static const char msg_arg_name__ ##messagename[] = # typename; 177 178 /** 179 * Use this macro in a header to declare the existence of a given message, 180 * taking a pointer as auxiliary data. 181 * 182 * "messagename" is a unique identifier for the message; it must be a valid 183 * C identifier. 184 * 185 * "typename" is a unique identifier for the type of the auxiliary data. 186 * It needs to be defined somewhere in Tor, using 187 * "DISPATCH_REGISTER_TYPE." 188 * 189 * "c_ptr_type" is a C pointer type (like "char *" or "struct foo *"). 190 * The "*" needs to be included. 191 */ 192 #define DECLARE_MESSAGE(messagename, typename, c_ptr_type) \ 193 DECLARE_MESSAGE_COMMON__(messagename, typename, c_ptr_type) \ 194 ATTR_UNUSED static inline c_ptr_type \ 195 msg_arg_get__ ##messagename(msg_aux_data_t m) \ 196 { \ 197 return m.ptr; \ 198 } \ 199 ATTR_UNUSED static inline void \ 200 msg_arg_set__ ##messagename(msg_aux_data_t *m, c_ptr_type v) \ 201 { \ 202 m->ptr = v; \ 203 } \ 204 EAT_SEMICOLON 205 206 /** 207 * Use this macro in a header to declare the existence of a given message, 208 * taking an integer as auxiliary data. 209 * 210 * "messagename" is a unique identifier for the message; it must be a valid 211 * C identifier. 212 * 213 * "typename" is a unique identifier for the type of the auxiliary data. It 214 * needs to be defined somewhere in Tor, using "DISPATCH_REGISTER_TYPE." 215 * 216 * "c_type" is a C integer type, like "int" or "bool". It needs to fit inside 217 * a uint64_t. 218 */ 219 #define DECLARE_MESSAGE_INT(messagename, typename, c_type) \ 220 DECLARE_MESSAGE_COMMON__(messagename, typename, c_type) \ 221 ATTR_UNUSED static inline c_type \ 222 msg_arg_get__ ##messagename(msg_aux_data_t m) \ 223 { \ 224 return (c_type)m.u64; \ 225 } \ 226 ATTR_UNUSED static inline void \ 227 msg_arg_set__ ##messagename(msg_aux_data_t *m, c_type v) \ 228 { \ 229 m->u64 = (uint64_t)v; \ 230 } \ 231 EAT_SEMICOLON 232 233 /** 234 * Use this macro inside a C module declare that we'll be publishing a given 235 * message type from within this module. 236 * 237 * It creates necessary functions and wrappers to publish a message whose 238 * unique identifier is "messagename". 239 * 240 * Before you use this, you need to include the header where DECLARE_MESSAGE*() 241 * was used for this message. 242 * 243 * You can only use this once per message in each subsystem. 244 */ 245 #define DECLARE_PUBLISH(messagename) \ 246 static pub_binding_t pub_binding__ ##messagename; \ 247 static void \ 248 publish_fn__ ##messagename(msg_arg_type__ ##messagename arg) \ 249 { \ 250 msg_aux_data_t data; \ 251 msg_arg_set__ ##messagename(&data, arg); \ 252 pubsub_pub_(&pub_binding__ ##messagename, data); \ 253 } \ 254 EAT_SEMICOLON 255 256 /** 257 * Use this macro inside a C file to declare that we're subscribing to a 258 * given message and associating it with a given "hook function". It 259 * declares the hook function static, and helps with strong typing. 260 * 261 * Before you use this, you need to include the header where 262 * DECLARE_MESSAGE*() was used for the message whose unique identifier is 263 * "messagename". 264 * 265 * You will need to define a function with the name that you provide for 266 * "hookfn". The type of this function will be: 267 * static void hookfn(const msg_t *, const c_type) 268 * where c_type is the c type that you declared in the header. 269 * 270 * You can only use this once per message in each subsystem. 271 */ 272 #define DECLARE_SUBSCRIBE(messagename, hookfn) \ 273 static void hookfn(const msg_t *, \ 274 const msg_arg_consttype__ ##messagename); \ 275 static void recv_fn__ ## messagename(const msg_t *m) \ 276 { \ 277 msg_arg_type__ ## messagename arg; \ 278 arg = msg_arg_get__ ##messagename(m->aux_data__); \ 279 hookfn(m, arg); \ 280 } \ 281 EAT_SEMICOLON 282 283 /** 284 * Add a fake use of the publish function for 'messagename', so that 285 * the compiler does not call it unused. 286 */ 287 #define DISPATCH__FAKE_USE_OF_PUBFN_(messagename) \ 288 ( 0 ? (publish_fn__ ##messagename((msg_arg_type__##messagename)0), 1) \ 289 : 1) 290 291 /** 292 * This macro is for internal use. It backs DISPATCH_ADD_PUB*() 293 */ 294 #define DISPATCH_ADD_PUB_(connector, channel, messagename, flags) \ 295 ( \ 296 DISPATCH__FAKE_USE_OF_PUBFN_(messagename), \ 297 pubsub_add_pub_((connector), \ 298 &pub_binding__ ##messagename, \ 299 get_channel_id(# channel), \ 300 get_message_id(# messagename), \ 301 get_msg_type_id(msg_arg_name__ ## messagename), \ 302 (flags), \ 303 __FILE__, \ 304 __LINE__) \ 305 ) 306 307 /** 308 * Use a given connector and channel name to declare that this subsystem will 309 * publish a given message type. 310 * 311 * Call this macro from within the add_subscriptions() function of a module. 312 */ 313 #define DISPATCH_ADD_PUB(connector, channel, messagename) \ 314 DISPATCH_ADD_PUB_(connector, channel, messagename, 0) 315 316 /** 317 * Use a given connector and channel name to declare that this subsystem will 318 * publish a given message type, and that no other subsystem is allowed to. 319 * 320 * Call this macro from within the add_subscriptions() function of a module. 321 */ 322 #define DISPATCH_ADD_PUB_EXCL(connector, channel, messagename) \ 323 DISPATCH_ADD_PUB_(connector, channel, messagename, DISP_FLAG_EXCL) 324 325 /** 326 * This macro is for internal use. It backs DISPATCH_ADD_SUB*() 327 */ 328 #define DISPATCH_ADD_SUB_(connector, channel, messagename, flags) \ 329 pubsub_add_sub_((connector), \ 330 recv_fn__ ##messagename, \ 331 get_channel_id(#channel), \ 332 get_message_id(# messagename), \ 333 get_msg_type_id(msg_arg_name__ ##messagename), \ 334 (flags), \ 335 __FILE__, \ 336 __LINE__) 337 /** 338 * Use a given connector and channel name to declare that this subsystem will 339 * receive a given message type. 340 * 341 * Call this macro from within the add_subscriptions() function of a module. 342 */ 343 #define DISPATCH_ADD_SUB(connector, channel, messagename) \ 344 DISPATCH_ADD_SUB_(connector, channel, messagename, 0) 345 /** 346 * Use a given connector and channel name to declare that this subsystem will 347 * receive a given message type, and that no other subsystem is allowed to do 348 * so. 349 * 350 * Call this macro from within the add_subscriptions() function of a module. 351 */ 352 #define DISPATCH_ADD_SUB_EXCL(connector, channel, messagename) \ 353 DISPATCH_ADD_SUB_(connector, channel, messagename, DISP_FLAG_EXCL) 354 355 /** 356 * Publish a given message with a given argument. (Takes ownership of the 357 * argument if it is a pointer.) 358 */ 359 #define PUBLISH(messagename, arg) \ 360 publish_fn__ ##messagename(arg) 361 362 /** 363 * Use a given connector to declare that the functions to be used to manipuate 364 * a certain C type. 365 **/ 366 #define DISPATCH_REGISTER_TYPE(con, type, fns) \ 367 pubsub_connector_register_type_((con), \ 368 get_msg_type_id(#type), \ 369 (fns), \ 370 __FILE__, \ 371 __LINE__) 372 373 #endif /* !defined(TOR_DISPATCH_MSG_H) */