resourcesuser.h
1 /********************************************************************************
2  * FARSA Experiments Library *
3  * Copyright (C) 2007-2012 *
4  * Gianluca Massera <emmegian@yahoo.it> *
5  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
6  * Onofrio Gigliotta <onofrio.gigliotta@istc.cnr.it> *
7  * Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it> *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the Free Software *
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
22  ********************************************************************************/
23 
24 #ifndef RESOURCESUSER_H
25 #define RESOURCESUSER_H
26 
27 #include "parametersettable.h"
28 #include "configurationconfig.h"
29 #include "configurationexceptions.h"
30 #include "resource.h"
31 #include <QMap>
32 #include <QString>
33 #include <QStringList>
34 #include <QMutexLocker>
35 #include <QSet>
36 
37 class QObject;
38 
39 namespace farsa {
40 
41 class ResourcesUser;
42 class SimpleResourcesUser;
43 class ConcurrentResourcesUser;
44 class ResourcesLocker;
45 
70 class FARSA_CONF_API ResourcesUser : public Resource
71 {
72 public:
79 
80 public:
92  virtual void shareResourcesWith(ResourcesUser* other);
93 
94 protected:
100  ResourcesUser();
101 
110  ResourcesUser(const ResourcesUser& other) throw();
111 
120  ResourcesUser& operator=(const ResourcesUser& other) throw();
121 
127  virtual ~ResourcesUser();
128 
138  virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType) = 0;
139 
144 
149  friend class ResourceHandler;
150 
156 };
157 
227 class FARSA_CONF_API SimpleResourcesUser : public ResourcesUser
228 {
229 public:
234 
244 
253  SimpleResourcesUser& operator=(const SimpleResourcesUser& other);
254 
258  virtual ~SimpleResourcesUser();
259 
271  virtual void shareResourcesWith(ResourcesUser* other);
272 
280  template<class T>
281  void declareResource(QString name, T* resource)
282  {
283  // Now we have to get the resource (creating it if necessary).
284  ResourceHandler* h = m_resources->getResource(name, true);
285 
286  // Now setting the resource. If this throws an exception we could have simply created a non-existing
287  // resource above that doesn't modify the external behaviour of this and other classes (so we can say
288  // that this function is strongly exception-safe)
289  h->set(resource);
290  }
291 
301  template<class T>
302  T* getResource(QString name)
303  {
304  // Trying to retrieve the resource
305  ResourceHandler* h = m_resources->getResource(name, false);
306 
307  if ((h == NULL) || (!h->exists())){
308  throw ResourceNotDeclaredException(name.toAscii().data());
309  }
310 
311  T* resource = h->get<T>();
312  if (resource == NULL) {
313  throw ResourceTypeMismatchException(name.toAscii().data(), typeid(T).name());
314  }
315 
316  return resource;
317  }
318 
325  void deleteResource(QString name);
326 
334  bool hasResource(QString name) const
335  {
336  return m_resources->hasResource(name);
337  }
338 
339 protected:
351  virtual void resourceChanged(QString name, ResourceChangeType changeType);
352 
365  template<class T>
366  T* getResource()
367  {
368  if (!m_beingNotified) {
369  return NULL;
370  }
371 
372  if (!m_notifiedResourceHandler->exists()) {
373  // The resource could have been deleted
374  throw ResourceNotDeclaredException(m_notifiedResourceHandler->name().toAscii().data());
375  }
376  T* resource = m_notifiedResourceHandler->get<T>();
377  if (resource == NULL) {
378  throw ResourceTypeMismatchException(m_notifiedResourceHandler->name().toAscii().data(), typeid(T).name());
379  }
380 
381  return resource;
382  }
383 
384 private:
394  virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType);
395 
400  void removeAllNotifications();
401 
405  void forceBeingNotified();
406 
413  bool m_beingNotified;
414 
421  ResourceHandler* m_notifiedResourceHandler;
422 
426  QSet<ResourceHandler*> m_observedResources;
427 };
428 
592 class FARSA_CONF_API ConcurrentResourcesUser : public ResourcesUser
593 {
594 public:
599 
603  virtual ~ConcurrentResourcesUser();
604 
615  virtual void shareResourcesWith(ResourcesUser* buddy);
616 
631  void usableResources(QStringList resources);
632 
646  void addUsableResource(QString resource);
647 
663  void addUsableResources(QStringList resources);
664 
675  void removeUsableResource(QString resource);
676 
687  void removeUsableResources(QStringList resources);
688 
696  void removeAllUsableResources();
697 
726  template<class T>
727  void declareResource(QString name, T* resource, QString lockBuddy = "")
728  {
729  if ((m_lockAcquired > 0) || m_beingNotified) {
730  throw WrongResourceLockStatusForOperation("declareResource", true);
731  }
732 
733  // Getting the lock on the resource collection
734  QMutexLocker collectionLocker(&(m_resources->getLock()));
735 
736  // Now we have to get the resource (creating it if necessary) and then acquire the lock on the resource
737  ResourceHandler* h = m_resources->getResource(name, true);
738  if (!lockBuddy.isEmpty()) {
739  ResourceHandler* lockBuddyHandler = m_resources->getResource(lockBuddy, false);
740  if ((lockBuddyHandler == NULL) || (!lockBuddyHandler->exists())) {
741  throw ResourceNotDeclaredException(lockBuddy.toAscii().data());
742  }
743  h->shareLockWith(lockBuddyHandler);
744  }
745  ResourcesMutexesLocker resourceLocker(this, h);
746 
747  // Before setting the resource, we can release the lock on Resource Manager
748  collectionLocker.unlock();
749 
750  // Now setting the resource (the lock on the resource has been acquired here and will be released after
751  // returning from the following call)
752  h->set(resource);
753  }
754 
776  template<class T>
777  T* getResource(QString name, bool* resourceExists = NULL)
778  {
779  if ((m_lockAcquired == 0) && ((!m_beingNotified) || (m_notifiedResourceHandler->name() != name))) {
780  throw WrongResourceLockStatusForOperation("getResource", false);
781  }
782 
783  if (!m_usableResources.contains(name)) {
784  throw ResourceNotUsableException(name.toAscii().data());
785  }
786 
787  // Getting the resource. It could be non-existent, we have to check again
788  if (resourceExists != NULL) {
789  // This will be set to false after the check
790  *resourceExists = true;
791  }
792  ResourceHandler* h = m_usableResources[name];
793  if (!h->exists()) {
794  if (resourceExists != NULL) {
795  *resourceExists = false;
796  } else {
797  throw ResourceNotDeclaredException(name.toAscii().data());
798  }
799  }
800 
801  T* resource = h->get<T>();
802  if (resource == NULL) {
803  throw ResourceTypeMismatchException(name.toAscii().data(), typeid(T).name());
804  }
805 
806  return resource;
807  }
808 
817  void deleteResource(QString name);
818 
829  bool hasResource(QString name) const;
830 
839  bool usedResourcesExist(QStringList* nonExistingResources = NULL) const;
840 
841 protected:
859  virtual void resourceChanged(QString name, ResourceChangeType changeType);
860 
872  template<class T>
873  T* getResource()
874  {
875  if (!m_beingNotified) {
876  if (m_lockAcquired > 0) {
877  return NULL;
878  } else {
879  throw WrongResourceLockStatusForOperation("getResource (inside resourceChanged())", false);
880  }
881  }
882 
883  if (!m_notifiedResourceHandler->exists()) {
884  // The resource could have been deleted
885  throw ResourceNotDeclaredException(m_notifiedResourceHandler->name().toAscii().data());
886  }
887  T* resource = m_notifiedResourceHandler->get<T>();
888  if (resource == NULL) {
889  throw ResourceTypeMismatchException(m_notifiedResourceHandler->name().toAscii().data(), typeid(T).name());
890  }
891 
892  return resource;
893  }
894 
895 private:
902  void lockAll();
903 
910  void unlockAll();
911 
921  virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType);
922 
927  void removeAllNotifications();
928 
932  void forceBeingNotified();
933 
942  void lockResources(const QList<ResourceHandler*>& resources);
943 
952  void unlockResources(const QList<ResourceHandler*>& resources);
953 
963  class ResourcesMutexesLocker
964  {
965  public:
973  ResourcesMutexesLocker(ConcurrentResourcesUser *resourcesUser, const QList<ResourceHandler *>& resources) :
974  m_resourcesUser(resourcesUser),
975  m_resources(resources)
976  {
977  m_resourcesUser->lockResources(m_resources);
978  }
979 
987  ResourcesMutexesLocker(ConcurrentResourcesUser *resourcesUser, ResourceHandler* resource) :
988  m_resourcesUser(resourcesUser),
989  m_resources(QList<ResourceHandler *>() << resource)
990  {
991  m_resourcesUser->lockResources(m_resources);
992  }
993 
1000  ~ResourcesMutexesLocker()
1001  {
1002  m_resourcesUser->unlockResources(m_resources);
1003  }
1004 
1005  private:
1009  ConcurrentResourcesUser *const m_resourcesUser;
1010 
1014  QList<ResourceHandler *> m_resources;
1015  };
1016 
1024  unsigned int m_lockAcquired;
1025 
1032  bool m_beingNotified;
1033 
1040  ResourceHandler* m_notifiedResourceHandler;
1041 
1045  QMap<QString, ResourceHandler*> m_usableResources;
1046 
1050  ConcurrentResourcesUser(const ConcurrentResourcesUser& other);
1051 
1055  ConcurrentResourcesUser& operator=(const ConcurrentResourcesUser& other);
1056 
1061  friend class ResourcesLocker;
1062 };
1063 
1096 class FARSA_CONF_API ResourcesLocker
1097 {
1098 public:
1108  ResourcesLocker(ConcurrentResourcesUser *resourcesUser, bool acquireLock = true);
1109 
1115  ~ResourcesLocker();
1116 
1122  void lock();
1123 
1129  void unlock();
1130 
1131 private:
1135  ConcurrentResourcesUser *const m_resourcesUser;
1136 
1171  bool m_lockedByUs;
1172 };
1173 
1174 } // end namespace farsa
1175 
1176 #endif