pubsub.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /* Copyright (c) 2016-2017, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. /**
  4. * \file pubsub.h
  5. * \brief Macros to implement publish/subscribe abstractions.
  6. *
  7. * To use these macros, call DECLARE_PUBSUB_TOPIC() with an identifier to use
  8. * as your topic. Below, I'm going to assume you say DECLARE_PUBSUB_TOPIC(T).
  9. *
  10. * Doing this will declare the following types:
  11. * typedef struct T_event_data_t T_event_data_t; // you define this struct
  12. * typedef struct T_subscriber_data_t T_subscriber_data_t; // this one too.
  13. * typedef struct T_subscriber_t T_subscriber_t; // opaque
  14. * typedef int (*T_subscriber_fn_t)(T_event_data_t*, T_subscriber_data_t*);
  15. *
  16. * and it will declare the following functions:
  17. * const T_subscriber_t *T_subscribe(T_subscriber_fn_t,
  18. * T_subscriber_data_t *,
  19. * unsigned flags,
  20. * unsigned priority);
  21. * int T_unsubscribe(const T_subscriber_t *)
  22. *
  23. * Elsewhere you can say DECLARE_NOTIFY_PUBSUB_TOPIC(static, T), which
  24. * declares:
  25. *
  26. * static int T_notify(T_event_data_t *, unsigned notify_flags);
  27. * static void T_clear(void);
  28. *
  29. * And in some C file, you would define these functions with:
  30. * IMPLEMENT_PUBSUB_TOPIC(static, T).
  31. *
  32. * The implementations will be small typesafe wrappers over generic versions
  33. * of the above functions.
  34. *
  35. * To use the typesafe functions, you add any number of subscribers with
  36. * T_subscribe(). Each has an associated function pointer, data pointer,
  37. * and priority. Later, you can invoke T_notify() to declare that the
  38. * event has occurred. Each of the subscribers will be invoked once.
  39. **/
  40. #ifndef TOR_PUBSUB_H
  41. #define TOR_PUBSUB_H
  42. #include "torint.h"
  43. /**
  44. * Flag for T_subscribe: die with an assertion failure if the event
  45. * have ever been published before. Used when a subscriber must absolutely
  46. * never have missed an event.
  47. */
  48. #define SUBSCRIBE_ATSTART (1u<<0)
  49. #define DECLARE_PUBSUB_STRUCT_TYPES(name) \
  50. /* You define this type. */ \
  51. typedef struct name ## _event_data_t name ## _event_data_t; \
  52. /* You define this type. */ \
  53. typedef struct name ## _subscriber_data_t name ## _subscriber_data_t;
  54. #define DECLARE_PUBSUB_TOPIC(name) \
  55. /* This type is opaque. */ \
  56. typedef struct name ## _subscriber_t name ## _subscriber_t; \
  57. /* You declare functions matching this type. */ \
  58. typedef int (*name ## _subscriber_fn_t)( \
  59. name ## _event_data_t *data, \
  60. name ## _subscriber_data_t *extra); \
  61. /* Call this function to subscribe to a topic. */ \
  62. const name ## _subscriber_t *name ## _subscribe( \
  63. name##_subscriber_fn_t subscriber, \
  64. name##_subscriber_data_t *extra_data, \
  65. unsigned flags, \
  66. unsigned priority); \
  67. /* Call this function to unsubscribe from a topic. */ \
  68. int name ## _unsubscribe(const name##_subscriber_t *s);
  69. #define DECLARE_NOTIFY_PUBSUB_TOPIC(linkage, name) \
  70. /* Call this function to notify all subscribers. Flags not yet used. */ \
  71. linkage int name ## _notify(name ## _event_data_t *data, unsigned flags); \
  72. /* Call this function to release storage held by the topic. */ \
  73. linkage void name ## _clear(void);
  74. /**
  75. * Type used to hold a generic function for a subscriber.
  76. *
  77. * [Yes, it is safe to cast to this, so long as we cast back to the original
  78. * type before calling. From C99: "A pointer to a function of one type may be
  79. * converted to a pointer to a function of another type and back again; the
  80. * result shall compare equal to the original pointer."]
  81. */
  82. typedef int (*pubsub_subscriber_fn_t)(void *, void *);
  83. /**
  84. * Helper type to implement pubsub abstraction. Don't use this directly.
  85. * It represents a subscriber.
  86. */
  87. typedef struct pubsub_subscriber_t {
  88. /** Function to invoke when the event triggers. */
  89. pubsub_subscriber_fn_t fn;
  90. /** Data associated with this subscriber. */
  91. void *subscriber_data;
  92. /** Priority for this subscriber. Low priorities happen first. */
  93. unsigned priority;
  94. /** Flags set on this subscriber. Not yet used.*/
  95. unsigned subscriber_flags;
  96. } pubsub_subscriber_t;
  97. /**
  98. * Helper type to implement pubsub abstraction. Don't use this directly.
  99. * It represents a topic, and keeps a record of subscribers.
  100. */
  101. typedef struct pubsub_topic_t {
  102. /** List of subscribers to this topic. May be NULL. */
  103. struct smartlist_t *subscribers;
  104. /** Total number of times that pubsub_notify_() has ever been called on this
  105. * topic. */
  106. uint64_t n_events_fired;
  107. /** True iff we're running 'notify' on this topic, and shouldn't allow
  108. * any concurrent modifications or events. */
  109. unsigned locked;
  110. } pubsub_topic_t;
  111. const pubsub_subscriber_t *pubsub_subscribe_(pubsub_topic_t *topic,
  112. pubsub_subscriber_fn_t fn,
  113. void *subscriber_data,
  114. unsigned subscribe_flags,
  115. unsigned priority);
  116. int pubsub_unsubscribe_(pubsub_topic_t *topic, const pubsub_subscriber_t *sub);
  117. void pubsub_clear_(pubsub_topic_t *topic);
  118. typedef int (*pubsub_notify_fn_t)(pubsub_subscriber_t *subscriber,
  119. void *notify_data);
  120. int pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn,
  121. void *notify_data, unsigned notify_flags);
  122. #define IMPLEMENT_PUBSUB_TOPIC(notify_linkage, name) \
  123. static pubsub_topic_t name ## _topic_ = { NULL, 0, 0 }; \
  124. const name ## _subscriber_t * \
  125. name ## _subscribe(name##_subscriber_fn_t subscriber, \
  126. name##_subscriber_data_t *extra_data, \
  127. unsigned flags, \
  128. unsigned priority) \
  129. { \
  130. const pubsub_subscriber_t *s; \
  131. s = pubsub_subscribe_(&name##_topic_, \
  132. (pubsub_subscriber_fn_t)subscriber, \
  133. extra_data, \
  134. flags, \
  135. priority); \
  136. return (const name##_subscriber_t *)s; \
  137. } \
  138. int \
  139. name ## _unsubscribe(const name##_subscriber_t *subscriber) \
  140. { \
  141. return pubsub_unsubscribe_(&name##_topic_, \
  142. (const pubsub_subscriber_t *)subscriber); \
  143. } \
  144. static int \
  145. name##_call_the_notify_fn_(pubsub_subscriber_t *subscriber, \
  146. void *notify_data) \
  147. { \
  148. name ## _subscriber_fn_t fn; \
  149. fn = (name ## _subscriber_fn_t) subscriber->fn; \
  150. return fn(notify_data, subscriber->subscriber_data); \
  151. } \
  152. notify_linkage int \
  153. name ## _notify(name ## _event_data_t *event_data, unsigned flags) \
  154. { \
  155. return pubsub_notify_(&name##_topic_, \
  156. name##_call_the_notify_fn_, \
  157. event_data, \
  158. flags); \
  159. } \
  160. notify_linkage void \
  161. name ## _clear(void) \
  162. { \
  163. pubsub_clear_(&name##_topic_); \
  164. }
  165. #endif /* TOR_PUBSUB_H */