configuration/src/resourcesuser.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2008 by Tomassino Ferrauto                              *
00003  *   t_ferrauto@yahoo.it                                                   *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020 
00021 #include "resourcesuser.h"
00022 #include <memory>
00023 
00024 namespace farsa {
00025 
00026 namespace {
00027     // This class is needed so that the status of m_beingNotified is exception safe  (i.e. it is reset
00028     // to false if an exception gets thrown inside the handler for resource change). This is used both
00029     // by SimpleResourcesUser and ConcurrentResourcesUser
00030     class BeingNotifiedRAII
00031     {
00032     public:
00033         BeingNotifiedRAII(bool* beingNotified) :
00034             m_beingNotified(beingNotified)
00035         {
00036             *m_beingNotified = true;
00037         }
00038 
00039         ~BeingNotifiedRAII()
00040         {
00041             *m_beingNotified = false;
00042         }
00043 
00044     private:
00045         bool* const m_beingNotified;
00046     };
00047 }
00048 
00049 void ResourcesUser::shareResourcesWith(ResourcesUser* other)
00050 {
00051     if (other != NULL) {
00052         m_resources = other->m_resources;
00053     } else {
00054         // This is exception safe (the assignment operator doesn't throw and if the constructor
00055         // throws m_resources doesn't change)
00056         m_resources = ResourceCollectionHolder();
00057     }
00058 }
00059 
00060 ResourcesUser::ResourcesUser() :
00061     m_resources()
00062 {
00063 }
00064 
00065 ResourcesUser::ResourcesUser(const ResourcesUser& other) throw() :
00066     m_resources(other.m_resources)
00067 {
00068 }
00069 
00070 ResourcesUser& ResourcesUser::operator=(const ResourcesUser& other) throw()
00071 {
00072     if (&other == this) {
00073         return *this;
00074     }
00075 
00076     m_resources = other.m_resources;
00077 
00078     return *this;
00079 }
00080 
00081 ResourcesUser::~ResourcesUser()
00082 {
00083     // Nothing to do here
00084 }
00085 
00086 SimpleResourcesUser::SimpleResourcesUser() :
00087     ResourcesUser(),
00088     m_beingNotified(false),
00089     m_notifiedResourceHandler(NULL),
00090     m_observedResources()
00091 {
00092 }
00093 
00094 SimpleResourcesUser::SimpleResourcesUser(const SimpleResourcesUser& other) :
00095     ResourcesUser(other),
00096     m_beingNotified(false), // When we are created we can't be in the resource change handler
00097     m_notifiedResourceHandler(NULL),
00098     m_observedResources(other.m_observedResources)
00099 {
00100     // This is not exception safe. It is difficult to make this exception safe because QT aren't (and here we
00101     // have to deal with QT containers)
00102     
00103     // We have to be notified of changes in the same set of resources as other.
00104     forceBeingNotified();
00105 }
00106 
00107 SimpleResourcesUser& SimpleResourcesUser::operator=(const SimpleResourcesUser& other)
00108 {
00109     // This is not exception safe. It is difficult to make this exception safe because QT aren't (and here we
00110     // have to deal with QT containers)
00111 
00112     if (&other == this) {
00113         return *this;
00114     }
00115 
00116     // First of all we have to stop being notified for old resources
00117     removeAllNotifications();
00118 
00119     // Calling parent function
00120     ResourcesUser::operator=(other);
00121 
00122     // Now we can copy the set of resources for which notification is reuested. m_beingNotified and
00123     // m_notifiedResourceHandler are not changed as we could be inside the notification handler
00124     // (that would be weird, though)
00125     m_observedResources = other.m_observedResources;
00126 
00127     // We have to be notified of changes in the same same set of resources as other
00128     forceBeingNotified();
00129 
00130     return *this;
00131 }
00132 
00133 SimpleResourcesUser::~SimpleResourcesUser()
00134 {
00135     removeAllNotifications();
00136 }
00137 
00138 void SimpleResourcesUser::shareResourcesWith(ResourcesUser* other)
00139 {
00140     // Now we have to stop being notified for old resources
00141     removeAllNotifications();
00142 
00143     // Calling parent function
00144     ResourcesUser::shareResourcesWith(other);
00145 }
00146 
00147 void SimpleResourcesUser::deleteResource(QString name)
00148 {
00149     // Trying to retrieve the resource
00150     ResourceHandler* h = m_resources->getResource(name, false);
00151 
00152     if ((h == NULL) || (!h->exists())){
00153         throw ResourceNotDeclaredException(name.toAscii().data());
00154     }
00155 
00156     // Removing the resource
00157     h->unset();
00158 }
00159 
00160 void SimpleResourcesUser::resourceChanged(QString, ResourceChangeType)
00161 {
00162     // The default implementation does nothing
00163 }
00164 
00165 void SimpleResourcesUser::notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType)
00166 {
00167     BeingNotifiedRAII beingNotifiedRAII(&m_beingNotified);
00168     m_notifiedResourceHandler = resource;
00169 
00170     resourceChanged(m_notifiedResourceHandler->name(), changeType);
00171 }
00172 
00173 void SimpleResourcesUser::removeAllNotifications()
00174 {
00175     // Functions using this are not exception safe and its hard to make them exception-safe
00176     // because QT aren't...
00177     foreach (ResourceHandler* h, m_observedResources) {
00178         h->removeNotifee(this);
00179     }
00180 
00181     m_observedResources.clear();
00182 }
00183 
00184 void SimpleResourcesUser::forceBeingNotified()
00185 {
00186     // Functions using this are not exception safe and its hard to make them exception-safe
00187     // because QT aren't...
00188     foreach (ResourceHandler* h, m_observedResources) {
00189         h->addNotifee(this);
00190     }
00191 }
00192 
00193 ConcurrentResourcesUser::ConcurrentResourcesUser() :
00194     ResourcesUser(),
00195     m_lockAcquired(0),
00196     m_beingNotified(false),
00197     m_notifiedResourceHandler(NULL),
00198     m_usableResources()
00199 {
00200     // Here we need the ResourceCollectionHolder object to use locks
00201     m_resources.setUseLock(true);
00202 }
00203 
00204 ConcurrentResourcesUser::~ConcurrentResourcesUser()
00205 {
00206     // Removing from the notification list of all resources we were observing
00207     removeAllNotifications();
00208 
00209     m_usableResources.clear();
00210 }
00211 
00212 void ConcurrentResourcesUser::shareResourcesWith(ResourcesUser* other)
00213 {
00214     if ((m_lockAcquired > 0) || m_beingNotified) {
00215         throw WrongResourceLockStatusForOperation("shareResourcesWith", true);
00216     }
00217 
00218     // If other is NULL we do not get any lock on other resources, otherwise we have to. The two
00219     // parts of the if are similar but trying to merge them creates quite some problems (with locks and
00220     // exception safety), so I leave them separated
00221     if (other == NULL) {
00222         // Acquiring the lock on old resources to remove notifications, then we can release it. I'm doing
00223         // it like this because I'm not sure deleting the mutex before destroying the QMutexLocker is
00224         // ok (even if the mutex is unlocked). The mutex could be destroyed together with the corresponding
00225         // ResourceCollection in ResourcesUser::shareResourcesWith(). Here we also get the list of name of
00226         // existing resource that we use
00227         QSet<QString> m_existingResourcesInOldCollection;
00228         {
00229             QMutexLocker oldCollectionLocker(&(m_resources->getLock()));
00230 
00231             // Removing all notifications on old resources
00232             removeAllNotifications();
00233 
00234             for (QMap<QString, ResourceHandler*>::const_iterator it = m_usableResources.constBegin(); it != m_usableResources.constEnd(); ++it) {
00235                 if (it.value()->exists()) {
00236                     m_existingResourcesInOldCollection.insert(it.key());
00237                 }
00238             }
00239         }
00240 
00241         // Calling parent function.
00242         ResourcesUser::shareResourcesWith(other);
00243 
00244         // Taking the lock on the newly created resource collection
00245         QMutexLocker newCollectionLocker(&(m_resources->getLock()));
00246 
00247         // Now we have to update the m_usableResources map to point the the new resource handlers. No resource
00248         // still esists, we create empty resourced here
00249         for (QMap<QString, ResourceHandler*>::iterator it = m_usableResources.begin(); it != m_usableResources.end(); ++it) {
00250             it.value() = m_resources->getResource(it.key(), true);
00251             it.value()->addNotifee(this);
00252         }
00253 
00254         // Before sending notifications, we have to get the lock on all resources and to release the lock on the
00255         // resource collection
00256         ResourcesMutexesLocker resourcesLocker(this, m_usableResources.values());
00257 
00258         // Now we can unlock the mutex on the Resource Manager
00259         newCollectionLocker.unlock();
00260 
00261         // Now we can send all notifications. All previously existing resources have been deleted
00262         foreach (QString name, m_existingResourcesInOldCollection) {
00263             notifyResourceChange(m_usableResources[name], Deleted);
00264         }
00265     } else {
00266         // Acquiring lock on new resources
00267         QMutexLocker newCollectionLocker(&(other->m_resources->getLock()));
00268 
00269         // Acquiring the lock on old resources to remove notifications, then we can release it. I'm doing
00270         // it like this because I'm not sure deleting the mutex before destroying the QMutexLocker is
00271         // ok (even if the mutex is unlocked). The mutex could be destroyed together with the corresponding
00272         // ResourceCollection in ResourcesUser::shareResourcesWith(). Here we also get the list of name of
00273         // existing resource that we use
00274         QSet<QString> m_existingResourcesInOldCollection;
00275         {
00276             QMutexLocker oldCollectionLocker(&(m_resources->getLock()));
00277 
00278             // Removing all notifications on old resources
00279             removeAllNotifications();
00280 
00281             for (QMap<QString, ResourceHandler*>::const_iterator it = m_usableResources.constBegin(); it != m_usableResources.constEnd(); ++it) {
00282                 if (it.value()->exists()) {
00283                     m_existingResourcesInOldCollection.insert(it.key());
00284                 }
00285             }
00286         }
00287 
00288         // Calling parent function to actually share resources. Here the lock on new resources would be taken
00289         // again (but the mutex is not recursive), so we have to temporarly prevent m_resources from locking
00290         m_resources.setUseLock(false);
00291         ResourcesUser::shareResourcesWith(other);
00292         m_resources.setUseLock(true);
00293 
00294         // Now we have to update the m_usableResources map to point the the new resource handlers. We also create
00295         // the set of names of resources that exists in the new collection
00296         QSet<QString> m_existingResourcesInNewCollection;
00297         for (QMap<QString, ResourceHandler*>::iterator it = m_usableResources.begin(); it != m_usableResources.end(); ++it) {
00298             it.value() = m_resources->getResource(it.key(), true);
00299             it.value()->addNotifee(this);
00300 
00301             if (it.value()->exists()) {
00302                 m_existingResourcesInNewCollection.insert(it.key());
00303             }
00304         }
00305 
00306         // Before sending notifications, we have to get the lock on all resources and to release the lock on the
00307         // resource collection
00308         ResourcesMutexesLocker resourcesLocker(this, m_usableResources.values());
00309 
00310         // Now we can unlock the mutex on the Resource Manager
00311         newCollectionLocker.unlock();
00312 
00313         // Now we can send all notifications. The changeType to use depends on the status of the resource with a given
00314         // name in the new and old set: if a resource existed in the old set and not in the new one, changeType will be
00315         // Deleted, if it existed in the old set and is also present in the new one, changeType will be Modified, if it
00316         // didn't exist in the old set but is present in the new one, changeType will be Created.
00317         const QSet<QString> deletedResources = m_existingResourcesInOldCollection - m_existingResourcesInNewCollection;
00318         const QSet<QString> modifiedResources = m_existingResourcesInOldCollection & m_existingResourcesInNewCollection;
00319         const QSet<QString> createdResources = m_existingResourcesInNewCollection - m_existingResourcesInOldCollection;
00320 
00321         // Actually sending all notifications
00322         foreach (QString name, deletedResources) {
00323             notifyResourceChange(m_usableResources[name], Deleted);
00324         }
00325         foreach (QString name, modifiedResources) {
00326             notifyResourceChange(m_usableResources[name], Modified);
00327         }
00328         foreach (QString name, createdResources) {
00329             notifyResourceChange(m_usableResources[name], Created);
00330         }
00331     }
00332 }
00333 
00334 void ConcurrentResourcesUser::usableResources(QStringList resources)
00335 {
00336     if ((m_lockAcquired > 0) || m_beingNotified) {
00337         throw WrongResourceLockStatusForOperation("usableResources", true);
00338     }
00339 
00340     // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
00341     QMutexLocker collectionLocker(&(m_resources->getLock()));
00342 
00343     // Removing old resources
00344     m_usableResources.clear();
00345 
00346     foreach (QString resource, resources) {
00347         // Here we create a Resource Handler if the resource doesn't exists
00348         ResourceHandler* h = m_resources->getResource(resource, true);
00349 
00350         h->addNotifee(this);
00351         m_usableResources[resource] = h;
00352     }
00353 
00354     // Getting the lock on all new resources to send notifications. There is a little overhead, here,
00355     // because we get the lock also for non-existing resources (that are not notified, though)
00356     ResourcesMutexesLocker resourcesLocker(this, m_usableResources.values());
00357 
00358     // Now we can unlock the mutex on the Resource Manager
00359     collectionLocker.unlock();
00360 
00361     // Notifying that all the new resources exist
00362     foreach (ResourceHandler* h, m_usableResources) {
00363         if (h->exists()) {
00364             notifyResourceChange(h, Created);
00365         }
00366     }
00367 }
00368 
00369 void ConcurrentResourcesUser::addUsableResource(QString resource)
00370 {
00371     if ((m_lockAcquired > 0) || m_beingNotified) {
00372         throw WrongResourceLockStatusForOperation("addUsableResource", true);
00373     }
00374 
00375     // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
00376     QMutexLocker collectionLocker(&(m_resources->getLock()));
00377 
00378     // Here we create a Resource Handler if the resource doesn't exists
00379     ResourceHandler* h = m_resources->getResource(resource, true);
00380 
00381     h->addNotifee(this);
00382     m_usableResources[resource] = h;
00383 
00384     // If the resource exists, sending notification
00385     if (h->exists()) {
00386         // Taking the lock on the resource because we have to send a notification
00387         ResourcesMutexesLocker resourceLocker(this, h);
00388 
00389         // Now we can unlock the mutex on the Resource Manager
00390         collectionLocker.unlock();
00391 
00392         // Notifying that the resource exists
00393         notifyResourceChange(h, Created);
00394     }
00395 }
00396 
00397 void ConcurrentResourcesUser::addUsableResources(QStringList resources)
00398 {
00399     if ((m_lockAcquired > 0) || m_beingNotified) {
00400         throw WrongResourceLockStatusForOperation("addUsableResources", true);
00401     }
00402 
00403     // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
00404     QMutexLocker collectionLocker(&(m_resources->getLock()));
00405 
00406     // Adding all new resources
00407     QList<ResourceHandler *> newResources;
00408     foreach (QString resource, resources) {
00409         // Here we create a Resource Handler if the resource doesn't exists
00410         ResourceHandler* h = m_resources->getResource(resource, true);
00411 
00412         h->addNotifee(this);
00413         m_usableResources[resource] = h;
00414 
00415         // Saving the list of the new resources handlers for notifications
00416         newResources.append(h);
00417     }
00418 
00419     // Getting the lock on all new resources to send notifications. There is a little overhead, here,
00420     // because we get the lock also for non-existing resources (that are not notified, though)
00421     ResourcesMutexesLocker resourcesLocker(this, newResources);
00422 
00423     // Now we can unlock the mutex on the Resource Manager
00424     collectionLocker.unlock();
00425 
00426     // Notifying that all the new resources exist
00427     foreach (ResourceHandler* h, newResources) {
00428         if (h->exists()) {
00429             notifyResourceChange(h, Created);
00430         }
00431     }
00432 }
00433 
00434 void ConcurrentResourcesUser::removeUsableResource(QString resource)
00435 {
00436     if ((m_lockAcquired > 0) || m_beingNotified) {
00437         throw WrongResourceLockStatusForOperation("removeUsableResource", true);
00438     }
00439 
00440     if (m_usableResources.contains(resource)) {
00441         // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
00442         QMutexLocker collectionLocker(&(m_resources->getLock()));
00443 
00444         // The ResourceHandler should exist, if not there is a bug and this function will crash
00445         ResourceHandler* h = m_resources->getResource(resource, false);
00446         h->removeNotifee(this);
00447 
00448         m_usableResources.remove(resource);
00449 
00450         // Now notifying the resource is no longer available. First taking the lock on the resource
00451         ResourcesMutexesLocker resourceLocker(this, h);
00452 
00453         // Now we can unlock the mutex on the Resource Manager
00454         collectionLocker.unlock();
00455 
00456         // Notifying that the resource exists
00457         notifyResourceChange(h, Deleted);
00458     }
00459 }
00460 
00461 void ConcurrentResourcesUser::removeUsableResources(QStringList resources)
00462 {
00463     if ((m_lockAcquired > 0) || m_beingNotified) {
00464         throw WrongResourceLockStatusForOperation("removeUsableResources", true);
00465     }
00466 
00467     // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
00468     QMutexLocker collectionLocker(&(m_resources->getLock()));
00469 
00470     // Deleting all resources
00471     QList<ResourceHandler *> deletedResources;
00472     foreach (QString resource, resources) {
00473         if (!m_usableResources.contains(resource)) {
00474             continue;
00475         }
00476 
00477         // Taking the resource handler and then removing the resource (the resource must exist, otherwise there is a
00478         // bug and this will crash)
00479         ResourceHandler* h = m_resources->getResource(resource, false);
00480         h->removeNotifee(this);
00481 
00482         m_usableResources.remove(resource);
00483 
00484         // Saving the list of the deleted resources handlers for notifications
00485         deletedResources.append(h);
00486     }
00487 
00488     // Getting the lock on all deleted resources to send notifications. There is a little overhead, here,
00489     // because we get the lock also for non-existing resources (that are not notified, though)
00490     ResourcesMutexesLocker resourcesLocker(this, deletedResources);
00491 
00492     // Now we can unlock the mutex on the Resource Manager
00493     collectionLocker.unlock();
00494 
00495     // Notifying that all the resources that exist
00496     foreach (ResourceHandler* h, deletedResources) {
00497         if (h->exists()) {
00498             notifyResourceChange(h, Deleted);
00499         }
00500     }
00501 }
00502 
00503 void ConcurrentResourcesUser::removeAllUsableResources()
00504 {
00505     if ((m_lockAcquired > 0) || m_beingNotified) {
00506         throw WrongResourceLockStatusForOperation("removeAllUsableResources", true);
00507     }
00508 
00509     // First of all we have to acquire the lock on the Resource Manager (because we need to call getResource)
00510     QMutexLocker collectionLocker(&(m_resources->getLock()));
00511 
00512     // Deleting all resources
00513     foreach (ResourceHandler* h, m_usableResources) {
00514         h->removeNotifee(this);
00515     }
00516 
00517     QList<ResourceHandler *> deletedResources = m_usableResources.values();
00518     // Clearing the list of resources we use
00519     m_usableResources.clear();
00520 
00521     // Getting the lock on all deleted resources to send notifications. There is a little overhead, here,
00522     // because we get the lock also for non-existing resources (that are not notified, though)
00523     ResourcesMutexesLocker resourcesLocker(this, deletedResources);
00524 
00525     // Now we can unlock the mutex on the Resource Manager
00526     collectionLocker.unlock();
00527 
00528     // Notifying that all the resources that exist
00529     foreach (ResourceHandler* h, deletedResources) {
00530         if (h->exists()) {
00531             notifyResourceChange(h, Deleted);
00532         }
00533     }
00534 }
00535 
00536 void ConcurrentResourcesUser::deleteResource(QString name)
00537 {
00538     if ((m_lockAcquired > 0) || m_beingNotified) {
00539         throw WrongResourceLockStatusForOperation("deleteResource", true);
00540     }
00541 
00542     // Getting the lock on the resource collection
00543     QMutexLocker collectionLocker(&(m_resources->getLock()));
00544 
00545     // Now we have to get the resource and then acquire the lock on the resource if it exists
00546     ResourceHandler* h = m_resources->getResource(name, false);
00547     if ((h == NULL) || (!h->exists())) {
00548         throw ResourceNotDeclaredException(name.toAscii().data());
00549     }
00550     ResourcesMutexesLocker resourceLocker(this, h);
00551 
00552     // Before setting the resource, we can release the lock on Resource Manager
00553     collectionLocker.unlock();
00554 
00555     // Now removing the resource
00556     h->unset();
00557 }
00558 
00559 bool ConcurrentResourcesUser::hasResource(QString name) const
00560 {
00561     if ((m_lockAcquired > 0) || m_beingNotified) {
00562         throw WrongResourceLockStatusForOperation("hasResource", true);
00563     }
00564 
00565     QMutexLocker locker(&(m_resources->getLock()));
00566 
00567     return m_resources->hasResource(name);
00568 }
00569 
00570 bool ConcurrentResourcesUser::usedResourcesExist(QStringList* nonExistingResources) const
00571 {
00572     if ((m_lockAcquired > 0) || m_beingNotified) {
00573         throw WrongResourceLockStatusForOperation("usedResourcesExist", true);
00574     }
00575 
00576     QMutexLocker locker(&(m_resources->getLock()));
00577 
00578     bool allExist = true;
00579 
00580     foreach (ResourceHandler* h, m_usableResources) {
00581         if (!h->exists()) {
00582             allExist = false;
00583             if (nonExistingResources != NULL) {
00584                 nonExistingResources->append(h->name());
00585             } else {
00586                 // If we don't have to return the list of non-existing resource, we can exit
00587                 // from the cicle as soon as we find a resource that doesn't exists
00588                 break;
00589             }
00590         }
00591     }
00592 
00593     return allExist;
00594 }
00595 
00596 void ConcurrentResourcesUser::resourceChanged(QString, ResourceChangeType)
00597 {
00598     // The default implementation of this function does nothing
00599 }
00600 
00601 void ConcurrentResourcesUser::lockAll()
00602 {
00603     // Locking all resources. To avoid deadlocks here we first acquire the lock on the Resource Manager
00604     QMutexLocker locker(&(m_resources->getLock()));
00605 
00606     // Locking all resources
00607     lockResources(m_usableResources.values());
00608 
00609     ++m_lockAcquired;
00610 }
00611 
00612 void ConcurrentResourcesUser::unlockAll()
00613 {
00614     // Unlocking all resources. Here we have to take the mutex because the unlock of resources must be
00615     // atomic (otherwise a lockAll() call can find a resource locked even if we are here because we haven't
00616     // unlocked a certain resource yet and he could then miss the wake of the wait condition)
00617     QMutexLocker locker(&(m_resources->getLock()));
00618 
00619     // Unlocking all resources and signalling threads on the wait condition
00620     unlockResources(m_usableResources.values());
00621 
00622     --m_lockAcquired;
00623 
00624     // The mutex will be unlocked here
00625 }
00626 
00627 void ConcurrentResourcesUser::notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType)
00628 {
00629     BeingNotifiedRAII beingNotifiedRAII(&m_beingNotified);
00630     m_notifiedResourceHandler = resource;
00631 
00632     resourceChanged(m_notifiedResourceHandler->name(), changeType);
00633 }
00634 
00635 void ConcurrentResourcesUser::removeAllNotifications()
00636 {
00637     // Functions using this are not exception safe and its hard to make them exception-safe
00638     // because QT aren't...
00639     foreach (ResourceHandler* h, m_usableResources) {
00640         h->removeNotifee(this);
00641     }
00642 }
00643 
00644 void ConcurrentResourcesUser::forceBeingNotified()
00645 {
00646     // Functions using this are not exception safe and its hard to make them exception-safe
00647     // because QT aren't...
00648     foreach (ResourceHandler* h, m_usableResources) {
00649         h->addNotifee(this);
00650     }
00651 }
00652 
00653 void ConcurrentResourcesUser::lockResources(const QList<ResourceHandler*>& resources)
00654 {
00655     // We have to cycle until getting resources is successful
00656     bool oneLockFailed;
00657     do {
00658         // Resetting the status of the flag for failed locks
00659         oneLockFailed = false;
00660 
00661         // Now locking all our resources. The mutexes for resources are recursive, so they will not fail
00662         // if this function is called recursively. Here I assume that locking a mutex never throws... If
00663         // this is not the case, an exception during the lock of one mutex will leave some mutex locked
00664         // indefinitely and some other unlocked.
00665         QList<ResourceHandler*> lockedResources;
00666         foreach (ResourceHandler* resource, resources) {
00667             const bool locked = resource->getLock().tryLock();
00668             if (!locked) {
00669                 oneLockFailed = true;
00670                 break;
00671             } else {
00672                 lockedResources.append(resource);
00673             }
00674         }
00675 
00676         // If one lock failed, we can't acquire all resources here, so we unlock what we have
00677         // locked and wait
00678         if (oneLockFailed) {
00679             foreach (ResourceHandler* resource, lockedResources) {
00680                 resource->getLock().unlock();
00681             }
00682 
00683             // Waiting
00684             m_resources->getWaitCondition().wait(&(m_resources->getLock()));
00685         }
00686     } while (oneLockFailed);
00687 }
00688 
00689 void ConcurrentResourcesUser::unlockResources(const QList<ResourceHandler*>& resources)
00690 {
00691     // Unlocking all resources. Here I assume that unlocking a mutex never throws... If
00692     // this is not the case, an exception during the unlock of one mutex will leave some
00693     // mutex locked indefinitely and some other unlocked.
00694     foreach (ResourceHandler* resource, resources) {
00695         resource->getLock().unlock();
00696     }
00697 
00698     // Waking threads that are waiting
00699     m_resources->getWaitCondition().wakeAll();
00700 }
00701 
00702 ResourcesLocker::ResourcesLocker(ConcurrentResourcesUser *resourcesUser, bool acquireLock):
00703     m_resourcesUser(resourcesUser),
00704     m_lockedByUs(false)
00705 {
00706     if (acquireLock) {
00707         lock();
00708     }
00709 }
00710 
00711 ResourcesLocker::~ResourcesLocker()
00712 {
00713     unlock();
00714 }
00715 
00716 void ResourcesLocker::lock()
00717 {
00718     if (m_lockedByUs) {
00719         return;
00720     }
00721 
00722     m_resourcesUser->lockAll();
00723 
00724     m_lockedByUs = true;
00725 }
00726 
00727 void ResourcesLocker::unlock()
00728 {
00729     if (!m_lockedByUs) {
00730         return;
00731     }
00732 
00733     m_resourcesUser->unlockAll();
00734 
00735     m_lockedByUs = false;
00736 }
00737 
00738 } // end namspace farsa
00739