blob: 3f92e01fab187544b552297e4aea3dc6db6d7db1 [file] [log] [blame]
Qiuhan Ding43c8e162015-02-02 15:16:48 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
Varun Patila24bd3e2020-11-24 10:08:33 +05303 * Copyright (c) 2020, 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"
12#include <QStringList>
13
Qiuhan Ding43c8e162015-02-02 15:16:48 -080014#ifndef Q_MOC_RUN
15
16#endif
17
18namespace chronochat {
19
20static const time::milliseconds FRESHNESS_PERIOD(60000);
21static const time::seconds REFRESH_INTERVAL(60);
22static const time::seconds HELLO_INTERVAL(60);
Qiuhan Dingba3e57a2015-01-08 19:07:39 -080023static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
24 ndn::name::Component::fromEscapedString("%F0%2E");
Qiuhan Ding43c8e162015-02-02 15:16:48 -080025// a count enforced when a manager himself find another one publish chatroom data
26static const int MAXIMUM_COUNT = 3;
27static const int IDENTITY_OFFSET = -1;
Yingdi Yuf3401182015-02-02 20:21:07 -080028static const int CONNECTION_RETRY_TIMER = 3;
Qiuhan Ding43c8e162015-02-02 15:16:48 -080029
30ChatroomDiscoveryBackend::ChatroomDiscoveryBackend(const Name& routingPrefix,
31 const Name& identity,
32 QObject* parent)
33 : QThread(parent)
Yingdi Yuf3401182015-02-02 20:21:07 -080034 , m_shouldResume(false)
Qiuhan Ding43c8e162015-02-02 15:16:48 -080035 , m_routingPrefix(routingPrefix)
36 , m_identity(identity)
37 , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
38 , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(500,2000))
Qiuhan Ding43c8e162015-02-02 15:16:48 -080039{
40 m_discoveryPrefix.append("ndn")
41 .append("broadcast")
42 .append("ChronoChat")
43 .append("Discovery");
44 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
45 updatePrefixes();
46}
47
48ChatroomDiscoveryBackend::~ChatroomDiscoveryBackend()
49{
50}
51
52void
53ChatroomDiscoveryBackend::run()
54{
55 bool shouldResume = false;
56 do {
57 initializeSync();
58
59 if (m_face == nullptr)
60 break;
61
Yingdi Yuf3401182015-02-02 20:21:07 -080062 try {
63 m_face->getIoService().run();
64 }
Varun Patila24bd3e2020-11-24 10:08:33 +053065 catch (const std::runtime_error& e) {
Yingdi Yuf3401182015-02-02 20:21:07 -080066 {
67 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
68 m_isNfdConnected = false;
69 }
70 emit nfdError();
71 {
72 std::lock_guard<std::mutex>lock(m_resumeMutex);
73 m_shouldResume = true;
74 }
75#ifdef BOOST_THREAD_USES_CHRONO
76 time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
77#else
78 boost::posix_time::time_duration reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
79#endif
80 while (!m_isNfdConnected) {
81#ifdef BOOST_THREAD_USES_CHRONO
82 boost::this_thread::sleep_for(reconnectTimer);
83#else
84 boost::this_thread::sleep(reconnectTimer);
85#endif
86 }
87 }
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070088 {
Yingdi Yuf3401182015-02-02 20:21:07 -080089 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070090 shouldResume = m_shouldResume;
91 m_shouldResume = false;
92 }
Yingdi Yuf3401182015-02-02 20:21:07 -080093 close();
Qiuhan Ding43c8e162015-02-02 15:16:48 -080094
95 } while (shouldResume);
96
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070097 std::cerr << "DiscoveryBackend: Bye!" << std::endl;
Qiuhan Ding43c8e162015-02-02 15:16:48 -080098}
99
100void
101ChatroomDiscoveryBackend::initializeSync()
102{
103 BOOST_ASSERT(m_sock == nullptr);
104
105 m_face = shared_ptr<ndn::Face>(new ndn::Face);
106 m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
107
108 m_sock = make_shared<chronosync::Socket>(m_discoveryPrefix,
109 Name(),
110 ref(*m_face),
111 bind(&ChatroomDiscoveryBackend::processSyncUpdate,
112 this, _1));
113
114 // add an timer to refresh front end
Varun Patil3d850902020-11-23 12:19:14 +0530115 m_refreshPanelId = m_scheduler->schedule(REFRESH_INTERVAL,
116 [this] { sendChatroomList(); });
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800117}
118
119void
120ChatroomDiscoveryBackend::close()
121{
122 m_scheduler->cancelAllEvents();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800123 m_chatroomList.clear();
124 m_sock.reset();
125}
126
127void
128ChatroomDiscoveryBackend::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates)
129{
130 if (updates.empty()) {
131 return;
132 }
133 for (const auto& update : updates) {
134 m_sock->fetchData(update.session, update.high,
135 bind(&ChatroomDiscoveryBackend::processChatroomData, this, _1), 2);
136 }
137}
138
139void
Varun Patil3d850902020-11-23 12:19:14 +0530140ChatroomDiscoveryBackend::processChatroomData(const ndn::Data& data)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800141{
142 // extract chatroom name by get(-3)
Varun Patil3d850902020-11-23 12:19:14 +0530143 Name::Component chatroomName = data.getName().get(-3);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800144 auto it = m_chatroomList.find(chatroomName);
145 if (it == m_chatroomList.end()) {
146 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
147 m_chatroomList[chatroomName].count = 0;
148 m_chatroomList[chatroomName].isPrint = false;
149 m_chatroomList[chatroomName].isParticipant = false;
150 m_chatroomList[chatroomName].isManager = false;
151 it = m_chatroomList.find(chatroomName);
152 }
153 // If the user is the manager of this chatroom, he should not receive any data from this chatroom
154 if (it->second.isManager) {
155 if (it->second.count < MAXIMUM_COUNT) {
156 it->second.count++;
157 return;
158 }
159 else {
160 it->second.count = 0;
Varun Patil3d850902020-11-23 12:19:14 +0530161 if (m_routableUserDiscoveryPrefix < data.getName()) {
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800162 // when two managers exist, the one with "smaller" name take the control
163 sendUpdate(chatroomName);
164 return;
165 }
166 else {
Varun Patila24bd3e2020-11-24 10:08:33 +0530167 if (it->second.helloTimeoutEventId)
Varun Patil3d850902020-11-23 12:19:14 +0530168 it->second.helloTimeoutEventId.cancel();
Varun Patila24bd3e2020-11-24 10:08:33 +0530169
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800170 it->second.isManager = false;
171 }
172
173 }
174 }
175
176 else if (it->second.isParticipant) {
Varun Patil3d850902020-11-23 12:19:14 +0530177 if (it->second.localChatroomTimeoutEventId)
178 it->second.localChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800179
180 // If a user start a random timer it means that he think his own chatroom is not alive
181 // But when he receive some packet, it means that this chatroom is alive, so he can
182 // cancel the timer
Varun Patil3d850902020-11-23 12:19:14 +0530183 if (it->second.managerSelectionTimeoutEventId)
184 it->second.managerSelectionTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800185
186 it->second.localChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530187 m_scheduler->schedule(HELLO_INTERVAL * 3,
188 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
189 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800190 }
191 else {
Varun Patil3d850902020-11-23 12:19:14 +0530192 if (data.hasContent()) {
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800193 ChatroomInfo chatroom;
Varun Patil3d850902020-11-23 12:19:14 +0530194 chatroom.wireDecode(data.getContent().blockFromValue());
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800195 it->second.info = chatroom;
196 }
197
Varun Patil3d850902020-11-23 12:19:14 +0530198 if (it->second.remoteChatroomTimeoutEventId)
199 it->second.remoteChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800200
201 it->second.remoteChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530202 m_scheduler->schedule(HELLO_INTERVAL * 5,
203 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
204 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800205 }
206 // if this is a chatroom that haven't been print on the discovery panel, print it.
207 if(!it->second.isPrint) {
208 sendChatroomList();
209 it->second.isPrint = true;
210 }
211}
212
213void
214ChatroomDiscoveryBackend::localSessionTimeout(const Name::Component& chatroomName)
215{
216 auto it = m_chatroomList.find(chatroomName);
217 if (it == m_chatroomList.end() || it->second.isParticipant == false)
218 return;
219 it->second.managerSelectionTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530220 m_scheduler->schedule(time::milliseconds(m_rangeUniformRandom()),
221 bind(&ChatroomDiscoveryBackend::randomSessionTimeout,
222 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800223}
224
225void
226ChatroomDiscoveryBackend::remoteSessionTimeout(const Name::Component& chatroomName)
227{
228 m_chatroomList.erase(chatroomName);
229}
230
231void
232ChatroomDiscoveryBackend::randomSessionTimeout(const Name::Component& chatroomName)
233{
234 Name prefix = m_routableUserDiscoveryPrefix;
235 prefix.append(chatroomName);
236 m_sock->addSyncNode(prefix);
237
238 emit chatroomInfoRequest(chatroomName.toUri(), true);
239}
240
241void
242ChatroomDiscoveryBackend::sendUpdate(const Name::Component& chatroomName)
243{
244 auto it = m_chatroomList.find(chatroomName);
245 if (it != m_chatroomList.end() && it->second.isManager) {
246 ndn::Block buf = it->second.info.wireEncode();
247
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800248 m_sock->publishData(buf.wire(), buf.size(), FRESHNESS_PERIOD, it->second.chatroomPrefix);
249
250 it->second.helloTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530251 m_scheduler->schedule(HELLO_INTERVAL,
252 bind(&ChatroomDiscoveryBackend::sendUpdate, this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800253 // if this is a chatroom that haven't been print on the discovery panel, print it.
254 if(!it->second.isPrint) {
255 sendChatroomList();
256 it->second.isPrint = true;
257 }
258 }
259}
260
261void
262ChatroomDiscoveryBackend::updatePrefixes()
263{
264 Name temp;
265 if (m_routingPrefix.isPrefixOf(m_userDiscoveryPrefix))
266 temp = m_userDiscoveryPrefix;
267 else
268 temp.append(m_routingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800269 .append(ROUTING_HINT_SEPARATOR)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800270 .append(m_userDiscoveryPrefix);
271
272 Name routableIdentity = m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET);
273 for (auto& chatroom : m_chatroomList) {
274 if (chatroom.second.isParticipant) {
275 chatroom.second.info.removeParticipant(routableIdentity);
276 chatroom.second.info.addParticipant(temp.getPrefix(IDENTITY_OFFSET));
277 }
278 }
279 m_routableUserDiscoveryPrefix = temp;
280}
281
282void
283ChatroomDiscoveryBackend::updateRoutingPrefix(const QString& routingPrefix)
284{
285 Name newRoutingPrefix(routingPrefix.toStdString());
286 if (!newRoutingPrefix.empty() && newRoutingPrefix != m_routingPrefix) {
287 // Update localPrefix
288 m_routingPrefix = newRoutingPrefix;
289
290 updatePrefixes();
291
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700292 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800293 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700294 m_shouldResume = true;
295 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800296
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800297 m_face->getIoService().stop();
298 }
299}
300
301void
302ChatroomDiscoveryBackend::onEraseInRoster(ndn::Name sessionPrefix,
303 ndn::Name::Component chatroomName)
304{
305 auto it = m_chatroomList.find(chatroomName);
306 if (it != m_chatroomList.end()) {
307 it->second.info.removeParticipant(sessionPrefix);
308 if (it->second.info.getParticipants().size() == 0) {
309 // Before deleting the chatroom, cancel the hello event timer if exist
Varun Patil3d850902020-11-23 12:19:14 +0530310 if (it->second.helloTimeoutEventId)
311 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800312
313 m_chatroomList.erase(chatroomName);
314 Name prefix = sessionPrefix;
315 prefix.append("CHRONOCHAT-DISCOVERYDATA").append(chatroomName);
316 m_sock->removeSyncNode(prefix);
317 sendChatroomList();
318 return;
319 }
320
321 if (sessionPrefix.isPrefixOf(m_routableUserDiscoveryPrefix)) {
322 it->second.isParticipant = false;
323 it->second.isManager = false;
324 it->second.isPrint = false;
325 it->second.count = 0;
Varun Patil3d850902020-11-23 12:19:14 +0530326 if (it->second.helloTimeoutEventId)
327 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800328
Varun Patil3d850902020-11-23 12:19:14 +0530329 if (it->second.localChatroomTimeoutEventId)
330 it->second.localChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800331
332 it->second.remoteChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530333 m_scheduler->schedule(HELLO_INTERVAL * 5,
334 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
335 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800336 }
337
338 if (it->second.isManager) {
339 sendUpdate(chatroomName);
340 }
341 }
342}
343
344void
345ChatroomDiscoveryBackend::onAddInRoster(ndn::Name sessionPrefix,
346 ndn::Name::Component chatroomName)
347{
348 auto it = m_chatroomList.find(chatroomName);
349 if (it != m_chatroomList.end()) {
350 it->second.info.addParticipant(sessionPrefix);
351 if (it->second.isManager)
352 sendUpdate(chatroomName);
353 }
354 else {
Yingdi Yuf3401182015-02-02 20:21:07 -0800355 onNewChatroomForDiscovery(chatroomName);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800356 }
357}
358
359void
Yingdi Yuf3401182015-02-02 20:21:07 -0800360ChatroomDiscoveryBackend::onNewChatroomForDiscovery(ndn::Name::Component chatroomName)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800361{
362 Name newPrefix = m_routableUserDiscoveryPrefix;
363 newPrefix.append(chatroomName);
364 auto it = m_chatroomList.find(chatroomName);
365 if (it == m_chatroomList.end()) {
366 m_chatroomList[chatroomName].chatroomPrefix = newPrefix;
367 m_chatroomList[chatroomName].isParticipant = true;
368 m_chatroomList[chatroomName].isManager = false;
369 m_chatroomList[chatroomName].count = 0;
370 m_chatroomList[chatroomName].isPrint = false;
Varun Patil3d850902020-11-23 12:19:14 +0530371 m_scheduler->schedule(time::milliseconds(600),
372 bind(&ChatroomDiscoveryBackend::randomSessionTimeout, this,
373 chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800374 }
375 else {
376 // Entering an existing chatroom
377 it->second.isParticipant = true;
378 it->second.isManager = false;
379 it->second.chatroomPrefix = newPrefix;
380
Varun Patil3d850902020-11-23 12:19:14 +0530381 if (it->second.remoteChatroomTimeoutEventId)
382 it->second.remoteChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800383 it->second.isPrint = false;
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800384
385 it->second.localChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530386 m_scheduler->schedule(HELLO_INTERVAL * 3,
387 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
388 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800389 emit chatroomInfoRequest(chatroomName.toUri(), false);
390 }
391}
392
393void
394ChatroomDiscoveryBackend::onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager)
395{
396 if (isManager)
397 chatroomInfo.setManager(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET));
398 Name::Component chatroomName = chatroomInfo.getName();
399 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
400 m_chatroomList[chatroomName].isManager = isManager;
401 m_chatroomList[chatroomName].count = 0;
402 m_chatroomList[chatroomName].info = chatroomInfo;
403 sendChatroomList();
404 onAddInRoster(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET), chatroomName);
405}
406
407void
408ChatroomDiscoveryBackend::onIdentityUpdated(const QString& identity)
409{
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800410 m_identity = Name(identity.toStdString());
411 m_userDiscoveryPrefix.clear();
412 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
413 updatePrefixes();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800414}
415
416void
417ChatroomDiscoveryBackend::sendChatroomList()
418{
419 QStringList chatroomList;
420 for (const auto& chatroom : m_chatroomList) {
421 chatroomList << QString::fromStdString(chatroom.first.toUri());
422 }
423
424 emit chatroomListReady(chatroomList);
Varun Patil3d850902020-11-23 12:19:14 +0530425 m_refreshPanelId = m_scheduler->schedule(REFRESH_INTERVAL,
426 [this] { sendChatroomList(); });
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800427}
428
429void
430ChatroomDiscoveryBackend::onWaitForChatroomInfo(const QString& chatroomName)
431{
432 auto chatroom = m_chatroomList.find(Name::Component(chatroomName.toStdString()));
433 if (chatroom != m_chatroomList.end())
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800434 emit chatroomInfoReady(chatroom->second.info, chatroom->second.isParticipant);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800435}
436
437void
438ChatroomDiscoveryBackend::shutdown()
439{
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700440 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800441 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700442 m_shouldResume = false;
443 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800444
Yingdi Yuf3401182015-02-02 20:21:07 -0800445 {
446 // In this case, we just stop checking the nfd connection and exit
447 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
448 m_isNfdConnected = true;
449 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800450
451 m_face->getIoService().stop();
452}
453
Yingdi Yuf3401182015-02-02 20:21:07 -0800454void
455ChatroomDiscoveryBackend::onNfdReconnect()
456{
457 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
458 m_isNfdConnected = true;
459}
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800460
461} // namespace chronochat
462
463#if WAF
464#include "chatroom-discovery-backend.moc"
465#endif