dataexchange.h
Go to the documentation of this file.
1 /********************************************************************************
2  * FARSA *
3  * Copyright (C) 2007-2012 *
4  * Gianluca Massera <emmegian@yahoo.it> *
5  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
6  * Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it> *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
21  ********************************************************************************/
22 
23 #ifndef DATAEXCHANGE_H
24 #define DATAEXCHANGE_H
25 
26 #include <QEvent>
27 #include <QObject>
28 #include <QSet>
29 #include <QMutex>
30 #include <QMutexLocker>
31 #include <QWaitCondition>
32 #include <QSharedData>
33 #include <QExplicitlySharedDataPointer>
34 #include <memory>
35 #include "baseexception.h"
36 #include "utilitiesexceptions.h"
37 
220 namespace farsa {
221 
222 namespace __DataExchange_internal {
223  class QueueHolderBase;
224  template <class DataType_t>
225  class QueueHolder;
226 }
227 template <class DataType_t>
229 template <class DataType_t>
231 template <class DataType_t>
233 
240 template <class DataType_t>
241 class FARSA_UTIL_TEMPLATE DataUploader
242 {
243 public:
247  typedef DataType_t DataType;
248 
253 
259  BlockUploader,
261  IncreaseQueueSize,
263  SignalUploader
264  };
265 
266 public:
277  DataUploader(unsigned int queueSize, FullQueueBehavior b);
278 
282  ~DataUploader();
283 
289  unsigned int getQueueSize() const
290  {
291  return m_queueSize;
292  }
293 
300  {
301  return m_fullQueueBehavior;
302  }
303 
309  bool downloaderPresent() const;
310 
319  unsigned int getAvailableSpace() const;
320 
328  unsigned int getNumDataInQueue() const;
329 
343  DataType* createDatum();
344 
350  void uploadDatum();
351 
358  bool datumCreatedNotUploaded() const;
359 
369  {
370  m_checkAssociationBeforeUpload = v;
371  }
372 
382  {
383  return m_checkAssociationBeforeUpload;
384  }
385 
386 private:
393  const unsigned int m_queueSize;
394 
398  const FullQueueBehavior m_fullQueueBehavior;
399 
408  QExplicitlySharedDataPointer<__DataExchange_internal::QueueHolder<DataType> > m_queue;
409 
416  bool m_checkAssociationBeforeUpload;
417 
423  DataUploader(const DataUploader<DataType>& other);
424 
430  DataUploader& operator=(const DataUploader<DataType>& other);
431 
436 };
437 
446 template <class DataType_t>
447 class FARSA_UTIL_TEMPLATE DatumToUpload
448 {
449 public:
450 
454  typedef DataType_t DataType;
455 
456 public:
463  m_uploader(uploader),
464  m_datum(m_uploader.createDatum()),
465  m_datumUploaded(false)
466  {
467  }
468 
475  {
476  uploadDatum();
477  }
478 
484  void uploadDatum()
485  {
486  if (!m_datumUploaded) {
487  m_uploader.uploadDatum();
488  m_datum = NULL;
489  m_datumUploaded = true;
490  }
491  }
492 
499  const DataType* operator->() const
500  {
501  return m_datum;
502  }
503 
509  DataType* operator->()
510  {
511  return m_datum;
512  }
513 
520  bool operator==(const DataType* other) const
521  {
522  return (m_datum == other);
523  }
524 
530  operator bool() const
531  {
532  return (m_datum != NULL);
533  }
534 
535 private:
539  DataUploader<DataType>& m_uploader;
540 
544  DataType* m_datum;
545 
549  bool m_datumUploaded;
550 
557 
563  DatumToUpload& operator=(const DatumToUpload<DataType>& other);
564 };
565 
572 template<class DataType_t>
573 class FARSA_UTIL_TEMPLATE NewDatumEvent : public QEvent
574 {
575 public:
579  static const QEvent::Type newDatumEventType = static_cast<QEvent::Type>(QEvent::User + 1);
580 
584  typedef DataType_t DataType;
585 
586 public:
593  QEvent(newDatumEventType),
594  m_downloader(downloader)
595  {
596  }
597 
601  virtual ~NewDatumEvent()
602  {
603  }
604 
612  {
613  return m_downloader;
614  }
615 
622  {
623  return m_downloader;
624  }
625 
626 private:
630  DataDownloader<DataType>* const m_downloader;
631 };
632 
637 template <class DataType_t>
638 class FARSA_UTIL_TEMPLATE NewDatumNotifiable
639 {
640 public:
644  typedef DataType_t DataType;
645 
646 public:
651  {
652  }
653 
662  virtual void newDatumAvailable(DataDownloader<DataType>* downloader) = 0;
663 };
664 
671 template <class DataType_t>
672 class FARSA_UTIL_TEMPLATE DataDownloader
673 {
674 public:
678  typedef DataType_t DataType;
679 
685  NoNotificationBlocking,
688  QtEvent,
690  Callback
691  };
692 
693 public:
701  DataDownloader(NewDatumAvailableBehavior b);
702 
710  DataDownloader(QObject* o);
711 
720 
724  ~DataDownloader();
725 
732  {
733  return m_newDatumAvailableBehavior;
734  }
735 
741  bool uploaderPresent() const;
742 
748  unsigned int getNumAvailableData() const;
749 
760  const DataType* downloadDatum();
761 
762 private:
769  void sendNotification();
770 
774  const NewDatumAvailableBehavior m_newDatumAvailableBehavior;
775 
780  QObject* const m_qoject;
781 
786  NewDatumNotifiable<DataType>* const m_newDatumNotifiable;
787 
793  QExplicitlySharedDataPointer<__DataExchange_internal::QueueHolder<DataType> > m_queue;
794 
805  mutable QMutex m_mutex;
806 
810  friend class DataUploader<DataType>;
811 
816 
823 
829  DataDownloader& operator=(const DataDownloader<DataType>& other);
830 };
831 
838 template <class UploadedData_t, class DownloadedData_t>
839 class FARSA_UTIL_TEMPLATE DataUploaderDownloader : public DataUploader<UploadedData_t>, public DataDownloader<DownloadedData_t>
840 {
841 public:
845  typedef UploadedData_t UploadedData;
846 
850  typedef DownloadedData_t DownloadedData;
851 
856 
861 
862 public:
881  DataUploaderDownloader(unsigned int uploadQueueSize, FullQueueBehavior fullQueueBehavior, NewDatumAvailableBehavior newDatumAvailableBehavior) :
882  DataUploader<UploadedData>(uploadQueueSize, fullQueueBehavior),
883  DataDownloader<DownloadedData>(newDatumAvailableBehavior)
884  {
885  }
886 
903  DataUploaderDownloader(unsigned int uploadQueueSize, FullQueueBehavior fullQueueBehavior, QObject* o) :
904  DataUploader<UploadedData>(uploadQueueSize, fullQueueBehavior),
905  DataDownloader<DownloadedData>(o)
906  {
907  }
908 
925  DataUploaderDownloader(unsigned int uploadQueueSize, FullQueueBehavior fullQueueBehavior, NewDatumNotifiable<DownloadedData>* o) :
926  DataUploader<UploadedData>(uploadQueueSize, fullQueueBehavior),
927  DataDownloader<DownloadedData>(o)
928  {
929  }
930 
935  {
936  // Nothing to do here
937  }
938 };
939 
950 class FARSA_UTIL_API GlobalUploaderDownloader
951 {
952 public:
964  template <class DataType>
965  static void associate(DataUploader<DataType>* uploader, DataDownloader<DataType>* downloader);
966 
978  template <class DataType1, class DataType2>
980 
988  template <class DataType>
989  static void detach(DataUploader<DataType>* uploader);
990 
998  template <class DataType>
999  static void detach(DataDownloader<DataType>* downloader);
1000 
1009  template <class DataType1, class DataType2>
1010  static void detach(DataUploaderDownloader<DataType1, DataType2>* uploaderDownloader);
1011 
1023  static void stopAllDataExchanges();
1024 
1025 private:
1032 
1038  static GlobalUploaderDownloader& getInstance();
1039 
1047  template <class DataType>
1048  void internalAssociate(DataUploader<DataType>* uploader, DataDownloader<DataType>* downloader);
1049 
1057  template <class DataType1, class DataType2>
1059 
1066  template <class DataType>
1067  void internalDetach(DataUploader<DataType>* uploader);
1068 
1075  template <class DataType>
1076  void internalDetach(DataDownloader<DataType>* downloader);
1077 
1084  template <class DataType1, class DataType2>
1085  void internalDetach(DataUploaderDownloader<DataType1, DataType2>* uploaderDownloader);
1086 
1092  void internalStopAllDataExchanges();
1093 
1100  void addQueueHolder(__DataExchange_internal::QueueHolderBase* queueHolder);
1101 
1108  void removeQueueHolder(__DataExchange_internal::QueueHolderBase* queueHolder);
1109 
1122  template <class DataType>
1123  void uploaderDestroyed(DataUploader<DataType>* uploader);
1124 
1135  template <class DataType>
1136  void downloaderDestroyed(DataDownloader<DataType>* downloader);
1137 
1141  QSet<__DataExchange_internal::QueueHolderBase*> m_queueHolders;
1142 
1147  QMutex m_mutex;
1148 
1152  template <class DataType_t>
1153  friend class DataUploader;
1154 
1158  template <class DataType_t>
1159  friend class DataDownloader;
1160 
1164  template <class DataType_t>
1166 
1167 private:
1174 
1181 };
1182 
1183 } // end namespace farsa
1184 
1185 // Implementation of all template members
1186 #include <QCoreApplication>
1187 #include <QMutex>
1188 #include <QLinkedList>
1189 #include <QWaitCondition>
1190 #include <QMutexLocker>
1191 #include <QtGlobal>
1192 
1193 namespace farsa {
1194 
1200 namespace __DataExchange_internal {
1210  class QueueHolderBase : public QSharedData
1211  {
1212  public:
1217  : mutex()
1218  , waitCondition()
1219  , dataExchangeStopped(false)
1220  {
1221  }
1222 
1223  public:
1228  QMutex mutex;
1229 
1235  QWaitCondition waitCondition;
1236 
1241  };
1242 
1249  template <class DataType_t>
1250  class QueueHolder : public QueueHolderBase
1251  {
1252  public:
1256  typedef DataType_t DataType;
1257 
1258  public:
1266  QueueHolder(unsigned int queueSize, DataUploader<DataType>* u)
1267  : QueueHolderBase()
1268  , queue()
1269  , availableSpace(queueSize)
1270  , numDataInQueue(0)
1272  , datumCreatedNotUploaded(false)
1273  , nextUploaderDatum(NULL)
1274  , currentDownloaderDatum(NULL)
1275  , nextUploadIt()
1276  , nextDownloadIt()
1277  , uploader(u)
1278  , downloader(NULL)
1279  {
1280  // Allocating all memory. We use auto_ptr to ensure exception safety
1281  std::auto_ptr<DataType> uploaderDatum(new DataType());
1282  std::auto_ptr<DataType> downloaderDatum(new DataType());
1283 
1284  // Explicitly using a try-catch block to be exception-safe
1285  try {
1286  for (unsigned int i = 0; i < queueSize; i++) {
1287  queue.push_back(new DataType());
1288  }
1289  } catch (...) {
1290  // If an exception is thrown, deleting all objects allocated so far
1291  foreach(DataType* d, queue) {
1292  delete d;
1293  }
1294 
1295  // Propagating exception
1296  throw;
1297  }
1298 
1299  // Now initializing the iterators for the uploader and the downloader
1300  nextUploadIt = queue.begin();
1301  nextDownloadIt = queue.begin();
1302 
1303  // Now releasing the auto_ptrs
1304  nextUploaderDatum = uploaderDatum.release();
1305  currentDownloaderDatum = downloaderDatum.release();
1306 
1307  // Adding ourself to the list of queue holders
1308  GlobalUploaderDownloader::getInstance().addQueueHolder(this);
1309  }
1310 
1317  {
1318  // Removing ourself from the list of queue holders
1319  GlobalUploaderDownloader::getInstance().removeQueueHolder(this);
1320 
1321  foreach(DataType* d, queue) {
1322  delete d;
1323  }
1324  queue.clear();
1325 
1326  delete nextUploaderDatum;
1327  delete currentDownloaderDatum;
1328  }
1329 
1330  public:
1334  QLinkedList<DataType*> queue;
1335 
1340  unsigned int availableSpace;
1341 
1345  unsigned int numDataInQueue;
1346 
1352 
1359 
1369 
1377 
1382  typename QLinkedList<DataType*>::iterator nextUploadIt;
1383 
1388  typename QLinkedList<DataType*>::iterator nextDownloadIt;
1389 
1397 
1405  };
1406 }
1407 
1408 template <class DataType_t>
1410  m_queueSize((queueSize == 0) ? 1 : queueSize),
1411  m_fullQueueBehavior(b),
1412  m_queue(new __DataExchange_internal::QueueHolder<DataType>(m_queueSize, this)),
1413  m_checkAssociationBeforeUpload(true)
1414 {
1415 }
1416 
1417 template <class DataType_t>
1419 {
1420  // Calling the function of GlobalUploaderDownloader, it will take care of removing the
1421  // association, if present
1422  GlobalUploaderDownloader::getInstance().uploaderDestroyed(this);
1423 
1424  // The queue is deleted if we are the only one referencing it (we have used
1425  // QExplicitlySharedDataPointer)
1426 }
1427 
1428 template <class DataType_t>
1430 {
1431  QMutexLocker locker(&(m_queue->mutex));
1432 
1433  return (m_queue->downloader != NULL);
1434 }
1435 
1436 template <class DataType_t>
1438 {
1439  QMutexLocker locker(&(m_queue->mutex));
1440 
1441  if (m_fullQueueBehavior == IncreaseQueueSize) {
1442  return (m_queue->availableSpace == 0) ? 1 : m_queue->availableSpace;
1443  } else {
1444  return m_queue->availableSpace;
1445  }
1446 }
1447 
1448 template <class DataType_t>
1450 {
1451  QMutexLocker locker(&(m_queue->mutex));
1452 
1453  return m_queue->numDataInQueue;
1454 }
1455 
1456 template <class DataType_t>
1458 {
1459  QMutexLocker locker(&(m_queue->mutex));
1460 
1461  // If the datum has already been created, returning the same datum again
1462  if (m_queue->datumCreatedNotUploaded) {
1463  return m_queue->nextUploaderDatum;
1464  }
1465 
1466  // Checking whether data exchange has been stopped
1467  if (m_queue->dataExchangeStopped) {
1468  return NULL;
1469  }
1470 
1471  // Checking if we are associated with a downloader if we have to
1472  if (m_checkAssociationBeforeUpload && (m_queue->downloader == NULL)) {
1474  }
1475 
1476  // Resetting the flag signalling whether the queue was full
1477  m_queue->queueFullLastDatumCreation = false;
1478 
1479  // Checking if the queue is full
1480  if (m_queue->availableSpace == 0) {
1481  // Checking that uploader and downloader iterators point to the same location (this must
1482  // always happend if we get here)
1483  Q_ASSERT(m_queue->nextUploadIt == m_queue->nextDownloadIt);
1484 
1485  // The queue is full, setting the flag
1486  m_queue->queueFullLastDatumCreation = true;
1487 
1488  // What to do depends on the FullQueueBehavior
1489  switch (m_fullQueueBehavior) {
1490  case OverrideOlder:
1491  // The next datum would go where the downloader is going to take the element, so
1492  // we have to move the downloader forward
1493  ++m_queue->nextDownloadIt;
1494  if (m_queue->nextDownloadIt == m_queue->queue.end()) {
1495  m_queue->nextDownloadIt = m_queue->queue.begin();
1496  }
1497  break;
1498  case BlockUploader:
1499  // Waiting on the wait condition, we will be woke up when the downloader downloads
1500  // an element. In that case the nextDownloadIt has been moved forward, so we can safely
1501  // use nextUploadIt
1502  m_queue->waitCondition.wait(&m_queue->mutex);
1503 
1504  // If we were woken up because data exchange has been stopped, simply returning NULL
1505  if (m_queue->dataExchangeStopped) {
1506  return NULL;
1507  }
1508  break;
1509  case IncreaseQueueSize: {
1510  // We have to add a datum to the queue at the current location. After the current
1511  // datum has been uploaded, this element will be the nextUploaderDatum
1512  std::auto_ptr<DataType> d(new DataType());
1513  m_queue->nextUploadIt = m_queue->queue.insert(m_queue->nextDownloadIt, d.get());
1514  d.release();
1515  } break;
1516  case SignalUploader:
1517  // Returning NULL to tell the uploader that there is no space
1518  return NULL;
1519  break;
1520  }
1521  }
1522 
1523  m_queue->datumCreatedNotUploaded = true;
1524 
1525  // Returning the datum to modify
1526  return m_queue->nextUploaderDatum;
1527 }
1528 
1529 template <class DataType_t>
1531 {
1532  QMutexLocker locker(&(m_queue->mutex));
1533 
1534  // If the datum hasn't been created, doing nothing
1535  if (!m_queue->datumCreatedNotUploaded) {
1536  return;
1537  }
1538 
1539  // Checking whether data exchange has been stopped
1540  if (m_queue->dataExchangeStopped) {
1541  return;
1542  }
1543 
1544  // Putting the new datum in the queue, extracting the next element and moving the uploader iterator forward
1545  DataType* tmp = *m_queue->nextUploadIt;
1546  *m_queue->nextUploadIt = m_queue->nextUploaderDatum;
1547  m_queue->nextUploaderDatum = tmp;
1548  ++m_queue->nextUploadIt;
1549  if (m_queue->nextUploadIt == m_queue->queue.end()) {
1550  m_queue->nextUploadIt = m_queue->queue.begin();
1551  }
1552 
1553  // To update the variables with available space and data in queue, we have to check if the queue was full
1554  // and then check which is the FullQueueBehavior, to undestand what has been done in createDatum()
1555  if (m_queue->queueFullLastDatumCreation) {
1556  switch (m_fullQueueBehavior) {
1557  case OverrideOlder:
1558  // Nothing has changed, the queue is still full
1559  break;
1560  case BlockUploader:
1561  // The downloader has downloaded one datum before we could add the new one, so we have to update the counters
1562  --m_queue->availableSpace;
1563  ++m_queue->numDataInQueue;
1564  break;
1565  case IncreaseQueueSize:
1566  // The queue has grown, but still there is no space left. We only have to increase numDataInQueue
1567  ++m_queue->numDataInQueue;
1568  break;
1569  case SignalUploader:
1570  // Nothing has been done (we should never get here)
1571  Q_ASSERT(false);
1572  break;
1573  }
1574  } else {
1575  --m_queue->availableSpace;
1576  ++m_queue->numDataInQueue;
1577  }
1578 
1579  m_queue->datumCreatedNotUploaded = false;
1580 
1581  // Waking up the downloader, in case it was sleeping. This doesn't conflict with sendNotification() because if the
1582  // downloader is sleeping it is not expecting any notification
1583  m_queue->waitCondition.wakeAll();
1584 
1585  // Now we have to notify the downloader
1586  if (m_queue->downloader != NULL) {
1587  // If the downloader expects a callback to be called, we have to release the lock, otherwise a deadlock
1588  // is possible if the downloader tries to get the datum from inside the callback
1589  if (m_queue->downloader->m_newDatumAvailableBehavior == DataDownloader<DataType>::Callback) {
1590  locker.unlock();
1591  }
1592 
1593  // Notifying the downloader
1594  m_queue->downloader->sendNotification();
1595  }
1596 }
1597 
1598 template <class DataType_t>
1600 {
1601  QMutexLocker locker(&(m_queue->mutex));
1602 
1603  return m_queue->datumCreatedNotUploaded;
1604 }
1605 
1606 template <class DataType_t>
1608  m_newDatumAvailableBehavior(b),
1609  m_qoject(NULL),
1610  m_newDatumNotifiable(NULL),
1611  m_queue(),
1612  m_mutex()
1613 {
1614  if ((m_newDatumAvailableBehavior != NoNotification) && (m_newDatumAvailableBehavior != NoNotificationBlocking)) {
1615  if (m_newDatumAvailableBehavior == QtEvent) {
1616  throw InvalidNewDatumAvailableBehaviorException("when the NewDatumAvailableBehavior is \"QtEvent\" you must specify the QObject that receives the event");
1617  } else if (m_newDatumAvailableBehavior == Callback) {
1618  throw InvalidNewDatumAvailableBehaviorException("when the NewDatumAvailableBehavior is \"Callback\" you must specify the NewDatumNotifiable object whose callback is called");
1619  }
1620  }
1621 }
1622 
1623 template <class DataType_t>
1625  m_newDatumAvailableBehavior(QtEvent),
1626  m_qoject(o),
1627  m_newDatumNotifiable(NULL),
1628  m_queue(),
1629  m_mutex()
1630 {
1631  if (m_qoject == NULL) {
1632  throw InvalidNewDatumAvailableBehaviorException("when the NewDatumAvailableBehavior is \"QtEvent\" you must specify a valid (i.e. not NULL) QObject");
1633  }
1634 }
1635 
1636 template <class DataType_t>
1638  m_newDatumAvailableBehavior(Callback),
1639  m_qoject(NULL),
1640  m_newDatumNotifiable(o),
1641  m_queue(),
1642  m_mutex()
1643 {
1644  if (m_newDatumNotifiable == NULL) {
1645  throw InvalidNewDatumAvailableBehaviorException("when the NewDatumAvailableBehavior is \"Callback\" you must specify a valid (i.e. not NULL) NewDatumNotifiable");
1646  }
1647 }
1648 
1649 template <class DataType_t>
1651 {
1652  // Calling the function of GlobalUploaderDownloader, it will take care of removing the
1653  // association, if present
1654  GlobalUploaderDownloader::getInstance().downloaderDestroyed(this);
1655 
1656  // The queue is deleted if we are the only one referencing it (we have used
1657  // QExplicitlySharedDataPointer)
1658 }
1659 
1660 template <class DataType_t>
1662 {
1663  QMutexLocker internalLocker(&m_mutex);
1664 
1665  if (!m_queue) {
1666  return NULL;
1667  }
1668 
1669  QMutexLocker locker(&m_queue->mutex);
1670 
1671  return (m_queue->uploader != NULL);
1672 }
1673 
1674 template <class DataType_t>
1676 {
1677  QMutexLocker internalLocker(&m_mutex);
1678 
1679  if (!m_queue) {
1681  }
1682 
1683  QMutexLocker locker(&m_queue->mutex);
1684 
1685  return m_queue->numDataInQueue;
1686 }
1687 
1688 template <class DataType_t>
1690 {
1691  QMutexLocker internalLocker(&m_mutex);
1692 
1693  // Checking if the queue is there, We allow to download even if no uploader is present to
1694  // be able to remove the last data from the queue
1695  if (!m_queue) {
1697  }
1698 
1699  QMutexLocker locker(&(m_queue->mutex));
1700 
1701  // Checking whether data exchange has been stopped
1702  if (m_queue->dataExchangeStopped) {
1703  return NULL;
1704  }
1705 
1706  // Checking if the queue is empty
1707  if (m_queue->numDataInQueue == 0) {
1708  // Checking that uploader and downloader iterators point to the same location (this must
1709  // always happend if we get here)
1710  Q_ASSERT(m_queue->nextUploadIt == m_queue->nextDownloadIt);
1711 
1712  // What to do depends on the NewDatumAvailableBehavior
1713  switch (m_newDatumAvailableBehavior) {
1714  case NoNotificationBlocking:
1715  // Waiting on the wait condition, we will be woke up when the uploader has uploaded something
1716  m_queue->waitCondition.wait(&m_queue->mutex);
1717 
1718  // If we were woken up because data exchange has been stopped, simply returning NULL
1719  if (m_queue->dataExchangeStopped) {
1720  return NULL;
1721  }
1722  break;
1723  default:
1724  // In all the other cases we return NULL to tell that no datum is available
1725  return NULL;
1726  break;
1727  }
1728  }
1729 
1730  // We can return the current datum. We have to put back in the queue the datum that was downloaded before and move
1731  // the iterator forward
1732  DataType* oldDatum = m_queue->currentDownloaderDatum;
1733  m_queue->currentDownloaderDatum = *m_queue->nextDownloadIt;
1734  *m_queue->nextDownloadIt = oldDatum;
1735  ++m_queue->nextDownloadIt;
1736  if (m_queue->nextDownloadIt == m_queue->queue.end()) {
1737  m_queue->nextDownloadIt = m_queue->queue.begin();
1738  }
1739 
1740  // Incrementing the available space and decrementing the number of data in the queue
1741  ++m_queue->availableSpace;
1742  --m_queue->numDataInQueue;
1743 
1744  // Waking up the uploader, in case it was sleeping
1745  m_queue->waitCondition.wakeAll();
1746 
1747  return m_queue->currentDownloaderDatum;
1748 }
1749 
1750 template <class DataType_t>
1752 {
1753  // What we do here depends on the NewDatumAvailableBehavior
1754  switch (m_newDatumAvailableBehavior) {
1755  case NoNotification:
1756  case NoNotificationBlocking:
1757  // Nothing to do
1758  break;
1759  case QtEvent:
1760  // Posting a QT event
1761  Q_ASSERT(m_qoject != NULL);
1762  QCoreApplication::postEvent(m_qoject, new NewDatumEvent<DataType>(this));
1763  break;
1764  case Callback:
1765  // Calling the callback
1766  Q_ASSERT(m_newDatumNotifiable != NULL);
1767  m_newDatumNotifiable->newDatumAvailable(this);
1768  break;
1769  }
1770 }
1771 
1772 template <class DataType>
1774 {
1775  getInstance().internalAssociate(uploader, downloader);
1776 }
1777 
1778 template <class DataType1, class DataType2>
1780 {
1781  getInstance().internalAssociate(first, second);
1782 }
1783 
1784 template <class DataType>
1786 {
1787  getInstance().internalDetach(uploader);
1788 }
1789 
1790 template <class DataType>
1792 {
1793  getInstance().internalDetach(downloader);
1794 }
1795 
1796 template <class DataType1, class DataType2>
1798 {
1799  getInstance().internalDetach(uploaderDownloader);
1800 }
1801 
1802 template <class DataType>
1803 void GlobalUploaderDownloader::internalAssociate(DataUploader<DataType>* uploader, DataDownloader<DataType>* downloader)
1804 {
1805  // Locking order is the same in all functions: our mutex, downloader mutex, queue mutex
1806  QMutexLocker locker(&m_mutex);
1807 
1808  // Locking the downloader mutex
1809  QMutexLocker downloaderMutexLocker(&(downloader->m_mutex));
1810 
1811  // Now also locking the queue mutex (we take it from the uploader)
1812  QMutexLocker queueMutexLocker(&(uploader->m_queue->mutex));
1813 
1814  // First of all checking that neither the uploader nor the downloader is already associated
1815  if (uploader->m_queue->downloader != NULL) {
1817  } else if (downloader->m_queue && (downloader->m_queue->uploader != NULL)) {
1818  throw UploaderDownloaderAssociationNotUniqueException(UploaderDownloaderAssociationNotUniqueException::DownloaderAlreadyAssociated);
1819  }
1820 
1821  // No need to check whether a datum has been created but not uploaded as both ends are not associated!
1822 
1823  // Creating the association. The old queue in the downloader is deleted by QExplicitlySharedDataPointer
1824  // if present
1825  uploader->m_queue->downloader = downloader;
1826  uploader->m_queue->uploader = uploader;
1827  downloader->m_queue = uploader->m_queue;
1828 
1829  // If there are data available in the queue, we must notify the downloader
1830  if (uploader->m_queue->numDataInQueue != 0) {
1831  downloader->sendNotification();
1832  }
1833 }
1834 
1835 template <class DataType1, class DataType2>
1836 void GlobalUploaderDownloader::internalAssociate(DataUploaderDownloader<DataType1, DataType2>* first, DataUploaderDownloader<DataType2, DataType1>* second)
1837 {
1838  // Locking order is the same in all functions: our mutex, downloader mutex, queue mutex
1839  QMutexLocker locker(&m_mutex);
1840 
1841  // Splitting downloaders and uploaders
1842  DataUploader<DataType1>* const firstUploader = first;
1843  DataDownloader<DataType2>* const firstDownloader = first;
1844  DataUploader<DataType2>* const secondUploader = second;
1845  DataDownloader<DataType1>* const secondDownloader = second;
1846 
1847  // Taking all locks
1848  QMutexLocker firstDownloaderMutexLocker(&(firstDownloader->m_mutex));
1849  QMutexLocker firstQueueMutexLocker(&(firstUploader->m_queue->mutex));
1850  QMutexLocker secondDownloaderMutexLocker(&(secondDownloader->m_mutex));
1851  QMutexLocker secondQueueMutexLocker(&(secondUploader->m_queue->mutex));
1852 
1853  // First of all checking that neither uploaders nor downloaders are already associated
1854  if (firstUploader->m_queue->downloader != NULL) {
1855  throw UploaderDownloaderAssociationNotUniqueException(UploaderDownloaderAssociationNotUniqueException::UploaderAlreadyAssociated);
1856  } else if (firstDownloader->m_queue && (firstDownloader->m_queue->uploader != NULL)) {
1857  throw UploaderDownloaderAssociationNotUniqueException(UploaderDownloaderAssociationNotUniqueException::DownloaderAlreadyAssociated);
1858  } else if (secondUploader->m_queue->downloader != NULL) {
1859  throw UploaderDownloaderAssociationNotUniqueException(UploaderDownloaderAssociationNotUniqueException::UploaderAlreadyAssociated);
1860  } else if (secondDownloader->m_queue && (secondDownloader->m_queue->uploader != NULL)) {
1861  throw UploaderDownloaderAssociationNotUniqueException(UploaderDownloaderAssociationNotUniqueException::DownloaderAlreadyAssociated);
1862  }
1863 
1864  // No need to check whether a datum has been created but not uploaded as both ends are not associated!
1865 
1866  // Creating the association. The old queue in downloaders is deleted by QExplicitlySharedDataPointer
1867  // if present
1868  firstUploader->m_queue->downloader = secondDownloader;
1869  firstUploader->m_queue->uploader = firstUploader;
1870  secondDownloader->m_queue = firstUploader->m_queue;
1871  secondUploader->m_queue->downloader = firstDownloader;
1872  secondUploader->m_queue->uploader = secondUploader;
1873  firstDownloader->m_queue = secondUploader->m_queue;
1874 
1875  // If there are data available in the queue, we must notify the downloader
1876  if (firstUploader->m_queue->numDataInQueue != 0) {
1877  secondDownloader->sendNotification();
1878  }
1879  if (secondUploader->m_queue->numDataInQueue != 0) {
1880  firstDownloader->sendNotification();
1881  }
1882 }
1883 
1884 template <class DataType>
1885 void GlobalUploaderDownloader::internalDetach(DataUploader<DataType>* uploader)
1886 {
1887  // Locking order is the same in all functions: our mutex, downloader mutex, queue mutex
1888  // Here we must take the downloader from the queue and we do this without locking. This
1889  // is fine because the pointer to the downloader in the queue is protected by the mutex
1890  // in GlobalUploaderDownloader (which is locked first)
1891  QMutexLocker locker(&m_mutex);
1892 
1893  // If no association is present, returning directly
1894  DataDownloader<DataType>* const downloader = uploader->m_queue->downloader;
1895  if (downloader == NULL) {
1896  return;
1897  }
1898 
1899  // Locking the downloader mutex
1900  QMutexLocker downloaderMutexLocker(&(downloader->m_mutex));
1901 
1902  // Now also locking the queue mutex (we take it from the uploader)
1903  QMutexLocker queueMutexLocker(&(uploader->m_queue->mutex));
1904 
1905  // Removing the association
1906  uploader->m_queue->downloader = NULL;
1907 
1908  // Unlocking the lock on the queue because here it could be destroyed and if it
1909  // isn't, the lock is not necessary
1910  queueMutexLocker.unlock();
1911  downloader->m_queue.reset();
1912 }
1913 
1914 template <class DataType>
1915 void GlobalUploaderDownloader::internalDetach(DataDownloader<DataType>* downloader)
1916 {
1917  // Locking order is the same in all functions: our mutex, downloader mutex, queue mutex
1918  QMutexLocker locker(&m_mutex);
1919 
1920  // Locking the downloader mutex
1921  QMutexLocker downloaderMutexLocker(&(downloader->m_mutex));
1922 
1923  // If no association is present, returning directly
1924  if (!downloader->m_queue) {
1925  return;
1926  }
1927 
1928  // Now also locking the queue mutex (we take it from the downloader)
1929  QMutexLocker queueMutexLocker(&(downloader->m_queue->mutex));
1930 
1931  // Removing the association
1932  downloader->m_queue->downloader = NULL;
1933 
1934  // Unlocking the lock on the queue because here it could be destroyed and if it
1935  // isn't, the lock is not necessary
1936  queueMutexLocker.unlock();
1937  downloader->m_queue.reset();
1938 }
1939 
1940 template <class DataType1, class DataType2>
1941 void GlobalUploaderDownloader::internalDetach(DataUploaderDownloader<DataType1, DataType2>* uploaderDownloader)
1942 {
1943  // Locking order is the same in all functions: our mutex, downloader mutex, queue mutex
1944  // Here we must take the downloader associated with the uploader in uploaderDownloader from
1945  // the queue and we do this without locking. This is fine because the pointer to the downloader
1946  // in the queue is protected by the mutex in GlobalUploaderDownloader (which is locked first)
1947  QMutexLocker locker(&m_mutex);
1948 
1949  // Splitting downloader and uploader
1950  DataUploader<DataType1>* const uploader = uploaderDownloader;
1951  DataDownloader<DataType2>* const downloader = uploaderDownloader;
1952 
1953  // Taking all locks. To be able to use RAII with the mutex of the downloader associated with
1954  // the uploader of uploaderDownloader and with the queue associated with the downloader of
1955  // uploaderDownloader (which may not exist), we use an std::auto_ptr
1956  std::auto_ptr<QMutexLocker> otherDownloaderMutexLocker;
1957  DataDownloader<DataType1>* const otherDownloader = uploader->m_queue->downloader;
1958  if (otherDownloader != NULL) {
1959  otherDownloaderMutexLocker.reset(new QMutexLocker(&(otherDownloader->m_mutex)));
1960  }
1961  QMutexLocker queueMutexLocker(&(uploader->m_queue->mutex));
1962  QMutexLocker downloaderMutexLocker(&(downloader->m_mutex));
1963  std::auto_ptr<QMutexLocker> otherQueueMutexLocker;
1964  if (downloader->m_queue) {
1965  otherQueueMutexLocker.reset(new QMutexLocker(&(downloader->m_queue->mutex)));
1966  }
1967 
1968  // Removing all associations
1969  uploader->m_queue->downloader = NULL;
1970  if (otherDownloader != NULL) {
1971  // Unlocking the lock on the queue because here it could be destroyed and if it
1972  // isn't, the lock is not necessary
1973  otherDownloaderMutexLocker->unlock();
1974  otherDownloader->m_queue.reset();
1975  }
1976  if (downloader->m_queue) {
1977  downloader->m_queue->downloader = NULL;
1978 
1979  // Unlocking the lock on the queue because here it could be destroyed and if it
1980  // isn't, the lock is not necessary
1981  otherQueueMutexLocker->unlock();
1982  downloader->m_queue.reset();
1983  }
1984 }
1985 
1986 template <class DataType>
1987 void GlobalUploaderDownloader::uploaderDestroyed(DataUploader<DataType>* uploader)
1988 {
1989  // Locking order is the same in all functions: our mutex, downloader mutex, queue mutex
1990  // Here we must take the downloader from the queue and we do this without locking. This
1991  // is fine because the pointer to the downloader in the queue is protected by the mutex
1992  // in GlobalUploaderDownloader (which is locked first)
1993  QMutexLocker locker(&m_mutex);
1994 
1995  // If no association is present, returning directly
1996  DataDownloader<DataType>* const downloader = uploader->m_queue->downloader;
1997  if (downloader == NULL) {
1998  return;
1999  }
2000 
2001  // Locking the downloader mutex
2002  QMutexLocker downloaderMutexLocker(&(downloader->m_mutex));
2003 
2004  // Now also locking the queue mutex (we take it from the uploader)
2005  QMutexLocker queueMutexLocker(&(uploader->m_queue->mutex));
2006 
2007  // Removing the association
2008  uploader->m_queue->uploader = NULL;
2009 
2010  // Unlocking the lock on the queue because here it could be destroyed and if it
2011  // isn't, the lock is not necessary
2012  queueMutexLocker.unlock();
2013  uploader->m_queue.reset();
2014 }
2015 
2016 template <class DataType>
2017 void GlobalUploaderDownloader::downloaderDestroyed(DataDownloader<DataType>* downloader)
2018 {
2019  // Here we can simply call detach on the downloader
2020  internalDetach(downloader);
2021 }
2022 
2023 } // end namespace farsa
2024 
2025 #endif
The event sent to downloader when a new datum is ready.
Definition: dataexchange.h:573
NewDatumAvailableBehavior
The possible behaviors when a new datum arrives.
Definition: dataexchange.h:683
bool uploaderPresent() const
Returns true if we are associated with an uploader.
virtual ~NewDatumEvent()
Destructor.
Definition: dataexchange.h:601
static void detach(DataUploader< DataType > *uploader)
Removes an association.
const DataType * operator->() const
Overloading of the -> operator to access the datum (const version)
Definition: dataexchange.h:499
DataUploaderDownloader(unsigned int uploadQueueSize, FullQueueBehavior fullQueueBehavior, QObject *o)
Constructor.
Definition: dataexchange.h:903
bool operator==(const DataType *other) const
Comparison operator with a pointer to DataType.
Definition: dataexchange.h:520
~DataUploaderDownloader()
Destructor.
Definition: dataexchange.h:934
The exception thrown when using invalid combinations of NewDatumAvailableBehavior and objects for not...
FullQueueBehavior getFullQueueBehavior() const
Returns the FullQueueBehavior.
Definition: dataexchange.h:299
DataUploader< DataType > * uploader
The uploader associated with this queue.
unsigned int availableSpace
The number of data the queue can hold before becoming full.
void uploadDatum()
Uploads the datum.
Definition: dataexchange.h:484
FullQueueBehavior
The possible behaviors when the queue is full.
Definition: dataexchange.h:257
The parent of the class containing the queue and all related elements.
DataDownloader< DataType > * downloader
The downloader associated with this queue.
QMutex mutex
The mutex protecting from concurrent accesses to data in this object.
bool datumCreatedNotUploaded() const
Returns true if a new datum has been created but not uploaded (i.e. createDatum() has been called but...
~DataDownloader()
Destructor.
unsigned int getNumAvailableData() const
Returns the number of available data.
A class for bi-directional communication.
Definition: dataexchange.h:839
A macro to deprecate functions.
The class used to download data.
Definition: dataexchange.h:230
The exception thrown when the association between uploader and downloader is not 1:1.
bool downloaderPresent() const
Returns true if we are associated with a downloader.
QLinkedList< DataType * >::iterator nextUploadIt
The iterator to the element of the queue where the \ next datum uploaded by the uploader will be put...
const DataDownloader< DataType > * getDownloader() const
Returns the downloader object which triggered this event (const version)
Definition: dataexchange.h:611
DataType_t DataType
The type of data being exchanged.
Definition: dataexchange.h:247
DataType_t DataType
The type of data being exchanged.
Definition: dataexchange.h:454
void uploadDatum()
Adds the current datum to the queue.
DataType * createDatum()
Returns a pointer to an object that will be the next datum to upload.
DatumToUpload(DataUploader< DataType > &uploader)
Constructor.
Definition: dataexchange.h:462
QueueHolder(unsigned int queueSize, DataUploader< DataType > *u)
Constructor.
unsigned int getNumDataInQueue() const
Returns the number of data currently in the queue.
void checkAssociationBeforeUpload(bool v)
Sets whether an exception has to be thrown if the user tries to upload a datum but no association is ...
Definition: dataexchange.h:368
DataUploader(unsigned int queueSize, FullQueueBehavior b)
Constructor.
DataUploader< UploadedData >::FullQueueBehavior FullQueueBehavior
A typedef to easily access the FullQueueBehavior type.
Definition: dataexchange.h:855
The class used to upload data.
Definition: dataexchange.h:241
DataUploaderDownloader(unsigned int uploadQueueSize, FullQueueBehavior fullQueueBehavior, NewDatumNotifiable< DownloadedData > *o)
Constructor.
Definition: dataexchange.h:925
QLinkedList< DataType * > queue
The queue of data.
The interface for classes that want to be notified when a new datum is available. ...
Definition: dataexchange.h:638
DataDownloader(NewDatumAvailableBehavior b)
Constructor.
DataType_t DataType
The type of data being exchanged.
Definition: dataexchange.h:584
DataDownloader< DownloadedData >::NewDatumAvailableBehavior NewDatumAvailableBehavior
A typedef to easily access the NewDatumAvailableBehavior type.
Definition: dataexchange.h:860
DataType * operator->()
Overloading of the -> operator to access the datum.
Definition: dataexchange.h:509
DataType * currentDownloaderDatum
The datum the downloader is currently using.
The exception thrown when trying to create a datum or download without an uploader/downloader associa...
unsigned int getQueueSize() const
Returns the queue size.
Definition: dataexchange.h:289
QWaitCondition waitCondition
The wait condition used to wake the blocked downloader when a new datum arrives or the uploader when ...
const DataType * downloadDatum()
Returns a pointer to the next datum.
virtual ~NewDatumNotifiable()
Destructor.
Definition: dataexchange.h:650
UploadedData_t UploadedData
The type of data being uploaded by this class.
Definition: dataexchange.h:845
DataType_t DataType
The type of data being exchanged.
Definition: dataexchange.h:644
QLinkedList< DataType * >::iterator nextDownloadIt
The position in the queue that will be read next by the downloader.
bool queueFullLastDatumCreation
Whether the queue was full the last time a datum was created.
DownloadedData_t DownloadedData
The type of data being downloaded by this class.
Definition: dataexchange.h:850
NewDatumEvent(DataDownloader< DataType > *downloader)
Constructor.
Definition: dataexchange.h:592
DataUploaderDownloader(unsigned int uploadQueueSize, FullQueueBehavior fullQueueBehavior, NewDatumAvailableBehavior newDatumAvailableBehavior)
Constructor.
Definition: dataexchange.h:881
The class used to implement RAII for data uploading.
Definition: dataexchange.h:228
bool dataExchangeStopped
If true no data exchange is possible.
NewDatumAvailableBehavior getNewDatumAvailableBehavior() const
Returns the NewDatumAvailableBehavior.
Definition: dataexchange.h:731
DataDownloader< DataType > * getDownloader()
Returns the downloader object which triggered this event.
Definition: dataexchange.h:621
bool associationBeforeUploadChecked() const
Returns true if an exception is thrown if the user tries to upload a datum but no association is pres...
Definition: dataexchange.h:381
bool datumCreatedNotUploaded
True if a new datum has been created but not uploaded (i.e. createDatum() in the uploader has been ca...
DataType * nextUploaderDatum
The next datum the uploader will use.
DataType_t DataType
The type of data being exchanged.
Definition: dataexchange.h:678
unsigned int numDataInQueue
The number of data currently in the queue.
~DatumToUpload()
Destructor.
Definition: dataexchange.h:474
farsa::DatumToUpload< DataType > DatumToUpload
A typedef to use the correct DatumToUpload.
Definition: dataexchange.h:252
DataType_t DataType
The type of data being exchanged.
static void associate(DataUploader< DataType > *uploader, DataDownloader< DataType > *downloader)
The function to associate an uploader and a downloader.
~DataUploader()
Destructor.
The class containing the queue and all related elements.
Definition: dataexchange.h:225
unsigned int getAvailableSpace() const
Returns the number of data the queue can hold before becoming full.
The class to create or remove associations and to wake all sleeping uploaders and downloaders...
Definition: dataexchange.h:950