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