blob: 504a4c2f881c19c02de1669a5108433971a7c6e5 [file] [log] [blame]
Qiuhan Ding43c8e162015-02-02 15:16:48 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
Varun Patilb3d03b92021-01-08 17:09:32 +05303 * Copyright (c) 2013-2021, Regents of the University of California
Qiuhan Ding43c8e162015-02-02 15:16:48 -08004 *
5 * BSD license, See the LICENSE file for more information
6 *
7 * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
8 * Yingdi Yu <yingdi@cs.ucla.edu>
9 */
10
11#include "chatroom-discovery-backend.hpp"
Davide Pesavento7676b562020-12-14 00:41:26 -050012
Qiuhan Ding43c8e162015-02-02 15:16:48 -080013#include <QStringList>
14
Qiuhan Ding43c8e162015-02-02 15:16:48 -080015namespace chronochat {
16
17static const time::milliseconds FRESHNESS_PERIOD(60000);
18static const time::seconds REFRESH_INTERVAL(60);
19static const time::seconds HELLO_INTERVAL(60);
Davide Pesavento7676b562020-12-14 00:41:26 -050020static const Name::Component ROUTING_HINT_SEPARATOR = Name::Component::fromEscapedString("%F0%2E");
Qiuhan Ding43c8e162015-02-02 15:16:48 -080021// a count enforced when a manager himself find another one publish chatroom data
22static const int MAXIMUM_COUNT = 3;
23static const int IDENTITY_OFFSET = -1;
Yingdi Yuf3401182015-02-02 20:21:07 -080024static const int CONNECTION_RETRY_TIMER = 3;
Qiuhan Ding43c8e162015-02-02 15:16:48 -080025
26ChatroomDiscoveryBackend::ChatroomDiscoveryBackend(const Name& routingPrefix,
27 const Name& identity,
28 QObject* parent)
29 : QThread(parent)
Yingdi Yuf3401182015-02-02 20:21:07 -080030 , m_shouldResume(false)
Qiuhan Ding43c8e162015-02-02 15:16:48 -080031 , m_routingPrefix(routingPrefix)
32 , m_identity(identity)
33 , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
34 , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(500,2000))
Qiuhan Ding43c8e162015-02-02 15:16:48 -080035{
36 m_discoveryPrefix.append("ndn")
37 .append("broadcast")
38 .append("ChronoChat")
39 .append("Discovery");
40 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
41 updatePrefixes();
42}
43
44ChatroomDiscoveryBackend::~ChatroomDiscoveryBackend()
45{
46}
47
48void
49ChatroomDiscoveryBackend::run()
50{
51 bool shouldResume = false;
52 do {
53 initializeSync();
54
55 if (m_face == nullptr)
56 break;
57
Yingdi Yuf3401182015-02-02 20:21:07 -080058 try {
59 m_face->getIoService().run();
60 }
Varun Patila24bd3e2020-11-24 10:08:33 +053061 catch (const std::runtime_error& e) {
Yingdi Yuf3401182015-02-02 20:21:07 -080062 {
63 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
64 m_isNfdConnected = false;
65 }
66 emit nfdError();
67 {
68 std::lock_guard<std::mutex>lock(m_resumeMutex);
69 m_shouldResume = true;
70 }
71#ifdef BOOST_THREAD_USES_CHRONO
72 time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
73#else
74 boost::posix_time::time_duration reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
75#endif
76 while (!m_isNfdConnected) {
77#ifdef BOOST_THREAD_USES_CHRONO
78 boost::this_thread::sleep_for(reconnectTimer);
79#else
80 boost::this_thread::sleep(reconnectTimer);
81#endif
82 }
83 }
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070084 {
Yingdi Yuf3401182015-02-02 20:21:07 -080085 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070086 shouldResume = m_shouldResume;
87 m_shouldResume = false;
88 }
Yingdi Yuf3401182015-02-02 20:21:07 -080089 close();
Qiuhan Ding43c8e162015-02-02 15:16:48 -080090
91 } while (shouldResume);
Qiuhan Ding43c8e162015-02-02 15:16:48 -080092}
93
94void
95ChatroomDiscoveryBackend::initializeSync()
96{
97 BOOST_ASSERT(m_sock == nullptr);
98
99 m_face = shared_ptr<ndn::Face>(new ndn::Face);
100 m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
101
Varun Patilb3d03b92021-01-08 17:09:32 +0530102 m_sock = std::make_shared<chronosync::Socket>(m_discoveryPrefix,
103 Name(),
104 ref(*m_face),
105 bind(&ChatroomDiscoveryBackend::processSyncUpdate,
106 this, _1));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800107
108 // add an timer to refresh front end
Varun Patil3d850902020-11-23 12:19:14 +0530109 m_refreshPanelId = m_scheduler->schedule(REFRESH_INTERVAL,
110 [this] { sendChatroomList(); });
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800111}
112
113void
114ChatroomDiscoveryBackend::close()
115{
116 m_scheduler->cancelAllEvents();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800117 m_chatroomList.clear();
118 m_sock.reset();
119}
120
121void
122ChatroomDiscoveryBackend::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates)
123{
124 if (updates.empty()) {
125 return;
126 }
127 for (const auto& update : updates) {
128 m_sock->fetchData(update.session, update.high,
129 bind(&ChatroomDiscoveryBackend::processChatroomData, this, _1), 2);
130 }
131}
132
133void
Varun Patil3d850902020-11-23 12:19:14 +0530134ChatroomDiscoveryBackend::processChatroomData(const ndn::Data& data)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800135{
136 // extract chatroom name by get(-3)
Varun Patil3d850902020-11-23 12:19:14 +0530137 Name::Component chatroomName = data.getName().get(-3);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800138 auto it = m_chatroomList.find(chatroomName);
139 if (it == m_chatroomList.end()) {
140 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
141 m_chatroomList[chatroomName].count = 0;
142 m_chatroomList[chatroomName].isPrint = false;
143 m_chatroomList[chatroomName].isParticipant = false;
144 m_chatroomList[chatroomName].isManager = false;
145 it = m_chatroomList.find(chatroomName);
146 }
147 // If the user is the manager of this chatroom, he should not receive any data from this chatroom
148 if (it->second.isManager) {
149 if (it->second.count < MAXIMUM_COUNT) {
150 it->second.count++;
151 return;
152 }
153 else {
154 it->second.count = 0;
Varun Patil3d850902020-11-23 12:19:14 +0530155 if (m_routableUserDiscoveryPrefix < data.getName()) {
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800156 // when two managers exist, the one with "smaller" name take the control
157 sendUpdate(chatroomName);
158 return;
159 }
160 else {
Varun Patila24bd3e2020-11-24 10:08:33 +0530161 if (it->second.helloTimeoutEventId)
Varun Patil3d850902020-11-23 12:19:14 +0530162 it->second.helloTimeoutEventId.cancel();
Varun Patila24bd3e2020-11-24 10:08:33 +0530163
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800164 it->second.isManager = false;
165 }
166
167 }
168 }
169
170 else if (it->second.isParticipant) {
Varun Patil3d850902020-11-23 12:19:14 +0530171 if (it->second.localChatroomTimeoutEventId)
172 it->second.localChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800173
174 // If a user start a random timer it means that he think his own chatroom is not alive
175 // But when he receive some packet, it means that this chatroom is alive, so he can
176 // cancel the timer
Varun Patil3d850902020-11-23 12:19:14 +0530177 if (it->second.managerSelectionTimeoutEventId)
178 it->second.managerSelectionTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800179
180 it->second.localChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530181 m_scheduler->schedule(HELLO_INTERVAL * 3,
182 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
183 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800184 }
185 else {
Varun Patil3d850902020-11-23 12:19:14 +0530186 if (data.hasContent()) {
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800187 ChatroomInfo chatroom;
Varun Patil3d850902020-11-23 12:19:14 +0530188 chatroom.wireDecode(data.getContent().blockFromValue());
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800189 it->second.info = chatroom;
190 }
191
Varun Patil3d850902020-11-23 12:19:14 +0530192 if (it->second.remoteChatroomTimeoutEventId)
193 it->second.remoteChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800194
195 it->second.remoteChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530196 m_scheduler->schedule(HELLO_INTERVAL * 5,
197 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
198 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800199 }
200 // if this is a chatroom that haven't been print on the discovery panel, print it.
201 if(!it->second.isPrint) {
202 sendChatroomList();
203 it->second.isPrint = true;
204 }
205}
206
207void
208ChatroomDiscoveryBackend::localSessionTimeout(const Name::Component& chatroomName)
209{
210 auto it = m_chatroomList.find(chatroomName);
211 if (it == m_chatroomList.end() || it->second.isParticipant == false)
212 return;
213 it->second.managerSelectionTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530214 m_scheduler->schedule(time::milliseconds(m_rangeUniformRandom()),
215 bind(&ChatroomDiscoveryBackend::randomSessionTimeout,
216 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800217}
218
219void
220ChatroomDiscoveryBackend::remoteSessionTimeout(const Name::Component& chatroomName)
221{
222 m_chatroomList.erase(chatroomName);
223}
224
225void
226ChatroomDiscoveryBackend::randomSessionTimeout(const Name::Component& chatroomName)
227{
228 Name prefix = m_routableUserDiscoveryPrefix;
229 prefix.append(chatroomName);
230 m_sock->addSyncNode(prefix);
231
232 emit chatroomInfoRequest(chatroomName.toUri(), true);
233}
234
235void
236ChatroomDiscoveryBackend::sendUpdate(const Name::Component& chatroomName)
237{
238 auto it = m_chatroomList.find(chatroomName);
239 if (it != m_chatroomList.end() && it->second.isManager) {
240 ndn::Block buf = it->second.info.wireEncode();
241
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800242 m_sock->publishData(buf.wire(), buf.size(), FRESHNESS_PERIOD, it->second.chatroomPrefix);
243
244 it->second.helloTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530245 m_scheduler->schedule(HELLO_INTERVAL,
246 bind(&ChatroomDiscoveryBackend::sendUpdate, this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800247 // if this is a chatroom that haven't been print on the discovery panel, print it.
248 if(!it->second.isPrint) {
249 sendChatroomList();
250 it->second.isPrint = true;
251 }
252 }
253}
254
255void
256ChatroomDiscoveryBackend::updatePrefixes()
257{
258 Name temp;
259 if (m_routingPrefix.isPrefixOf(m_userDiscoveryPrefix))
260 temp = m_userDiscoveryPrefix;
261 else
262 temp.append(m_routingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800263 .append(ROUTING_HINT_SEPARATOR)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800264 .append(m_userDiscoveryPrefix);
265
266 Name routableIdentity = m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET);
267 for (auto& chatroom : m_chatroomList) {
268 if (chatroom.second.isParticipant) {
269 chatroom.second.info.removeParticipant(routableIdentity);
270 chatroom.second.info.addParticipant(temp.getPrefix(IDENTITY_OFFSET));
271 }
272 }
273 m_routableUserDiscoveryPrefix = temp;
274}
275
276void
277ChatroomDiscoveryBackend::updateRoutingPrefix(const QString& routingPrefix)
278{
279 Name newRoutingPrefix(routingPrefix.toStdString());
280 if (!newRoutingPrefix.empty() && newRoutingPrefix != m_routingPrefix) {
281 // Update localPrefix
282 m_routingPrefix = newRoutingPrefix;
283
284 updatePrefixes();
285
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700286 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800287 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700288 m_shouldResume = true;
289 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800290
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800291 m_face->getIoService().stop();
292 }
293}
294
295void
296ChatroomDiscoveryBackend::onEraseInRoster(ndn::Name sessionPrefix,
297 ndn::Name::Component chatroomName)
298{
299 auto it = m_chatroomList.find(chatroomName);
300 if (it != m_chatroomList.end()) {
301 it->second.info.removeParticipant(sessionPrefix);
302 if (it->second.info.getParticipants().size() == 0) {
303 // Before deleting the chatroom, cancel the hello event timer if exist
Varun Patil3d850902020-11-23 12:19:14 +0530304 if (it->second.helloTimeoutEventId)
305 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800306
307 m_chatroomList.erase(chatroomName);
308 Name prefix = sessionPrefix;
309 prefix.append("CHRONOCHAT-DISCOVERYDATA").append(chatroomName);
310 m_sock->removeSyncNode(prefix);
311 sendChatroomList();
312 return;
313 }
314
315 if (sessionPrefix.isPrefixOf(m_routableUserDiscoveryPrefix)) {
316 it->second.isParticipant = false;
317 it->second.isManager = false;
318 it->second.isPrint = false;
319 it->second.count = 0;
Varun Patil3d850902020-11-23 12:19:14 +0530320 if (it->second.helloTimeoutEventId)
321 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800322
Varun Patil3d850902020-11-23 12:19:14 +0530323 if (it->second.localChatroomTimeoutEventId)
324 it->second.localChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800325
326 it->second.remoteChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530327 m_scheduler->schedule(HELLO_INTERVAL * 5,
328 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
329 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800330 }
331
332 if (it->second.isManager) {
333 sendUpdate(chatroomName);
334 }
335 }
336}
337
338void
339ChatroomDiscoveryBackend::onAddInRoster(ndn::Name sessionPrefix,
340 ndn::Name::Component chatroomName)
341{
342 auto it = m_chatroomList.find(chatroomName);
343 if (it != m_chatroomList.end()) {
344 it->second.info.addParticipant(sessionPrefix);
345 if (it->second.isManager)
346 sendUpdate(chatroomName);
347 }
348 else {
Yingdi Yuf3401182015-02-02 20:21:07 -0800349 onNewChatroomForDiscovery(chatroomName);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800350 }
351}
352
353void
Yingdi Yuf3401182015-02-02 20:21:07 -0800354ChatroomDiscoveryBackend::onNewChatroomForDiscovery(ndn::Name::Component chatroomName)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800355{
356 Name newPrefix = m_routableUserDiscoveryPrefix;
357 newPrefix.append(chatroomName);
358 auto it = m_chatroomList.find(chatroomName);
359 if (it == m_chatroomList.end()) {
360 m_chatroomList[chatroomName].chatroomPrefix = newPrefix;
361 m_chatroomList[chatroomName].isParticipant = true;
362 m_chatroomList[chatroomName].isManager = false;
363 m_chatroomList[chatroomName].count = 0;
364 m_chatroomList[chatroomName].isPrint = false;
Varun Patil3d850902020-11-23 12:19:14 +0530365 m_scheduler->schedule(time::milliseconds(600),
366 bind(&ChatroomDiscoveryBackend::randomSessionTimeout, this,
367 chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800368 }
369 else {
370 // Entering an existing chatroom
371 it->second.isParticipant = true;
372 it->second.isManager = false;
373 it->second.chatroomPrefix = newPrefix;
374
Varun Patil3d850902020-11-23 12:19:14 +0530375 if (it->second.remoteChatroomTimeoutEventId)
376 it->second.remoteChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800377 it->second.isPrint = false;
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800378
379 it->second.localChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530380 m_scheduler->schedule(HELLO_INTERVAL * 3,
381 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
382 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800383 emit chatroomInfoRequest(chatroomName.toUri(), false);
384 }
385}
386
387void
388ChatroomDiscoveryBackend::onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager)
389{
390 if (isManager)
391 chatroomInfo.setManager(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET));
392 Name::Component chatroomName = chatroomInfo.getName();
393 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
394 m_chatroomList[chatroomName].isManager = isManager;
395 m_chatroomList[chatroomName].count = 0;
396 m_chatroomList[chatroomName].info = chatroomInfo;
397 sendChatroomList();
398 onAddInRoster(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET), chatroomName);
399}
400
401void
402ChatroomDiscoveryBackend::onIdentityUpdated(const QString& identity)
403{
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800404 m_identity = Name(identity.toStdString());
405 m_userDiscoveryPrefix.clear();
406 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
407 updatePrefixes();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800408}
409
410void
411ChatroomDiscoveryBackend::sendChatroomList()
412{
413 QStringList chatroomList;
414 for (const auto& chatroom : m_chatroomList) {
415 chatroomList << QString::fromStdString(chatroom.first.toUri());
416 }
417
418 emit chatroomListReady(chatroomList);
Varun Patil3d850902020-11-23 12:19:14 +0530419 m_refreshPanelId = m_scheduler->schedule(REFRESH_INTERVAL,
420 [this] { sendChatroomList(); });
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800421}
422
423void
424ChatroomDiscoveryBackend::onWaitForChatroomInfo(const QString& chatroomName)
425{
426 auto chatroom = m_chatroomList.find(Name::Component(chatroomName.toStdString()));
427 if (chatroom != m_chatroomList.end())
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800428 emit chatroomInfoReady(chatroom->second.info, chatroom->second.isParticipant);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800429}
430
431void
432ChatroomDiscoveryBackend::shutdown()
433{
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700434 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800435 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700436 m_shouldResume = false;
437 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800438
Yingdi Yuf3401182015-02-02 20:21:07 -0800439 {
440 // In this case, we just stop checking the nfd connection and exit
441 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
442 m_isNfdConnected = true;
443 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800444
445 m_face->getIoService().stop();
446}
447
Yingdi Yuf3401182015-02-02 20:21:07 -0800448void
449ChatroomDiscoveryBackend::onNfdReconnect()
450{
451 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
452 m_isNfdConnected = true;
453}
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800454
455} // namespace chronochat
456
457#if WAF
458#include "chatroom-discovery-backend.moc"
459#endif