resourcesuser.cpp
1 /***************************************************************************
2  * Copyright (C) 2008 by Tomassino Ferrauto *
3  * t_ferrauto@yahoo.it *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
20 
21 #include "resourcesuser.h"
22 #include <memory>
23 
24 namespace farsa {
25 
26 namespace {
27  // This class is needed so that the status of m_beingNotified is exception safe (i.e. it is reset
28  // to false if an exception gets thrown inside the handler for resource change). This is used both
29  // by SimpleResourcesUser and ConcurrentResourcesUser
30  class BeingNotifiedRAII
31  {
32  public:
33  BeingNotifiedRAII(bool* beingNotified) :
34  m_beingNotified(beingNotified)
35  {
36  *m_beingNotified = true;
37  }
38 
39  ~BeingNotifiedRAII()
40  {
41  *m_beingNotified = false;
42  }
43 
44  private:
45  bool* const m_beingNotified;
46  };
47 }
48 
50 {
51  if (other != NULL) {
52  m_resources = other->m_resources;
53  } else {
54  // This is exception safe (the assignment operator doesn't throw and if the constructor
55  // throws m_resources doesn't change)
57  }
58 }
59 
61  m_resources()
62 {
63 }
64 
66  m_resources(other.m_resources)
67 {
68 }
69 
71 {
72  if (&other == this) {
73  return *this;
74  }
75 
76  m_resources = other.m_resources;
77 
78  return *this;
79 }
80 
82 {
83  // Nothing to do here
84 }
85 
87  ResourcesUser(),
88  m_beingNotified(false),
89  m_notifiedResourceHandler(NULL),
90  m_observedResources()
91 {
92 }
93 
95  ResourcesUser(other),
96  m_beingNotified(false), // When we are created we can't be in the resource change handler
97  m_notifiedResourceHandler(NULL),
98  m_observedResources(other.m_observedResources)
99 {
100  // This is not exception safe. It is difficult to make this exception safe because QT aren't (and here we
101  // have to deal with QT containers)
102 
103  // We have to be notified of changes in the same set of resources as other.
104  forceBeingNotified();
105 }
106 
108 {
109  // This is not exception safe. It is difficult to make this exception safe because QT aren't (and here we
110  // have to deal with QT containers)
111 
112  if (&other == this) {
113  return *this;
114  }
115 
116  // First of all we have to stop being notified for old resources
117  removeAllNotifications();
118 
119  // Calling parent function
121 
122  // Now we can copy the set of resources for which notification is reuested. m_beingNotified and
123  // m_notifiedResourceHandler are not changed as we could be inside the notification handler
124  // (that would be weird, though)
125  m_observedResources = other.m_observedResources;
126 
127  // We have to be notified of changes in the same same set of resources as other
128  forceBeingNotified();
129 
130  return *this;
131 }
132 
134 {
135  removeAllNotifications();
136 }
137 
139 {
140  // Now we have to stop being notified for old resources
141  removeAllNotifications();
142 
143  // Calling parent function
145 }
146 
148 {
149  // Trying to retrieve the resource
150  ResourceHandler* h = m_resources->getResource(name, false);
151 
152  if ((h == NULL) || (!h->exists())){
153  throw ResourceNotDeclaredException(name.toAscii().data());
154  }
155 
156  // Removing the resource
157  h->unset();
158 }
159 
161 {
162  // The default implementation does nothing
163 }
164 
165 void SimpleResourcesUser::notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType)
166 {
167  BeingNotifiedRAII beingNotifiedRAII(&m_beingNotified);
168  m_notifiedResourceHandler = resource;
169 
170  resourceChanged(m_notifiedResourceHandler->name(), changeType);
171 }
172 
173 void SimpleResourcesUser::removeAllNotifications()
174 {
175  // Functions using this are not exception safe and its hard to make them exception-safe
176  // because QT aren't...
177  foreach (ResourceHandler* h, m_observedResources) {
178  h->removeNotifee(this);
179  }
180 
181  m_observedResources.clear();
182 }
183 
184 void SimpleResourcesUser::forceBeingNotified()
185 {
186  // Functions using this are not exception safe and its hard to make them exception-safe
187  // because QT aren't...
188  foreach (ResourceHandler* h, m_observedResources) {
189  h->addNotifee(this);
190  }
191 }
192 
194  ResourcesUser(),
195  m_lockAcquired(0),
196  m_beingNotified(false),
197  m_notifiedResourceHandler(NULL),
198  m_usableResources()
199 {
200  // Here we need the ResourceCollectionHolder object to use locks
201  m_resources.setUseLock(true);
202 }
203 
205 {
206  // Removing from the notification list of all resources we were observing
207  removeAllNotifications();
208 
209  m_usableResources.clear();
210 }
211 
213 {
214  if ((m_lockAcquired > 0) || m_beingNotified) {
215  throw WrongResourceLockStatusForOperation("shareResourcesWith", true);
216  }
217 
218  // If other is NULL we do not get any lock on other resources, otherwise we have to. The two
219  // parts of the if are similar but trying to merge them creates quite some problems (with locks and
220  // exception safety), so I leave them separated
221  if (other == NULL) {
222  // Acquiring the lock on old resources to remove notifications, then we can release it. I'm doing
223  // it like this because I'm not sure deleting the mutex before destroying the QMutexLocker is
224  // ok (even if the mutex is unlocked). The mutex could be destroyed together with the corresponding
225  // ResourceCollection in ResourcesUser::shareResourcesWith(). Here we also get the list of name of
226  // existing resource that we use
227  QSet<QString> m_existingResourcesInOldCollection;
228  {
229  QMutexLocker oldCollectionLocker(&(m_resources->getLock()));
230 
231  // Removing all notifications on old resources
232  removeAllNotifications();
233 
234  for (QMap<QString, ResourceHandler*>::const_iterator it = m_usableResources.constBegin(); it != m_usableResources.constEnd(); ++it) {
235  if (it.value()->exists()) {
236  m_existingResourcesInOldCollection.insert(it.key());
237  }
238  }
239  }
240 
241  // Calling parent function.
243 
244  // Taking the lock on the newly created resource collection
245  QMutexLocker newCollectionLocker(&(m_resources->getLock()));
246 
247  // Now we have to update the m_usableResources map to point the the new resource handlers. No resource
248  // still esists, we create empty resourced here
249  for (QMap<QString, ResourceHandler*>::iterator it = m_usableResources.begin(); it != m_usableResources.end(); ++it) {
250  it.value() = m_resources->getResource(it.key(), true);
251  it.value()->addNotifee(this);
252  }
253 
254  // Before sending notifications, we have to get the lock on all resources and to release the lock on the
255  // resource collection
256  ResourcesMutexesLocker resourcesLocker(this, m_usableResources.values());
257 
258  // Now we can unlock the mutex on the Resource Manager
259  newCollectionLocker.unlock();
260 
261  // Now we can send all notifications. All previously existing resources have been deleted
262  foreach (QString name, m_existingResourcesInOldCollection) {
263  notifyResourceChange(m_usableResources[name], Deleted);
264  }
265  } else {
266  // Acquiring lock on new resources
267  QMutexLocker newCollectionLocker(&(other->m_resources->getLock()));
268 
269  // Acquiring the lock on old resources to remove notifications, then we can release it. I'm doing
270  // it like this because I'm not sure deleting the mutex before destroying the QMutexLocker is
271  // ok (even if the mutex is unlocked). The mutex could be destroyed together with the corresponding
272  // ResourceCollection in ResourcesUser::shareResourcesWith(). Here we also get the list of name of
273  // existing resource that we use
274  QSet<QString> m_existingResourcesInOldCollection;
275  {
276  QMutexLocker oldCollectionLocker(&(m_resources->getLock()));
277 
278  // Removing all notifications on old resources
279  removeAllNotifications();
280 
281  for (QMap<QString, ResourceHandler*>::const_iterator it = m_usableResources.constBegin(); it != m_usableResources.constEnd(); ++it) {
282  if (it.value()->exists()) {
283  m_existingResourcesInOldCollection.insert(it.key());
284  }
285  }
286  }
287 
288  // Calling parent function to actually share resources. Here the lock on new resources would be taken
289  // again (but the mutex is not recursive), so we have to temporarly prevent m_resources from locking
290  m_resources.setUseLock(false);
292  m_resources.setUseLock(true);
293 
294  // Now we have to update the m_usableResources map to point the the new resource handlers. We also create
295  // the set of names of resources that exists in the new collection
296  QSet<QString> m_existingResourcesInNewCollection;
297  for (QMap<QString, ResourceHandler*>::iterator it = m_usableResources.begin(); it != m_usableResources.end(); ++it) {
298  it.value() = m_resources->getResource(it.key(), true);
299  it.value()->addNotifee(this);
300 
301  if (it.value()->exists()) {
302  m_existingResourcesInNewCollection.insert(it.key());
303  }
304  }
305 
306  // Before sending notifications, we have to get the lock on all resources and to release the lock on the
307  // resource collection
308  ResourcesMutexesLocker resourcesLocker(this, m_usableResources.values());
309 
310  // Now we can unlock the mutex on the Resource Manager
311  newCollectionLocker.unlock();
312 
313  // Now we can send all notifications. The changeType to use depends on the status of the resource with a given
314  // name in the new and old set: if a resource existed in the old set and not in the new one, changeType will be
315  // Deleted, if it existed in the old set and is also present in the new one, changeType will be Modified, if it
316  // didn't exist in the old set but is present in the new one, changeType will be Created.
317  const QSet<QString> deletedResources = m_existingResourcesInOldCollection - m_existingResourcesInNewCollection;
318  const QSet<QString> modifiedResources = m_existingResourcesInOldCollection & m_existingResourcesInNewCollection;
319  const QSet<QString> createdResources = m_existingResourcesInNewCollection - m_existingResourcesInOldCollection;
320 
321  // Actually sending all notifications
322  foreach (QString name, deletedResources) {
323  notifyResourceChange(m_usableResources[name], Deleted);
324  }
325  foreach (QString name, modifiedResources) {
326  notifyResourceChange(m_usableResources[name], Modified);
327  }
328  foreach (QString name, createdResources) {
329  notifyResourceChange(m_usableResources[name], Created);
330  }
331  }
332 }
333 
334 void ConcurrentResourcesUser::usableResources(QStringList resources)
335 {
336  if ((m_lockAcquired > 0) || m_beingNotified) {
337  throw WrongResourceLockStatusForOperation("usableResources", true);
338  }
339 
340  // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
341  QMutexLocker collectionLocker(&(m_resources->getLock()));
342 
343  // Removing old resources
344  m_usableResources.clear();
345 
346  foreach (QString resource, resources) {
347  // Here we create a Resource Handler if the resource doesn't exists
348  ResourceHandler* h = m_resources->getResource(resource, true);
349 
350  h->addNotifee(this);
351  m_usableResources[resource] = h;
352  }
353 
354  // Getting the lock on all new resources to send notifications. There is a little overhead, here,
355  // because we get the lock also for non-existing resources (that are not notified, though)
356  ResourcesMutexesLocker resourcesLocker(this, m_usableResources.values());
357 
358  // Now we can unlock the mutex on the Resource Manager
359  collectionLocker.unlock();
360 
361  // Notifying that all the new resources exist
362  foreach (ResourceHandler* h, m_usableResources) {
363  if (h->exists()) {
364  notifyResourceChange(h, Created);
365  }
366  }
367 }
368 
370 {
371  if ((m_lockAcquired > 0) || m_beingNotified) {
372  throw WrongResourceLockStatusForOperation("addUsableResource", true);
373  }
374 
375  // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
376  QMutexLocker collectionLocker(&(m_resources->getLock()));
377 
378  // Here we create a Resource Handler if the resource doesn't exists
379  ResourceHandler* h = m_resources->getResource(resource, true);
380 
381  h->addNotifee(this);
382  m_usableResources[resource] = h;
383 
384  // If the resource exists, sending notification
385  if (h->exists()) {
386  // Taking the lock on the resource because we have to send a notification
387  ResourcesMutexesLocker resourceLocker(this, h);
388 
389  // Now we can unlock the mutex on the Resource Manager
390  collectionLocker.unlock();
391 
392  // Notifying that the resource exists
393  notifyResourceChange(h, Created);
394  }
395 }
396 
398 {
399  if ((m_lockAcquired > 0) || m_beingNotified) {
400  throw WrongResourceLockStatusForOperation("addUsableResources", true);
401  }
402 
403  // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
404  QMutexLocker collectionLocker(&(m_resources->getLock()));
405 
406  // Adding all new resources
407  QList<ResourceHandler *> newResources;
408  foreach (QString resource, resources) {
409  // Here we create a Resource Handler if the resource doesn't exists
410  ResourceHandler* h = m_resources->getResource(resource, true);
411 
412  h->addNotifee(this);
413  m_usableResources[resource] = h;
414 
415  // Saving the list of the new resources handlers for notifications
416  newResources.append(h);
417  }
418 
419  // Getting the lock on all new resources to send notifications. There is a little overhead, here,
420  // because we get the lock also for non-existing resources (that are not notified, though)
421  ResourcesMutexesLocker resourcesLocker(this, newResources);
422 
423  // Now we can unlock the mutex on the Resource Manager
424  collectionLocker.unlock();
425 
426  // Notifying that all the new resources exist
427  foreach (ResourceHandler* h, newResources) {
428  if (h->exists()) {
429  notifyResourceChange(h, Created);
430  }
431  }
432 }
433 
435 {
436  if ((m_lockAcquired > 0) || m_beingNotified) {
437  throw WrongResourceLockStatusForOperation("removeUsableResource", true);
438  }
439 
440  if (m_usableResources.contains(resource)) {
441  // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
442  QMutexLocker collectionLocker(&(m_resources->getLock()));
443 
444  // The ResourceHandler should exist, if not there is a bug and this function will crash
445  ResourceHandler* h = m_resources->getResource(resource, false);
446  h->removeNotifee(this);
447 
448  m_usableResources.remove(resource);
449 
450  // Now notifying the resource is no longer available. First taking the lock on the resource
451  ResourcesMutexesLocker resourceLocker(this, h);
452 
453  // Now we can unlock the mutex on the Resource Manager
454  collectionLocker.unlock();
455 
456  // Notifying that the resource exists
457  notifyResourceChange(h, Deleted);
458  }
459 }
460 
462 {
463  if ((m_lockAcquired > 0) || m_beingNotified) {
464  throw WrongResourceLockStatusForOperation("removeUsableResources", true);
465  }
466 
467  // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
468  QMutexLocker collectionLocker(&(m_resources->getLock()));
469 
470  // Deleting all resources
471  QList<ResourceHandler *> deletedResources;
472  foreach (QString resource, resources) {
473  if (!m_usableResources.contains(resource)) {
474  continue;
475  }
476 
477  // Taking the resource handler and then removing the resource (the resource must exist, otherwise there is a
478  // bug and this will crash)
479  ResourceHandler* h = m_resources->getResource(resource, false);
480  h->removeNotifee(this);
481 
482  m_usableResources.remove(resource);
483 
484  // Saving the list of the deleted resources handlers for notifications
485  deletedResources.append(h);
486  }
487 
488  // Getting the lock on all deleted resources to send notifications. There is a little overhead, here,
489  // because we get the lock also for non-existing resources (that are not notified, though)
490  ResourcesMutexesLocker resourcesLocker(this, deletedResources);
491 
492  // Now we can unlock the mutex on the Resource Manager
493  collectionLocker.unlock();
494 
495  // Notifying that all the resources that exist
496  foreach (ResourceHandler* h, deletedResources) {
497  if (h->exists()) {
498  notifyResourceChange(h, Deleted);
499  }
500  }
501 }
502 
504 {
505  if ((m_lockAcquired > 0) || m_beingNotified) {
506  throw WrongResourceLockStatusForOperation("removeAllUsableResources", true);
507  }
508 
509  // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
510  QMutexLocker collectionLocker(&(m_resources->getLock()));
511 
512  // Deleting all resources
513  foreach (ResourceHandler* h, m_usableResources) {
514  h->removeNotifee(this);
515  }
516 
517  QList<ResourceHandler *> deletedResources = m_usableResources.values();
518  // Clearing the list of resources we use
519  m_usableResources.clear();
520 
521  // Getting the lock on all deleted resources to send notifications. There is a little overhead, here,
522  // because we get the lock also for non-existing resources (that are not notified, though)
523  ResourcesMutexesLocker resourcesLocker(this, deletedResources);
524 
525  // Now we can unlock the mutex on the Resource Manager
526  collectionLocker.unlock();
527 
528  // Notifying that all the resources that exist
529  foreach (ResourceHandler* h, deletedResources) {
530  if (h->exists()) {
531  notifyResourceChange(h, Deleted);
532  }
533  }
534 }
535 
537 {
538  if ((m_lockAcquired > 0) || m_beingNotified) {
539  throw WrongResourceLockStatusForOperation("deleteResource", true);
540  }
541 
542  // Getting the lock on the resource collection
543  QMutexLocker collectionLocker(&(m_resources->getLock()));
544 
545  // Now we have to get the resource and then acquire the lock on the resource if it exists
546  ResourceHandler* h = m_resources->getResource(name, false);
547  if ((h == NULL) || (!h->exists())) {
548  throw ResourceNotDeclaredException(name.toAscii().data());
549  }
550  ResourcesMutexesLocker resourceLocker(this, h);
551 
552  // Before setting the resource, we can release the lock on Resource Manager
553  collectionLocker.unlock();
554 
555  // Now removing the resource
556  h->unset();
557 }
558 
559 bool ConcurrentResourcesUser::hasResource(QString name) const
560 {
561  if ((m_lockAcquired > 0) || m_beingNotified) {
562  throw WrongResourceLockStatusForOperation("hasResource", true);
563  }
564 
565  QMutexLocker locker(&(m_resources->getLock()));
566 
567  return m_resources->hasResource(name);
568 }
569 
570 bool ConcurrentResourcesUser::usedResourcesExist(QStringList* nonExistingResources) const
571 {
572  if ((m_lockAcquired > 0) || m_beingNotified) {
573  throw WrongResourceLockStatusForOperation("usedResourcesExist", true);
574  }
575 
576  QMutexLocker locker(&(m_resources->getLock()));
577 
578  bool allExist = true;
579 
580  foreach (ResourceHandler* h, m_usableResources) {
581  if (!h->exists()) {
582  allExist = false;
583  if (nonExistingResources != NULL) {
584  nonExistingResources->append(h->name());
585  } else {
586  // If we don't have to return the list of non-existing resource, we can exit
587  // from the cicle as soon as we find a resource that doesn't exists
588  break;
589  }
590  }
591  }
592 
593  return allExist;
594 }
595 
597 {
598  // The default implementation of this function does nothing
599 }
600 
601 void ConcurrentResourcesUser::lockAll()
602 {
603  // Locking all resources. To avoid deadlocks here we first acquire the lock on the Resource Manager
604  QMutexLocker locker(&(m_resources->getLock()));
605 
606  // Locking all resources
607  lockResources(m_usableResources.values());
608 
609  ++m_lockAcquired;
610 }
611 
612 void ConcurrentResourcesUser::unlockAll()
613 {
614  // Unlocking all resources. Here we have to take the mutex because the unlock of resources must be
615  // atomic (otherwise a lockAll() call can find a resource locked even if we are here because we haven't
616  // unlocked a certain resource yet and he could then miss the wake of the wait condition)
617  QMutexLocker locker(&(m_resources->getLock()));
618 
619  // Unlocking all resources and signalling threads on the wait condition
620  unlockResources(m_usableResources.values());
621 
622  --m_lockAcquired;
623 
624  // The mutex will be unlocked here
625 }
626 
627 void ConcurrentResourcesUser::notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType)
628 {
629  BeingNotifiedRAII beingNotifiedRAII(&m_beingNotified);
630  m_notifiedResourceHandler = resource;
631 
632  resourceChanged(m_notifiedResourceHandler->name(), changeType);
633 }
634 
635 void ConcurrentResourcesUser::removeAllNotifications()
636 {
637  // Functions using this are not exception safe and its hard to make them exception-safe
638  // because QT aren't...
639  foreach (ResourceHandler* h, m_usableResources) {
640  h->removeNotifee(this);
641  }
642 }
643 
644 void ConcurrentResourcesUser::forceBeingNotified()
645 {
646  // Functions using this are not exception safe and its hard to make them exception-safe
647  // because QT aren't...
648  foreach (ResourceHandler* h, m_usableResources) {
649  h->addNotifee(this);
650  }
651 }
652 
653 void ConcurrentResourcesUser::lockResources(const QList<ResourceHandler*>& resources)
654 {
655  // We have to cycle until getting resources is successful
656  bool oneLockFailed;
657  do {
658  // Resetting the status of the flag for failed locks
659  oneLockFailed = false;
660 
661  // Now locking all our resources. The mutexes for resources are recursive, so they will not fail
662  // if this function is called recursively. Here I assume that locking a mutex never throws... If
663  // this is not the case, an exception during the lock of one mutex will leave some mutex locked
664  // indefinitely and some other unlocked.
665  QList<ResourceHandler*> lockedResources;
666  foreach (ResourceHandler* resource, resources) {
667  const bool locked = resource->getLock().tryLock();
668  if (!locked) {
669  oneLockFailed = true;
670  break;
671  } else {
672  lockedResources.append(resource);
673  }
674  }
675 
676  // If one lock failed, we can't acquire all resources here, so we unlock what we have
677  // locked and wait
678  if (oneLockFailed) {
679  foreach (ResourceHandler* resource, lockedResources) {
680  resource->getLock().unlock();
681  }
682 
683  // Waiting
685  }
686  } while (oneLockFailed);
687 }
688 
689 void ConcurrentResourcesUser::unlockResources(const QList<ResourceHandler*>& resources)
690 {
691  // Unlocking all resources. Here I assume that unlocking a mutex never throws... If
692  // this is not the case, an exception during the unlock of one mutex will leave some
693  // mutex locked indefinitely and some other unlocked.
694  foreach (ResourceHandler* resource, resources) {
695  resource->getLock().unlock();
696  }
697 
698  // Waking threads that are waiting
699  m_resources->getWaitCondition().wakeAll();
700 }
701 
703  m_resourcesUser(resourcesUser),
704  m_lockedByUs(false)
705 {
706  if (acquireLock) {
707  lock();
708  }
709 }
710 
712 {
713  unlock();
714 }
715 
717 {
718  if (m_lockedByUs) {
719  return;
720  }
721 
722  m_resourcesUser->lockAll();
723 
724  m_lockedByUs = true;
725 }
726 
728 {
729  if (!m_lockedByUs) {
730  return;
731  }
732 
733  m_resourcesUser->unlockAll();
734 
735  m_lockedByUs = false;
736 }
737 
738 } // end namspace farsa
739