blob: 0da409d507bd24feb7fc17b2afa6e8df0d6ec1f6 [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;
29
30ChatroomDiscoveryBackend::ChatroomDiscoveryBackend(const Name& routingPrefix,
31 const Name& identity,
32 QObject* parent)
33 : QThread(parent)
34 , m_routingPrefix(routingPrefix)
35 , m_identity(identity)
36 , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
37 , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(500,2000))
38 , m_shouldResume(false)
39{
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
62 m_face->getIoService().run();
63
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070064 {
65 std::lock_guard<std::mutex>lock(m_mutex);
66 shouldResume = m_shouldResume;
67 m_shouldResume = false;
68 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -080069
70 } while (shouldResume);
71
Qiuhan Dingf22c41b2015-03-11 13:19:01 -070072 std::cerr << "DiscoveryBackend: Bye!" << std::endl;
Qiuhan Ding43c8e162015-02-02 15:16:48 -080073}
74
75void
76ChatroomDiscoveryBackend::initializeSync()
77{
78 BOOST_ASSERT(m_sock == nullptr);
79
80 m_face = shared_ptr<ndn::Face>(new ndn::Face);
81 m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
82
83 m_sock = make_shared<chronosync::Socket>(m_discoveryPrefix,
84 Name(),
85 ref(*m_face),
86 bind(&ChatroomDiscoveryBackend::processSyncUpdate,
87 this, _1));
88
89 // add an timer to refresh front end
90 if (m_refreshPanelId != nullptr) {
91 m_scheduler->cancelEvent(m_refreshPanelId);
92 }
93 m_refreshPanelId = m_scheduler->scheduleEvent(REFRESH_INTERVAL,
94 [this] { sendChatroomList(); });
95}
96
97void
98ChatroomDiscoveryBackend::close()
99{
100 m_scheduler->cancelAllEvents();
101 m_refreshPanelId.reset();
102 m_chatroomList.clear();
103 m_sock.reset();
104}
105
106void
107ChatroomDiscoveryBackend::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates)
108{
109 if (updates.empty()) {
110 return;
111 }
112 for (const auto& update : updates) {
113 m_sock->fetchData(update.session, update.high,
114 bind(&ChatroomDiscoveryBackend::processChatroomData, this, _1), 2);
115 }
116}
117
118void
119ChatroomDiscoveryBackend::processChatroomData(const ndn::shared_ptr<const ndn::Data>& data)
120{
121 // extract chatroom name by get(-3)
122 Name::Component chatroomName = data->getName().get(-3);
123 auto it = m_chatroomList.find(chatroomName);
124 if (it == m_chatroomList.end()) {
125 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
126 m_chatroomList[chatroomName].count = 0;
127 m_chatroomList[chatroomName].isPrint = false;
128 m_chatroomList[chatroomName].isParticipant = false;
129 m_chatroomList[chatroomName].isManager = false;
130 it = m_chatroomList.find(chatroomName);
131 }
132 // If the user is the manager of this chatroom, he should not receive any data from this chatroom
133 if (it->second.isManager) {
134 if (it->second.count < MAXIMUM_COUNT) {
135 it->second.count++;
136 return;
137 }
138 else {
139 it->second.count = 0;
140 if (m_routableUserDiscoveryPrefix < data->getName()) {
141 // when two managers exist, the one with "smaller" name take the control
142 sendUpdate(chatroomName);
143 return;
144 }
145 else {
146 if (it->second.helloTimeoutEventId != nullptr) {
147 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
148 }
149 it->second.helloTimeoutEventId = nullptr;
150 it->second.isManager = false;
151 }
152
153 }
154 }
155
156 else if (it->second.isParticipant) {
157 if (it->second.localChatroomTimeoutEventId != nullptr)
158 m_scheduler->cancelEvent(it->second.localChatroomTimeoutEventId);
159
160 // If a user start a random timer it means that he think his own chatroom is not alive
161 // But when he receive some packet, it means that this chatroom is alive, so he can
162 // cancel the timer
163 if (it->second.managerSelectionTimeoutEventId != nullptr)
164 m_scheduler->cancelEvent(it->second.managerSelectionTimeoutEventId);
165 it->second.managerSelectionTimeoutEventId = nullptr;
166
167 it->second.localChatroomTimeoutEventId =
168 m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
169 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
170 this, chatroomName));
171 }
172 else {
173 if (!data->getContent().empty()) {
174 ChatroomInfo chatroom;
175 chatroom.wireDecode(data->getContent().blockFromValue());
176 it->second.info = chatroom;
177 }
178
179 if (it->second.remoteChatroomTimeoutEventId != nullptr)
180 m_scheduler->cancelEvent(it->second.remoteChatroomTimeoutEventId);
181
182 it->second.remoteChatroomTimeoutEventId =
183 m_scheduler->scheduleEvent(HELLO_INTERVAL * 5,
184 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
185 this, chatroomName));
186 }
187 // if this is a chatroom that haven't been print on the discovery panel, print it.
188 if(!it->second.isPrint) {
189 sendChatroomList();
190 it->second.isPrint = true;
191 }
192}
193
194void
195ChatroomDiscoveryBackend::localSessionTimeout(const Name::Component& chatroomName)
196{
197 auto it = m_chatroomList.find(chatroomName);
198 if (it == m_chatroomList.end() || it->second.isParticipant == false)
199 return;
200 it->second.managerSelectionTimeoutEventId =
201 m_scheduler->scheduleEvent(time::milliseconds(m_rangeUniformRandom()),
202 bind(&ChatroomDiscoveryBackend::randomSessionTimeout,
203 this, chatroomName));
204}
205
206void
207ChatroomDiscoveryBackend::remoteSessionTimeout(const Name::Component& chatroomName)
208{
209 m_chatroomList.erase(chatroomName);
210}
211
212void
213ChatroomDiscoveryBackend::randomSessionTimeout(const Name::Component& chatroomName)
214{
215 Name prefix = m_routableUserDiscoveryPrefix;
216 prefix.append(chatroomName);
217 m_sock->addSyncNode(prefix);
218
219 emit chatroomInfoRequest(chatroomName.toUri(), true);
220}
221
222void
223ChatroomDiscoveryBackend::sendUpdate(const Name::Component& chatroomName)
224{
225 auto it = m_chatroomList.find(chatroomName);
226 if (it != m_chatroomList.end() && it->second.isManager) {
227 ndn::Block buf = it->second.info.wireEncode();
228
229 if (it->second.helloTimeoutEventId != nullptr) {
230 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
231 }
232
233 m_sock->publishData(buf.wire(), buf.size(), FRESHNESS_PERIOD, it->second.chatroomPrefix);
234
235 it->second.helloTimeoutEventId =
236 m_scheduler->scheduleEvent(HELLO_INTERVAL,
237 bind(&ChatroomDiscoveryBackend::sendUpdate, this, chatroomName));
238 // if this is a chatroom that haven't been print on the discovery panel, print it.
239 if(!it->second.isPrint) {
240 sendChatroomList();
241 it->second.isPrint = true;
242 }
243 }
244}
245
246void
247ChatroomDiscoveryBackend::updatePrefixes()
248{
249 Name temp;
250 if (m_routingPrefix.isPrefixOf(m_userDiscoveryPrefix))
251 temp = m_userDiscoveryPrefix;
252 else
253 temp.append(m_routingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800254 .append(ROUTING_HINT_SEPARATOR)
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800255 .append(m_userDiscoveryPrefix);
256
257 Name routableIdentity = m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET);
258 for (auto& chatroom : m_chatroomList) {
259 if (chatroom.second.isParticipant) {
260 chatroom.second.info.removeParticipant(routableIdentity);
261 chatroom.second.info.addParticipant(temp.getPrefix(IDENTITY_OFFSET));
262 }
263 }
264 m_routableUserDiscoveryPrefix = temp;
265}
266
267void
268ChatroomDiscoveryBackend::updateRoutingPrefix(const QString& routingPrefix)
269{
270 Name newRoutingPrefix(routingPrefix.toStdString());
271 if (!newRoutingPrefix.empty() && newRoutingPrefix != m_routingPrefix) {
272 // Update localPrefix
273 m_routingPrefix = newRoutingPrefix;
274
275 updatePrefixes();
276
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700277 {
278 std::lock_guard<std::mutex>lock(m_mutex);
279 m_shouldResume = true;
280 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800281
282 close();
283
284 m_face->getIoService().stop();
285 }
286}
287
288void
289ChatroomDiscoveryBackend::onEraseInRoster(ndn::Name sessionPrefix,
290 ndn::Name::Component chatroomName)
291{
292 auto it = m_chatroomList.find(chatroomName);
293 if (it != m_chatroomList.end()) {
294 it->second.info.removeParticipant(sessionPrefix);
295 if (it->second.info.getParticipants().size() == 0) {
296 // Before deleting the chatroom, cancel the hello event timer if exist
297 if (it->second.helloTimeoutEventId != nullptr)
298 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
299
300 m_chatroomList.erase(chatroomName);
301 Name prefix = sessionPrefix;
302 prefix.append("CHRONOCHAT-DISCOVERYDATA").append(chatroomName);
303 m_sock->removeSyncNode(prefix);
304 sendChatroomList();
305 return;
306 }
307
308 if (sessionPrefix.isPrefixOf(m_routableUserDiscoveryPrefix)) {
309 it->second.isParticipant = false;
310 it->second.isManager = false;
311 it->second.isPrint = false;
312 it->second.count = 0;
313 if (it->second.helloTimeoutEventId != nullptr)
314 m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
315 it->second.helloTimeoutEventId = nullptr;
316
317 if (it->second.localChatroomTimeoutEventId != nullptr)
318 m_scheduler->cancelEvent(it->second.localChatroomTimeoutEventId);
319 it->second.localChatroomTimeoutEventId = nullptr;
320
321 it->second.remoteChatroomTimeoutEventId =
322 m_scheduler->scheduleEvent(HELLO_INTERVAL * 5,
323 bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
324 this, chatroomName));
325 }
326
327 if (it->second.isManager) {
328 sendUpdate(chatroomName);
329 }
330 }
331}
332
333void
334ChatroomDiscoveryBackend::onAddInRoster(ndn::Name sessionPrefix,
335 ndn::Name::Component chatroomName)
336{
337 auto it = m_chatroomList.find(chatroomName);
338 if (it != m_chatroomList.end()) {
339 it->second.info.addParticipant(sessionPrefix);
340 if (it->second.isManager)
341 sendUpdate(chatroomName);
342 }
343 else {
344 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
345 m_chatroomList[chatroomName].info.setName(chatroomName);
346 m_chatroomList[chatroomName].info.addParticipant(sessionPrefix);
347 }
348}
349
350void
351ChatroomDiscoveryBackend::onNewChatroomForDiscovery(Name::Component chatroomName)
352{
353 Name newPrefix = m_routableUserDiscoveryPrefix;
354 newPrefix.append(chatroomName);
355 auto it = m_chatroomList.find(chatroomName);
356 if (it == m_chatroomList.end()) {
357 m_chatroomList[chatroomName].chatroomPrefix = newPrefix;
358 m_chatroomList[chatroomName].isParticipant = true;
359 m_chatroomList[chatroomName].isManager = false;
360 m_chatroomList[chatroomName].count = 0;
361 m_chatroomList[chatroomName].isPrint = false;
362 m_scheduler->scheduleEvent(time::milliseconds(600),
363 bind(&ChatroomDiscoveryBackend::randomSessionTimeout, this,
364 chatroomName));
365 }
366 else {
367 // Entering an existing chatroom
368 it->second.isParticipant = true;
369 it->second.isManager = false;
370 it->second.chatroomPrefix = newPrefix;
371
372 if (it->second.remoteChatroomTimeoutEventId != nullptr)
373 m_scheduler->cancelEvent(it->second.remoteChatroomTimeoutEventId);
374 it->second.isPrint = false;
375 it->second.remoteChatroomTimeoutEventId = nullptr;
376
377 it->second.localChatroomTimeoutEventId =
378 m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
379 bind(&ChatroomDiscoveryBackend::localSessionTimeout,
380 this, chatroomName));
381 emit chatroomInfoRequest(chatroomName.toUri(), false);
382 }
383}
384
385void
386ChatroomDiscoveryBackend::onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager)
387{
388 if (isManager)
389 chatroomInfo.setManager(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET));
390 Name::Component chatroomName = chatroomInfo.getName();
391 m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
392 m_chatroomList[chatroomName].isManager = isManager;
393 m_chatroomList[chatroomName].count = 0;
394 m_chatroomList[chatroomName].info = chatroomInfo;
395 sendChatroomList();
396 onAddInRoster(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET), chatroomName);
397}
398
399void
400ChatroomDiscoveryBackend::onIdentityUpdated(const QString& identity)
401{
402 m_chatroomList.clear();
403 m_identity = Name(identity.toStdString());
404 m_userDiscoveryPrefix.clear();
405 m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
406 updatePrefixes();
407
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700408 {
409 std::lock_guard<std::mutex>lock(m_mutex);
410 m_shouldResume = true;
411 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800412
413 close();
414
415 m_face->getIoService().stop();
416}
417
418void
419ChatroomDiscoveryBackend::sendChatroomList()
420{
421 QStringList chatroomList;
422 for (const auto& chatroom : m_chatroomList) {
423 chatroomList << QString::fromStdString(chatroom.first.toUri());
424 }
425
426 emit chatroomListReady(chatroomList);
427 if (m_refreshPanelId != nullptr) {
428 m_scheduler->cancelEvent(m_refreshPanelId);
429 }
430 m_refreshPanelId = m_scheduler->scheduleEvent(REFRESH_INTERVAL,
431 [this] { sendChatroomList(); });
432}
433
434void
435ChatroomDiscoveryBackend::onWaitForChatroomInfo(const QString& chatroomName)
436{
437 auto chatroom = m_chatroomList.find(Name::Component(chatroomName.toStdString()));
438 if (chatroom != m_chatroomList.end())
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800439 emit chatroomInfoReady(chatroom->second.info, chatroom->second.isParticipant);
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800440}
441
442void
443ChatroomDiscoveryBackend::shutdown()
444{
Qiuhan Dingf22c41b2015-03-11 13:19:01 -0700445 {
446 std::lock_guard<std::mutex>lock(m_mutex);
447 m_shouldResume = false;
448 }
Qiuhan Ding43c8e162015-02-02 15:16:48 -0800449
450 close();
451
452 m_face->getIoService().stop();
453}
454
455
456} // namespace chronochat
457
458#if WAF
459#include "chatroom-discovery-backend.moc"
460#endif