Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:35

0001 
0002 // Copyright 2020, Jefferson Science Associates, LLC.
0003 // Subject to the terms in the LICENSE file found in the top-level directory.
0004 
0005 #pragma once
0006 #include <JANA/Services/JServiceLocator.h>
0007 
0008 class JEventProcessor;
0009 
0010 class JLockService : public JService {
0011 
0012 public:
0013 
0014     JLockService() {
0015         pthread_rwlock_init(&m_rw_locks_lock, nullptr);
0016         pthread_rwlock_init(&m_root_fill_locks_lock, nullptr);
0017         m_app_rw_lock = CreateLock("app");
0018         m_root_rw_lock = CreateLock("root");
0019     }
0020 
0021     ~JLockService() override {
0022         for (const auto& pair : m_rw_locks) {
0023             delete pair.second;
0024         }
0025         for (const auto& pair : m_root_fill_rw_lock) {
0026             delete pair.second;
0027         }
0028     }
0029 
0030     inline pthread_rwlock_t *CreateLock(const std::string &name, bool throw_exception_if_exists = true);
0031 
0032     inline pthread_rwlock_t *ReadLock(const std::string &name);
0033 
0034     inline pthread_rwlock_t *WriteLock(const std::string &name);
0035 
0036     inline pthread_rwlock_t *Unlock(const std::string &name = std::string("app"));
0037 
0038     inline pthread_rwlock_t *RootReadLock() {
0039         pthread_rwlock_rdlock(m_root_rw_lock);
0040         return m_root_rw_lock;
0041     }
0042 
0043     inline pthread_rwlock_t *RootWriteLock() {
0044         pthread_rwlock_wrlock(m_root_rw_lock);
0045         return m_root_rw_lock;
0046     }
0047 
0048     inline pthread_rwlock_t *RootUnLock() {
0049         pthread_rwlock_unlock(m_root_rw_lock);
0050         return m_root_rw_lock;
0051     }
0052 
0053     inline pthread_rwlock_t *RootFillLock(JEventProcessor *proc);
0054 
0055     inline pthread_rwlock_t *RootFillUnLock(JEventProcessor *proc);
0056 
0057     pthread_rwlock_t* GetReadWriteLock(std::string &name) {
0058         return m_rw_locks.count( name ) == 0 ? nullptr : m_rw_locks[name];
0059     }
0060     pthread_rwlock_t* GetRootReadWriteLock() {
0061         return m_root_rw_lock;
0062     }
0063     pthread_rwlock_t* GetRootFillLock( JEventProcessor *proc ) {
0064         return m_root_fill_rw_lock.count( proc ) == 0 ? nullptr : m_root_fill_rw_lock[proc];
0065     }
0066 
0067 
0068 private:
0069 
0070     std::map<std::string, pthread_rwlock_t *> m_rw_locks;
0071     pthread_rwlock_t *m_app_rw_lock = nullptr;
0072     pthread_rwlock_t *m_root_rw_lock = nullptr;
0073     pthread_rwlock_t m_rw_locks_lock {}; // control access to rw_locks
0074     pthread_rwlock_t m_root_fill_locks_lock {}; // control access to m_root_fill_rw_lock
0075     std::map<JEventProcessor *, pthread_rwlock_t *> m_root_fill_rw_lock;
0076 
0077 };
0078 
0079 inline pthread_rwlock_t *JLockService::CreateLock(const std::string &name, bool throw_exception_if_exists) {
0080     // Lock the rw locks lock
0081     pthread_rwlock_wrlock(&m_rw_locks_lock);
0082 
0083     // Make sure a lock with this name does not already exist
0084     std::map<std::string, pthread_rwlock_t *>::iterator iter = m_rw_locks.find(name);
0085     pthread_rwlock_t *lock = (iter != m_rw_locks.end() ? iter->second : NULL);
0086 
0087     if (lock != NULL) {
0088         // Lock exists. Throw exception (if specified)
0089         if (throw_exception_if_exists) {
0090             pthread_rwlock_unlock(&m_rw_locks_lock);
0091             std::string mess = "Trying to create JANA rw lock \"" + name + "\" when it already exists!";
0092             throw JException(mess);
0093         }
0094     } else {
0095         // Lock does not exist. Create it.
0096         lock = new pthread_rwlock_t;
0097         pthread_rwlock_init(lock, NULL);
0098         m_rw_locks[name] = lock;
0099     }
0100 
0101     // Unlock the rw locks lock
0102     pthread_rwlock_unlock(&m_rw_locks_lock);
0103 
0104     return lock;
0105 }
0106 
0107 //---------------------------------
0108 // ReadLock
0109 //---------------------------------
0110 inline pthread_rwlock_t *JLockService::ReadLock(const std::string &name) {
0111     /// Lock a global, named, rw_lock for reading. If a lock with that
0112     /// name does not exist, then create one and lock it for reading.
0113     ///
0114     /// This is a little tricky. Access to the map of rw locks must itself
0115     /// be controlled by a rw lock. This means we incure the overhead of two
0116     /// locks and one unlock for every call to this method. Furthermore, to
0117     /// keep this efficient, we want to try only read locking the map at
0118     /// first. If we fail to find the requested lock in the map, we must
0119     /// release the map's read lock and try creating the new lock.
0120 
0121     // Ensure the rw_locks map is not changed while we're looking at it,
0122     // lock the rw_locks_lock.
0123     pthread_rwlock_rdlock(&m_rw_locks_lock);
0124 
0125     // Find the lock. If it doesn't exist, set pointer to NULL
0126     std::map<std::string, pthread_rwlock_t *>::iterator iter = m_rw_locks.find(name);
0127     pthread_rwlock_t *lock = (iter != m_rw_locks.end() ? iter->second : NULL);
0128 
0129     // Unlock the locks lock
0130     pthread_rwlock_unlock(&m_rw_locks_lock);
0131 
0132     // If the lock doesn't exist, we need to create it. Because multiple
0133     // threads may be trying to do this at the same time, one may create
0134     // it while another waits for the locks lock. We flag the CreateLock
0135     // method to not throw an exception to accommodate this.
0136     if (lock == NULL) lock = CreateLock(name, false);
0137 
0138     // Finally, lock the named lock or print error message if not found
0139     if (lock != NULL) {
0140         pthread_rwlock_rdlock(lock);
0141     } else {
0142         std::string mess = "Unable to find or create lock \"" + name + "\" for reading!";
0143         throw JException(mess);
0144     }
0145 
0146     return lock;
0147 }
0148 
0149 //---------------------------------
0150 // WriteLock
0151 //---------------------------------
0152 inline pthread_rwlock_t *JLockService::WriteLock(const std::string &name) {
0153     /// Lock a global, named, rw_lock for writing. If a lock with that
0154     /// name does not exist, then create one and lock it for writing.
0155     ///
0156     /// This is a little tricky. Access to the map of rw locks must itself
0157     /// be controlled by a rw lock. This means we incure the overhead of two
0158     /// locks and one unlock for every call to this method. Furthermore, to
0159     /// keep this efficient, we want to try only read locking the map at
0160     /// first. If we fail to find the requested lock in the map, we must
0161     /// release the map's read lock and try creating the new lock.
0162 
0163     // Ensure the rw_locks map is not changed while we're looking at it,
0164     // lock the rw_locks_lock.
0165     pthread_rwlock_rdlock(&m_rw_locks_lock);
0166 
0167     // Find the lock. If it doesn't exist, set pointer to NULL
0168     std::map<std::string, pthread_rwlock_t *>::iterator iter = m_rw_locks.find(name);
0169     pthread_rwlock_t *lock = (iter != m_rw_locks.end() ? iter->second : NULL);
0170 
0171     // Unlock the locks lock
0172     pthread_rwlock_unlock(&m_rw_locks_lock);
0173 
0174     // If the lock doesn't exist, we need to create it. Because multiple
0175     // threads may be trying to do this at the same time, one may create
0176     // it while another waits for the locks lock. We flag the CreateLock
0177     // method to not throw an exception to accommodate this.
0178     if (lock == NULL) lock = CreateLock(name, false);
0179 
0180     // Finally, lock the named lock or print error message if not found
0181     if (lock != NULL) {
0182         pthread_rwlock_wrlock(lock);
0183     } else {
0184         std::string mess = "Unable to find or create lock \"" + name + "\" for writing!";
0185         throw JException(mess);
0186     }
0187 
0188     return lock;
0189 }
0190 
0191 //---------------------------------
0192 // Unlock
0193 //---------------------------------
0194 inline pthread_rwlock_t *JLockService::Unlock(const std::string &name) {
0195     /// Unlock a global, named rw_lock
0196 
0197     // To ensure the rw_locks map is not changed while we're looking at it,
0198     // lock the rw_locks_lock.
0199     pthread_rwlock_rdlock(&m_rw_locks_lock);
0200 
0201     // Find the lock. If it doesn't exist, set pointer to NULL
0202     std::map<std::string, pthread_rwlock_t *>::iterator iter = m_rw_locks.find(name);
0203     pthread_rwlock_t *lock = (iter != m_rw_locks.end() ? iter->second : NULL);
0204 
0205     // Unlock the locks lock
0206     pthread_rwlock_unlock(&m_rw_locks_lock);
0207 
0208     // Finally, unlock the named lock or print error message if not found
0209     if (lock != NULL) {
0210         pthread_rwlock_unlock(lock);
0211     } else {
0212         std::string mess = "Unable to find lock \"" + name + "\" for unlocking!";
0213         throw JException(mess);
0214     }
0215 
0216     return lock;
0217 }
0218 
0219 //---------------------------------
0220 // RootFillLock
0221 //---------------------------------
0222 inline pthread_rwlock_t *JLockService::RootFillLock(JEventProcessor *proc) {
0223     /// Use this to lock a rwlock that is used exclusively by the given
0224     /// JEventProcessor. This addresses the common case where many plugins
0225     /// are in use and all contending for the same root lock. You should
0226     /// only use this when filling a histogram and not for creating. Use
0227     /// RootWriteLock and RootUnLock for that.
0228     pthread_rwlock_t *lock;
0229 
0230     pthread_rwlock_rdlock(&m_root_fill_locks_lock);
0231     auto iter = m_root_fill_rw_lock.find(proc);
0232     if (iter == m_root_fill_rw_lock.end()) {
0233         pthread_rwlock_unlock(&m_root_fill_locks_lock);
0234         pthread_rwlock_wrlock(&m_root_fill_locks_lock);
0235         auto iter_now = m_root_fill_rw_lock.find(proc);
0236         if(iter_now == m_root_fill_rw_lock.end()){
0237             lock = new pthread_rwlock_t;
0238             pthread_rwlock_init(lock, nullptr);
0239             m_root_fill_rw_lock[proc] = lock;
0240         }
0241         else{
0242             lock = iter_now->second;
0243         }
0244     }
0245     else {
0246         lock = iter->second;
0247     }
0248     pthread_rwlock_unlock(&m_root_fill_locks_lock);
0249     pthread_rwlock_wrlock(lock);
0250     return lock;
0251 }
0252 
0253 //---------------------------------
0254 // RootFillUnLock
0255 //---------------------------------
0256 inline pthread_rwlock_t *JLockService::RootFillUnLock(JEventProcessor *proc) {
0257     /// Use this to unlock a rwlock that is used exclusively by the given
0258     /// JEventProcessor. This addresses the common case where many plugins
0259     /// are in use and all contending for the same root lock. You should
0260     /// only use this when filling a histogram and not for creating. Use
0261     /// RootWriteLock and RootUnLock for that.
0262     pthread_rwlock_rdlock(&m_root_fill_locks_lock);
0263     std::map<JEventProcessor *, pthread_rwlock_t *>::iterator iter = m_root_fill_rw_lock.find(proc);
0264     if (iter == m_root_fill_rw_lock.end()) {
0265         pthread_rwlock_unlock(&m_root_fill_locks_lock);
0266         throw JException(
0267                 "Tried calling JLockService::RootFillUnLock with something other than a registered JEventProcessor!");
0268     }
0269     pthread_rwlock_t *lock = iter->second;
0270     pthread_rwlock_unlock(&m_root_fill_locks_lock);
0271     pthread_rwlock_unlock(lock);
0272     return lock;
0273 }
0274 
0275