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 
797  return NULL;
798  } else {
799  throw ResourceNotDeclaredException(name.toAscii().data());
800  }
801  }
802 
803  T* resource = h->get<T>();
804  if (resource == NULL) {
805  throw ResourceTypeMismatchException(name.toAscii().data(), typeid(T).name());
806  }
807 
808  return resource;
809  }
810 
819  void deleteResource(QString name);
820 
831  bool hasResource(QString name) const;
832 
841  bool usedResourcesExist(QStringList* nonExistingResources = NULL) const;
842 
843 protected:
861  virtual void resourceChanged(QString name, ResourceChangeType changeType);
862 
874  template<class T>
875  T* getResource()
876  {
877  if (!m_beingNotified) {
878  if (m_lockAcquired > 0) {
879  return NULL;
880  } else {
881  throw WrongResourceLockStatusForOperation("getResource (inside resourceChanged())", false);
882  }
883  }
884 
885  if (!m_notifiedResourceHandler->exists()) {
886  // The resource could have been deleted
887  throw ResourceNotDeclaredException(m_notifiedResourceHandler->name().toAscii().data());
888  }
889  T* resource = m_notifiedResourceHandler->get<T>();
890  if (resource == NULL) {
891  throw ResourceTypeMismatchException(m_notifiedResourceHandler->name().toAscii().data(), typeid(T).name());
892  }
893 
894  return resource;
895  }
896 
897 private:
904  void lockAll();
905 
912  void unlockAll();
913 
923  virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType);
924 
929  void removeAllNotifications();
930 
934  void forceBeingNotified();
935 
944  void lockResources(const QList<ResourceHandler*>& resources);
945 
954  void unlockResources(const QList<ResourceHandler*>& resources);
955 
965  class ResourcesMutexesLocker
966  {
967  public:
975  ResourcesMutexesLocker(ConcurrentResourcesUser *resourcesUser, const QList<ResourceHandler *>& resources) :
976  m_resourcesUser(resourcesUser),
977  m_resources(resources)
978  {
979  m_resourcesUser->lockResources(m_resources);
980  }
981 
989  ResourcesMutexesLocker(ConcurrentResourcesUser *resourcesUser, ResourceHandler* resource) :
990  m_resourcesUser(resourcesUser),
991  m_resources(QList<ResourceHandler *>() << resource)
992  {
993  m_resourcesUser->lockResources(m_resources);
994  }
995 
1002  ~ResourcesMutexesLocker()
1003  {
1004  m_resourcesUser->unlockResources(m_resources);
1005  }
1006 
1007  private:
1011  ConcurrentResourcesUser *const m_resourcesUser;
1012 
1016  QList<ResourceHandler *> m_resources;
1017  };
1018 
1026  unsigned int m_lockAcquired;
1027 
1034  bool m_beingNotified;
1035 
1042  ResourceHandler* m_notifiedResourceHandler;
1043 
1047  QMap<QString, ResourceHandler*> m_usableResources;
1048 
1052  ConcurrentResourcesUser(const ConcurrentResourcesUser& other);
1053 
1057  ConcurrentResourcesUser& operator=(const ConcurrentResourcesUser& other);
1058 
1063  friend class ResourcesLocker;
1064 };
1065 
1098 class FARSA_CONF_API ResourcesLocker
1099 {
1100 public:
1110  ResourcesLocker(ConcurrentResourcesUser *resourcesUser, bool acquireLock = true);
1111 
1117  ~ResourcesLocker();
1118 
1124  void lock();
1125 
1131  void unlock();
1132 
1133 private:
1137  ConcurrentResourcesUser *const m_resourcesUser;
1138 
1173  bool m_lockedByUs;
1174 };
1175 
1176 } // end namespace farsa
1177 
1178 #endif