blob: 67dd7fa013bf68bd7e9577ee3efc8b5aa2a651f5 [file] [log] [blame]
Qiuhan Ding43c8e162015-02-02 15:16:48 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013, Regents of the University of California
4 *
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
14
15#ifndef Q_MOC_RUN
16
17#endif
18
19namespace chronochat {
20
21static const time::milliseconds FRESHNESS_PERIOD(60000);
22static const time::seconds REFRESH_INTERVAL(60);
23static const time::seconds HELLO_INTERVAL(60);
Qiuhan Dingba3e57a2015-01-08 19:07:39 -080024static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
25 ndn::name::Component::fromEscapedString("%F0%2E");
Qiuhan Ding43c8e162015-02-02 15:16:48 -080026// a count enforced when a manager himself find another one publish chatroom data
27static const int MAXIMUM_COUNT = 3;
28static const int IDENTITY_OFFSET = -1;
Yingdi Yuf3401182015-02-02 20:21:07 -080029static const int CONNECTION_RETRY_TIMER = 3;
Qiuhan Ding43c8e162015-02-02 15:16:48 -080030
31ChatroomDiscoveryBackend::ChatroomDiscoveryBackend(const Name& routingPrefix,
32 const Name& identity,
33 QObject* parent)
34 : QThread(parent)
Yingdi Yuf3401182015-02-02 20:21:07 -080035 , m_shouldResume(false)
Qiuhan Ding43c8e162015-02-02 15:16:48 -080036 , m_routingPrefix(routingPrefix)
37 , m_identity(identity)
38 , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
39 , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(500,2000))
Qiuhan Ding43c8e162015-02-02 15:16:48 -080040{
41 m_discoveryPrefix.append("ndn")
42 .append("broadcast")
43 .append("ChronoChat")
44 .append("Discovery");
45 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
46 updatePrefixes();
47}
48
49ChatroomDiscoveryBackend::~ChatroomDiscoveryBackend()
50{
51}
52
53void
54ChatroomDiscoveryBackend::run()
55{
56 bool shouldResume = false;
57 do {
58 initializeSync();
59
60 if (m_face == nullptr)
61 break;
62
Yingdi Yuf3401182015-02-02 20:21:07 -080063 try {
64 m_face->getIoService().run();
65 }
66 catch (std::runtime_error& e) {
67 {
68 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
69 m_isNfdConnected = false;
70 }
71 emit nfdError();
72 {
73 std::lock_guard<std::mutex>lock(m_resumeMutex);
74 m_shouldResume = true;
75 }
76#ifdef BOOST_THREAD_USES_CHRONO
77 time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
78#else
79 boost::posix_time::time_duration reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
80#endif
81 while (!m_isNfdConnected) {
82#ifdef BOOST_THREAD_USES_CHRONO
83 boost::this_thread::sleep_for(reconnectTimer);
84#else
85 boost::this_thread::sleep(reconnectTimer);
86#endif
87 }
88 }
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070089 {
Yingdi Yuf3401182015-02-02 20:21:07 -080090 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070091 shouldResume = m_shouldResume;
92 m_shouldResume = false;
93 }
Yingdi Yuf3401182015-02-02 20:21:07 -080094 close();
Qiuhan Ding43c8e162015-02-02 15:16:48 -080095
96 } while (shouldResume);
97
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070098 std::cerr << "DiscoveryBackend: Bye!" << std::endl;
Qiuhan Ding43c8e162015-02-02 15:16:48 -080099}
100
101void
102ChatroomDiscoveryBackend::initializeSync()
103{
104 BOOST_ASSERT(m_sock == nullptr);
105
106 m_face = shared_ptr<ndn::Face>(new ndn::Face);
107 m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
108
109 m_sock = make_shared<chronosync::Socket>(m_discoveryPrefix,
110 Name(),
111 ref(*m_face),
112 bind(&ChatroomDiscoveryBackend::processSyncUpdate,
113 this, _1));
114
115 // add an timer to refresh front end
Varun Patil3d850902020-11-23 12:19:14 +0530116 if (m_refreshPanelId) {
117 m_refreshPanelId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800118 }
Varun Patil3d850902020-11-23 12:19:14 +0530119 m_refreshPanelId = m_scheduler->schedule(REFRESH_INTERVAL,
120 [this] { sendChatroomList(); });
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800121}
122
123void
124ChatroomDiscoveryBackend::close()
125{
126 m_scheduler->cancelAllEvents();
127 m_refreshPanelId.reset();
128 m_chatroomList.clear();
129 m_sock.reset();
130}
131
132void
133ChatroomDiscoveryBackend::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates)
134{
135 if (updates.empty()) {
136 return;
137 }
138 for (const auto& update : updates) {
139 m_sock->fetchData(update.session, update.high,
140 bind(&ChatroomDiscoveryBackend::processChatroomData, this, _1), 2);
141 }
142}
143
144void
Varun Patil3d850902020-11-23 12:19:14 +0530145ChatroomDiscoveryBackend::processChatroomData(const ndn::Data& data)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800146{
147 // extract chatroom name by get(-3)
Varun Patil3d850902020-11-23 12:19:14 +0530148 Name::Component chatroomName = data.getName().get(-3);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800149 auto it = m_chatroomList.find(chatroomName);
150 if (it == m_chatroomList.end()) {
151 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
152 m_chatroomList[chatroomName].count = 0;
153 m_chatroomList[chatroomName].isPrint = false;
154 m_chatroomList[chatroomName].isParticipant = false;
155 m_chatroomList[chatroomName].isManager = false;
156 it = m_chatroomList.find(chatroomName);
157 }
158 // If the user is the manager of this chatroom, he should not receive any data from this chatroom
159 if (it->second.isManager) {
160 if (it->second.count < MAXIMUM_COUNT) {
161 it->second.count++;
162 return;
163 }
164 else {
165 it->second.count = 0;
Varun Patil3d850902020-11-23 12:19:14 +0530166 if (m_routableUserDiscoveryPrefix < data.getName()) {
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800167 // when two managers exist, the one with "smaller" name take the control
168 sendUpdate(chatroomName);
169 return;
170 }
171 else {
Varun Patil3d850902020-11-23 12:19:14 +0530172 if (it->second.helloTimeoutEventId) {
173 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800174 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800175 it->second.isManager = false;
176 }
177
178 }
179 }
180
181 else if (it->second.isParticipant) {
Varun Patil3d850902020-11-23 12:19:14 +0530182 if (it->second.localChatroomTimeoutEventId)
183 it->second.localChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800184
185 // If a user start a random timer it means that he think his own chatroom is not alive
186 // But when he receive some packet, it means that this chatroom is alive, so he can
187 // cancel the timer
Varun Patil3d850902020-11-23 12:19:14 +0530188 if (it->second.managerSelectionTimeoutEventId)
189 it->second.managerSelectionTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800190
191 it->second.localChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530192 m_scheduler->schedule(HELLO_INTERVAL * 3,
193 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
194 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800195 }
196 else {
Varun Patil3d850902020-11-23 12:19:14 +0530197 if (data.hasContent()) {
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800198 ChatroomInfo chatroom;
Varun Patil3d850902020-11-23 12:19:14 +0530199 chatroom.wireDecode(data.getContent().blockFromValue());
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800200 it->second.info = chatroom;
201 }
202
Varun Patil3d850902020-11-23 12:19:14 +0530203 if (it->second.remoteChatroomTimeoutEventId)
204 it->second.remoteChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800205
206 it->second.remoteChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530207 m_scheduler->schedule(HELLO_INTERVAL * 5,
208 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
209 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800210 }
211 // if this is a chatroom that haven't been print on the discovery panel, print it.
212 if(!it->second.isPrint) {
213 sendChatroomList();
214 it->second.isPrint = true;
215 }
216}
217
218void
219ChatroomDiscoveryBackend::localSessionTimeout(const Name::Component& chatroomName)
220{
221 auto it = m_chatroomList.find(chatroomName);
222 if (it == m_chatroomList.end() || it->second.isParticipant == false)
223 return;
224 it->second.managerSelectionTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530225 m_scheduler->schedule(time::milliseconds(m_rangeUniformRandom()),
226 bind(&ChatroomDiscoveryBackend::randomSessionTimeout,
227 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800228}
229
230void
231ChatroomDiscoveryBackend::remoteSessionTimeout(const Name::Component& chatroomName)
232{
233 m_chatroomList.erase(chatroomName);
234}
235
236void
237ChatroomDiscoveryBackend::randomSessionTimeout(const Name::Component& chatroomName)
238{
239 Name prefix = m_routableUserDiscoveryPrefix;
240 prefix.append(chatroomName);
241 m_sock->addSyncNode(prefix);
242
243 emit chatroomInfoRequest(chatroomName.toUri(), true);
244}
245
246void
247ChatroomDiscoveryBackend::sendUpdate(const Name::Component& chatroomName)
248{
249 auto it = m_chatroomList.find(chatroomName);
250 if (it != m_chatroomList.end() && it->second.isManager) {
251 ndn::Block buf = it->second.info.wireEncode();
252
Varun Patil3d850902020-11-23 12:19:14 +0530253 if (it->second.helloTimeoutEventId) {
254 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800255 }
256
257 m_sock->publishData(buf.wire(), buf.size(), FRESHNESS_PERIOD, it->second.chatroomPrefix);
258
259 it->second.helloTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530260 m_scheduler->schedule(HELLO_INTERVAL,
261 bind(&ChatroomDiscoveryBackend::sendUpdate, this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800262 // if this is a chatroom that haven't been print on the discovery panel, print it.
263 if(!it->second.isPrint) {
264 sendChatroomList();
265 it->second.isPrint = true;
266 }
267 }
268}
269
270void
271ChatroomDiscoveryBackend::updatePrefixes()
272{
273 Name temp;
274 if (m_routingPrefix.isPrefixOf(m_userDiscoveryPrefix))
275 temp = m_userDiscoveryPrefix;
276 else
277 temp.append(m_routingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800278 .append(ROUTING_HINT_SEPARATOR)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800279 .append(m_userDiscoveryPrefix);
280
281 Name routableIdentity = m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET);
282 for (auto& chatroom : m_chatroomList) {
283 if (chatroom.second.isParticipant) {
284 chatroom.second.info.removeParticipant(routableIdentity);
285 chatroom.second.info.addParticipant(temp.getPrefix(IDENTITY_OFFSET));
286 }
287 }
288 m_routableUserDiscoveryPrefix = temp;
289}
290
291void
292ChatroomDiscoveryBackend::updateRoutingPrefix(const QString& routingPrefix)
293{
294 Name newRoutingPrefix(routingPrefix.toStdString());
295 if (!newRoutingPrefix.empty() && newRoutingPrefix != m_routingPrefix) {
296 // Update localPrefix
297 m_routingPrefix = newRoutingPrefix;
298
299 updatePrefixes();
300
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700301 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800302 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700303 m_shouldResume = true;
304 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800305
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800306 m_face->getIoService().stop();
307 }
308}
309
310void
311ChatroomDiscoveryBackend::onEraseInRoster(ndn::Name sessionPrefix,
312 ndn::Name::Component chatroomName)
313{
314 auto it = m_chatroomList.find(chatroomName);
315 if (it != m_chatroomList.end()) {
316 it->second.info.removeParticipant(sessionPrefix);
317 if (it->second.info.getParticipants().size() == 0) {
318 // Before deleting the chatroom, cancel the hello event timer if exist
Varun Patil3d850902020-11-23 12:19:14 +0530319 if (it->second.helloTimeoutEventId)
320 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800321
322 m_chatroomList.erase(chatroomName);
323 Name prefix = sessionPrefix;
324 prefix.append("CHRONOCHAT-DISCOVERYDATA").append(chatroomName);
325 m_sock->removeSyncNode(prefix);
326 sendChatroomList();
327 return;
328 }
329
330 if (sessionPrefix.isPrefixOf(m_routableUserDiscoveryPrefix)) {
331 it->second.isParticipant = false;
332 it->second.isManager = false;
333 it->second.isPrint = false;
334 it->second.count = 0;
Varun Patil3d850902020-11-23 12:19:14 +0530335 if (it->second.helloTimeoutEventId)
336 it->second.helloTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800337
Varun Patil3d850902020-11-23 12:19:14 +0530338 if (it->second.localChatroomTimeoutEventId)
339 it->second.localChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800340
341 it->second.remoteChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530342 m_scheduler->schedule(HELLO_INTERVAL * 5,
343 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
344 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800345 }
346
347 if (it->second.isManager) {
348 sendUpdate(chatroomName);
349 }
350 }
351}
352
353void
354ChatroomDiscoveryBackend::onAddInRoster(ndn::Name sessionPrefix,
355 ndn::Name::Component chatroomName)
356{
357 auto it = m_chatroomList.find(chatroomName);
358 if (it != m_chatroomList.end()) {
359 it->second.info.addParticipant(sessionPrefix);
360 if (it->second.isManager)
361 sendUpdate(chatroomName);
362 }
363 else {
Yingdi Yuf3401182015-02-02 20:21:07 -0800364 onNewChatroomForDiscovery(chatroomName);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800365 }
366}
367
368void
Yingdi Yuf3401182015-02-02 20:21:07 -0800369ChatroomDiscoveryBackend::onNewChatroomForDiscovery(ndn::Name::Component chatroomName)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800370{
371 Name newPrefix = m_routableUserDiscoveryPrefix;
372 newPrefix.append(chatroomName);
373 auto it = m_chatroomList.find(chatroomName);
374 if (it == m_chatroomList.end()) {
375 m_chatroomList[chatroomName].chatroomPrefix = newPrefix;
376 m_chatroomList[chatroomName].isParticipant = true;
377 m_chatroomList[chatroomName].isManager = false;
378 m_chatroomList[chatroomName].count = 0;
379 m_chatroomList[chatroomName].isPrint = false;
Varun Patil3d850902020-11-23 12:19:14 +0530380 m_scheduler->schedule(time::milliseconds(600),
381 bind(&ChatroomDiscoveryBackend::randomSessionTimeout, this,
382 chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800383 }
384 else {
385 // Entering an existing chatroom
386 it->second.isParticipant = true;
387 it->second.isManager = false;
388 it->second.chatroomPrefix = newPrefix;
389
Varun Patil3d850902020-11-23 12:19:14 +0530390 if (it->second.remoteChatroomTimeoutEventId)
391 it->second.remoteChatroomTimeoutEventId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800392 it->second.isPrint = false;
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800393
394 it->second.localChatroomTimeoutEventId =
Varun Patil3d850902020-11-23 12:19:14 +0530395 m_scheduler->schedule(HELLO_INTERVAL * 3,
396 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
397 this, chatroomName));
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800398 emit chatroomInfoRequest(chatroomName.toUri(), false);
399 }
400}
401
402void
403ChatroomDiscoveryBackend::onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager)
404{
405 if (isManager)
406 chatroomInfo.setManager(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET));
407 Name::Component chatroomName = chatroomInfo.getName();
408 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
409 m_chatroomList[chatroomName].isManager = isManager;
410 m_chatroomList[chatroomName].count = 0;
411 m_chatroomList[chatroomName].info = chatroomInfo;
412 sendChatroomList();
413 onAddInRoster(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET), chatroomName);
414}
415
416void
417ChatroomDiscoveryBackend::onIdentityUpdated(const QString& identity)
418{
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800419 m_identity = Name(identity.toStdString());
420 m_userDiscoveryPrefix.clear();
421 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
422 updatePrefixes();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800423}
424
425void
426ChatroomDiscoveryBackend::sendChatroomList()
427{
428 QStringList chatroomList;
429 for (const auto& chatroom : m_chatroomList) {
430 chatroomList << QString::fromStdString(chatroom.first.toUri());
431 }
432
433 emit chatroomListReady(chatroomList);
Varun Patil3d850902020-11-23 12:19:14 +0530434 if (m_refreshPanelId) {
435 m_refreshPanelId.cancel();
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800436 }
Varun Patil3d850902020-11-23 12:19:14 +0530437 m_refreshPanelId = m_scheduler->schedule(REFRESH_INTERVAL,
438 [this] { sendChatroomList(); });
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800439}
440
441void
442ChatroomDiscoveryBackend::onWaitForChatroomInfo(const QString& chatroomName)
443{
444 auto chatroom = m_chatroomList.find(Name::Component(chatroomName.toStdString()));
445 if (chatroom != m_chatroomList.end())
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800446 emit chatroomInfoReady(chatroom->second.info, chatroom->second.isParticipant);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800447}
448
449void
450ChatroomDiscoveryBackend::shutdown()
451{
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700452 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800453 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700454 m_shouldResume = false;
455 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800456
Yingdi Yuf3401182015-02-02 20:21:07 -0800457 {
458 // In this case, we just stop checking the nfd connection and exit
459 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
460 m_isNfdConnected = true;
461 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800462
463 m_face->getIoService().stop();
464}
465
Yingdi Yuf3401182015-02-02 20:21:07 -0800466void
467ChatroomDiscoveryBackend::onNfdReconnect()
468{
469 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
470 m_isNfdConnected = true;
471}
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800472
473} // namespace chronochat
474
475#if WAF
476#include "chatroom-discovery-backend.moc"
477#endif