blob: 371a119c1a802acd21b99c30806b152eb602054a [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
116 if (m_refreshPanelId != nullptr) {
117 m_scheduler->cancelEvent(m_refreshPanelId);
118 }
119 m_refreshPanelId = m_scheduler->scheduleEvent(REFRESH_INTERVAL,
120 [this] { sendChatroomList(); });
121}
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
145ChatroomDiscoveryBackend::processChatroomData(const ndn::shared_ptr<const ndn::Data>& data)
146{
147 // extract chatroom name by get(-3)
148 Name::Component chatroomName = data->getName().get(-3);
149 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;
166 if (m_routableUserDiscoveryPrefix < data->getName()) {
167 // when two managers exist, the one with "smaller" name take the control
168 sendUpdate(chatroomName);
169 return;
170 }
171 else {
172 if (it->second.helloTimeoutEventId != nullptr) {
173 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
174 }
175 it->second.helloTimeoutEventId = nullptr;
176 it->second.isManager = false;
177 }
178
179 }
180 }
181
182 else if (it->second.isParticipant) {
183 if (it->second.localChatroomTimeoutEventId != nullptr)
184 m_scheduler->cancelEvent(it->second.localChatroomTimeoutEventId);
185
186 // If a user start a random timer it means that he think his own chatroom is not alive
187 // But when he receive some packet, it means that this chatroom is alive, so he can
188 // cancel the timer
189 if (it->second.managerSelectionTimeoutEventId != nullptr)
190 m_scheduler->cancelEvent(it->second.managerSelectionTimeoutEventId);
191 it->second.managerSelectionTimeoutEventId = nullptr;
192
193 it->second.localChatroomTimeoutEventId =
194 m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
195 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
196 this, chatroomName));
197 }
198 else {
199 if (!data->getContent().empty()) {
200 ChatroomInfo chatroom;
201 chatroom.wireDecode(data->getContent().blockFromValue());
202 it->second.info = chatroom;
203 }
204
205 if (it->second.remoteChatroomTimeoutEventId != nullptr)
206 m_scheduler->cancelEvent(it->second.remoteChatroomTimeoutEventId);
207
208 it->second.remoteChatroomTimeoutEventId =
209 m_scheduler->scheduleEvent(HELLO_INTERVAL * 5,
210 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
211 this, chatroomName));
212 }
213 // if this is a chatroom that haven't been print on the discovery panel, print it.
214 if(!it->second.isPrint) {
215 sendChatroomList();
216 it->second.isPrint = true;
217 }
218}
219
220void
221ChatroomDiscoveryBackend::localSessionTimeout(const Name::Component& chatroomName)
222{
223 auto it = m_chatroomList.find(chatroomName);
224 if (it == m_chatroomList.end() || it->second.isParticipant == false)
225 return;
226 it->second.managerSelectionTimeoutEventId =
227 m_scheduler->scheduleEvent(time::milliseconds(m_rangeUniformRandom()),
228 bind(&ChatroomDiscoveryBackend::randomSessionTimeout,
229 this, chatroomName));
230}
231
232void
233ChatroomDiscoveryBackend::remoteSessionTimeout(const Name::Component& chatroomName)
234{
235 m_chatroomList.erase(chatroomName);
236}
237
238void
239ChatroomDiscoveryBackend::randomSessionTimeout(const Name::Component& chatroomName)
240{
241 Name prefix = m_routableUserDiscoveryPrefix;
242 prefix.append(chatroomName);
243 m_sock->addSyncNode(prefix);
244
245 emit chatroomInfoRequest(chatroomName.toUri(), true);
246}
247
248void
249ChatroomDiscoveryBackend::sendUpdate(const Name::Component& chatroomName)
250{
251 auto it = m_chatroomList.find(chatroomName);
252 if (it != m_chatroomList.end() && it->second.isManager) {
253 ndn::Block buf = it->second.info.wireEncode();
254
255 if (it->second.helloTimeoutEventId != nullptr) {
256 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
257 }
258
259 m_sock->publishData(buf.wire(), buf.size(), FRESHNESS_PERIOD, it->second.chatroomPrefix);
260
261 it->second.helloTimeoutEventId =
262 m_scheduler->scheduleEvent(HELLO_INTERVAL,
263 bind(&ChatroomDiscoveryBackend::sendUpdate, this, chatroomName));
264 // if this is a chatroom that haven't been print on the discovery panel, print it.
265 if(!it->second.isPrint) {
266 sendChatroomList();
267 it->second.isPrint = true;
268 }
269 }
270}
271
272void
273ChatroomDiscoveryBackend::updatePrefixes()
274{
275 Name temp;
276 if (m_routingPrefix.isPrefixOf(m_userDiscoveryPrefix))
277 temp = m_userDiscoveryPrefix;
278 else
279 temp.append(m_routingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800280 .append(ROUTING_HINT_SEPARATOR)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800281 .append(m_userDiscoveryPrefix);
282
283 Name routableIdentity = m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET);
284 for (auto& chatroom : m_chatroomList) {
285 if (chatroom.second.isParticipant) {
286 chatroom.second.info.removeParticipant(routableIdentity);
287 chatroom.second.info.addParticipant(temp.getPrefix(IDENTITY_OFFSET));
288 }
289 }
290 m_routableUserDiscoveryPrefix = temp;
291}
292
293void
294ChatroomDiscoveryBackend::updateRoutingPrefix(const QString& routingPrefix)
295{
296 Name newRoutingPrefix(routingPrefix.toStdString());
297 if (!newRoutingPrefix.empty() && newRoutingPrefix != m_routingPrefix) {
298 // Update localPrefix
299 m_routingPrefix = newRoutingPrefix;
300
301 updatePrefixes();
302
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700303 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800304 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700305 m_shouldResume = true;
306 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800307
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800308 m_face->getIoService().stop();
309 }
310}
311
312void
313ChatroomDiscoveryBackend::onEraseInRoster(ndn::Name sessionPrefix,
314 ndn::Name::Component chatroomName)
315{
316 auto it = m_chatroomList.find(chatroomName);
317 if (it != m_chatroomList.end()) {
318 it->second.info.removeParticipant(sessionPrefix);
319 if (it->second.info.getParticipants().size() == 0) {
320 // Before deleting the chatroom, cancel the hello event timer if exist
321 if (it->second.helloTimeoutEventId != nullptr)
322 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
323
324 m_chatroomList.erase(chatroomName);
325 Name prefix = sessionPrefix;
326 prefix.append("CHRONOCHAT-DISCOVERYDATA").append(chatroomName);
327 m_sock->removeSyncNode(prefix);
328 sendChatroomList();
329 return;
330 }
331
332 if (sessionPrefix.isPrefixOf(m_routableUserDiscoveryPrefix)) {
333 it->second.isParticipant = false;
334 it->second.isManager = false;
335 it->second.isPrint = false;
336 it->second.count = 0;
337 if (it->second.helloTimeoutEventId != nullptr)
338 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
339 it->second.helloTimeoutEventId = nullptr;
340
341 if (it->second.localChatroomTimeoutEventId != nullptr)
342 m_scheduler->cancelEvent(it->second.localChatroomTimeoutEventId);
343 it->second.localChatroomTimeoutEventId = nullptr;
344
345 it->second.remoteChatroomTimeoutEventId =
346 m_scheduler->scheduleEvent(HELLO_INTERVAL * 5,
347 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
348 this, chatroomName));
349 }
350
351 if (it->second.isManager) {
352 sendUpdate(chatroomName);
353 }
354 }
355}
356
357void
358ChatroomDiscoveryBackend::onAddInRoster(ndn::Name sessionPrefix,
359 ndn::Name::Component chatroomName)
360{
361 auto it = m_chatroomList.find(chatroomName);
362 if (it != m_chatroomList.end()) {
363 it->second.info.addParticipant(sessionPrefix);
364 if (it->second.isManager)
365 sendUpdate(chatroomName);
366 }
367 else {
Yingdi Yuf3401182015-02-02 20:21:07 -0800368 onNewChatroomForDiscovery(chatroomName);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800369 }
370}
371
372void
Yingdi Yuf3401182015-02-02 20:21:07 -0800373ChatroomDiscoveryBackend::onNewChatroomForDiscovery(ndn::Name::Component chatroomName)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800374{
375 Name newPrefix = m_routableUserDiscoveryPrefix;
376 newPrefix.append(chatroomName);
377 auto it = m_chatroomList.find(chatroomName);
378 if (it == m_chatroomList.end()) {
379 m_chatroomList[chatroomName].chatroomPrefix = newPrefix;
380 m_chatroomList[chatroomName].isParticipant = true;
381 m_chatroomList[chatroomName].isManager = false;
382 m_chatroomList[chatroomName].count = 0;
383 m_chatroomList[chatroomName].isPrint = false;
384 m_scheduler->scheduleEvent(time::milliseconds(600),
385 bind(&ChatroomDiscoveryBackend::randomSessionTimeout, this,
386 chatroomName));
387 }
388 else {
389 // Entering an existing chatroom
390 it->second.isParticipant = true;
391 it->second.isManager = false;
392 it->second.chatroomPrefix = newPrefix;
393
394 if (it->second.remoteChatroomTimeoutEventId != nullptr)
395 m_scheduler->cancelEvent(it->second.remoteChatroomTimeoutEventId);
396 it->second.isPrint = false;
397 it->second.remoteChatroomTimeoutEventId = nullptr;
398
399 it->second.localChatroomTimeoutEventId =
400 m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
401 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
402 this, chatroomName));
403 emit chatroomInfoRequest(chatroomName.toUri(), false);
404 }
405}
406
407void
408ChatroomDiscoveryBackend::onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager)
409{
410 if (isManager)
411 chatroomInfo.setManager(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET));
412 Name::Component chatroomName = chatroomInfo.getName();
413 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
414 m_chatroomList[chatroomName].isManager = isManager;
415 m_chatroomList[chatroomName].count = 0;
416 m_chatroomList[chatroomName].info = chatroomInfo;
417 sendChatroomList();
418 onAddInRoster(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET), chatroomName);
419}
420
421void
422ChatroomDiscoveryBackend::onIdentityUpdated(const QString& identity)
423{
424 m_chatroomList.clear();
425 m_identity = Name(identity.toStdString());
426 m_userDiscoveryPrefix.clear();
427 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
428 updatePrefixes();
429
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700430 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800431 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700432 m_shouldResume = true;
433 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800434
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800435 m_face->getIoService().stop();
436}
437
438void
439ChatroomDiscoveryBackend::sendChatroomList()
440{
441 QStringList chatroomList;
442 for (const auto& chatroom : m_chatroomList) {
443 chatroomList << QString::fromStdString(chatroom.first.toUri());
444 }
445
446 emit chatroomListReady(chatroomList);
447 if (m_refreshPanelId != nullptr) {
448 m_scheduler->cancelEvent(m_refreshPanelId);
449 }
450 m_refreshPanelId = m_scheduler->scheduleEvent(REFRESH_INTERVAL,
451 [this] { sendChatroomList(); });
452}
453
454void
455ChatroomDiscoveryBackend::onWaitForChatroomInfo(const QString& chatroomName)
456{
457 auto chatroom = m_chatroomList.find(Name::Component(chatroomName.toStdString()));
458 if (chatroom != m_chatroomList.end())
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800459 emit chatroomInfoReady(chatroom->second.info, chatroom->second.isParticipant);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800460}
461
462void
463ChatroomDiscoveryBackend::shutdown()
464{
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700465 {
Yingdi Yuf3401182015-02-02 20:21:07 -0800466 std::lock_guard<std::mutex>lock(m_resumeMutex);
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700467 m_shouldResume = false;
468 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800469
Yingdi Yuf3401182015-02-02 20:21:07 -0800470 {
471 // In this case, we just stop checking the nfd connection and exit
472 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
473 m_isNfdConnected = true;
474 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800475
476 m_face->getIoService().stop();
477}
478
Yingdi Yuf3401182015-02-02 20:21:07 -0800479void
480ChatroomDiscoveryBackend::onNfdReconnect()
481{
482 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
483 m_isNfdConnected = true;
484}
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800485
486} // namespace chronochat
487
488#if WAF
489#include "chatroom-discovery-backend.moc"
490#endif