drogon
C++14/17-based HTTP application framework
Loading...
Searching...
No Matches
PubSubService.h
Go to the documentation of this file.
1
14
15#pragma once
16
17#include <trantor/utils/NonCopyable.h>
18#include <functional>
19#include <mutex>
20#include <shared_mutex>
21#include <string>
22#include <memory>
23#include <unordered_map>
24
25namespace drogon
26{
27using SubscriberID = uint64_t;
28
34template <typename MessageType>
35class Topic : public trantor::NonCopyable
36{
37 public:
38 using MessageHandler = std::function<void(const MessageType &)>;
39#if __cplusplus >= 201703L | defined _WIN32
40 using SharedMutex = std::shared_mutex;
41#else
42 using SharedMutex = std::shared_timed_mutex;
43#endif
50 void publish(const MessageType &message) const
51 {
52 std::shared_lock<SharedMutex> lock(mutex_);
53 for (auto &pair : handlersMap_)
54 {
55 pair.second(message);
56 }
57 }
58
65 SubscriberID subscribe(const MessageHandler &handler)
66 {
67 std::unique_lock<SharedMutex> lock(mutex_);
68 handlersMap_[++id_] = handler;
69 return id_;
70 }
71
78 SubscriberID subscribe(MessageHandler &&handler)
79 {
80 std::unique_lock<SharedMutex> lock(mutex_);
81 handlersMap_[++id_] = std::move(handler);
82 return id_;
83 }
84
88 void unsubscribe(SubscriberID id)
89 {
90 std::unique_lock<SharedMutex> lock(mutex_);
91 handlersMap_.erase(id);
92 }
93
100 bool empty() const
101 {
102 std::shared_lock<SharedMutex> lock(mutex_);
103 return handlersMap_.empty();
104 }
105
110 void clear()
111 {
112 std::unique_lock<SharedMutex> lock(mutex_);
113 handlersMap_.clear();
114 }
115
116 private:
117 std::unordered_map<SubscriberID, MessageHandler> handlersMap_;
118 mutable SharedMutex mutex_;
119 SubscriberID id_{0};
120};
121
128template <typename MessageType>
129class PubSubService : public trantor::NonCopyable
130{
131 public:
132 using MessageHandler =
133 std::function<void(const std::string &, const MessageType &)>;
134#if __cplusplus >= 201703L | defined _WIN32
135 using SharedMutex = std::shared_mutex;
136#else
137 using SharedMutex = std::shared_timed_mutex;
138#endif
139
144 void publish(const std::string &topicName, const MessageType &message) const
145 {
146 std::shared_ptr<Topic<MessageType>> topicPtr;
147 {
148 std::shared_lock<SharedMutex> lock(mutex_);
149 auto iter = topicMap_.find(topicName);
150 if (iter != topicMap_.end())
151 {
152 topicPtr = iter->second;
153 }
154 else
155 {
156 return;
157 }
158 }
159 topicPtr->publish(message);
160 }
161
166 SubscriberID subscribe(const std::string &topicName,
167 const MessageHandler &handler)
168 {
169 auto topicHandler = [topicName, handler](const MessageType &message) {
170 handler(topicName, message);
171 };
172 return subscribeToTopic(topicName, std::move(topicHandler));
173 }
174
182 SubscriberID subscribe(const std::string &topicName,
183 MessageHandler &&handler)
184 {
185 auto topicHandler = [topicName, handler = std::move(handler)](
186 const MessageType &message) {
187 handler(topicName, message);
188 };
189 return subscribeToTopic(topicName, std::move(topicHandler));
190 }
191
198 void unsubscribe(const std::string &topicName, SubscriberID id)
199 {
200 {
201 std::shared_lock<SharedMutex> lock(mutex_);
202 auto iter = topicMap_.find(topicName);
203 if (iter == topicMap_.end())
204 {
205 return;
206 }
207 iter->second->unsubscribe(id);
208 if (!iter->second->empty())
209 return;
210 }
211 std::unique_lock<SharedMutex> lock(mutex_);
212 auto iter = topicMap_.find(topicName);
213 if (iter == topicMap_.end())
214 {
215 return;
216 }
217 if (iter->second->empty())
218 topicMap_.erase(iter);
219 }
220
224 size_t size() const
225 {
226 std::shared_lock<SharedMutex> lock(mutex_);
227 return topicMap_.size();
228 }
229
233 void clear()
234 {
235 std::unique_lock<SharedMutex> lock(mutex_);
236 topicMap_.clear();
237 }
238
243 void removeTopic(const std::string &topicName)
244 {
245 std::unique_lock<SharedMutex> lock(mutex_);
246 topicMap_.erase(topicName);
247 }
248
256 bool isTopicEmpty(const std::string &topicName) const
257 {
258 std::shared_ptr<Topic<MessageType>> topicPtr;
259 {
260 std::shared_lock<SharedMutex> lock(mutex_);
261 auto iter = topicMap_.find(topicName);
262 if (iter != topicMap_.end())
263 {
264 topicPtr = iter->second;
265 }
266 else
267 {
268 return true;
269 }
270 }
271 return topicPtr->empty();
272 }
273
274 private:
275 std::unordered_map<std::string, std::shared_ptr<Topic<MessageType>>>
276 topicMap_;
277 mutable SharedMutex mutex_;
278 SubscriberID subID_ = 0;
279
280 SubscriberID subscribeToTopic(
281 const std::string &topicName,
282 typename Topic<MessageType>::MessageHandler &&handler)
283 {
284 {
285 std::shared_lock<SharedMutex> lock(mutex_);
286 auto iter = topicMap_.find(topicName);
287 if (iter != topicMap_.end())
288 {
289 return iter->second->subscribe(std::move(handler));
290 }
291 }
292 std::unique_lock<SharedMutex> lock(mutex_);
293 auto iter = topicMap_.find(topicName);
294 if (iter != topicMap_.end())
295 {
296 return iter->second->subscribe(std::move(handler));
297 }
298 auto topicPtr = std::make_shared<Topic<MessageType>>();
299 auto id = topicPtr->subscribe(std::move(handler));
300 topicMap_[topicName] = std::move(topicPtr);
301 return id;
302 }
303};
304} // namespace drogon
This class template implements a publish-subscribe pattern with multiple named topics.
Definition PubSubService.h:130
bool isTopicEmpty(const std::string &topicName) const
Check if a topic is empty.
Definition PubSubService.h:256
SubscriberID subscribe(const std::string &topicName, MessageHandler &&handler)
Subscribe to a topic. When a message is published to the topic, the handler is invoked by passing the...
Definition PubSubService.h:182
void clear()
remove all topics.
Definition PubSubService.h:233
void publish(const std::string &topicName, const MessageType &message) const
Publish a message to a topic. The message will be broadcasted to every subscriber.
Definition PubSubService.h:144
SubscriberID subscribe(const std::string &topicName, const MessageHandler &handler)
Subscribe to a topic. When a message is published to the topic, the handler is invoked by passing the...
Definition PubSubService.h:166
void unsubscribe(const std::string &topicName, SubscriberID id)
Unsubscribe from a topic.
Definition PubSubService.h:198
size_t size() const
return the number of topics.
Definition PubSubService.h:224
void removeTopic(const std::string &topicName)
Remove a topic.
Definition PubSubService.h:243
This class template presents an unnamed topic.
Definition PubSubService.h:36
void clear()
Remove all subscribers from the topic.
Definition PubSubService.h:110
bool empty() const
Check if the topic is empty.
Definition PubSubService.h:100
SubscriberID subscribe(const MessageHandler &handler)
Subscribe to the topic.
Definition PubSubService.h:65
void unsubscribe(SubscriberID id)
Unsubscribe from the topic.
Definition PubSubService.h:88
void publish(const MessageType &message) const
Publish a message, every subscriber in the topic will receive the message.
Definition PubSubService.h:50
SubscriberID subscribe(MessageHandler &&handler)
Subscribe to the topic.
Definition PubSubService.h:78
Drogon Test is a minimal effort test framework developed because the major C++ test frameworks doesn'...
Definition Attribute.h:23