diff --git a/configure.seed b/configure.seed index bf82d88302..4f7e71afa8 100644 --- a/configure.seed +++ b/configure.seed @@ -535,6 +535,22 @@ then fi fi +AC_CACHE_CHECK([if pthread rwlocks are supported], [my_cv_rw_locks_supported], [ + AC_TRY_RUN([ + #include + + int main() { + pthread_rwlock_t t; + return 0; + } +] +, [my_cv_rw_locks_supported=yes], [my_cv_rw_locks_supported=no]) +] +) + +if test "$my_cv_rw_locks_supported" = yes; then + AC_DEFINE_UNQUOTED(HAVE_RW_LOCK, 1, [pthread rwlocks supported]) +fi if test -f /usr/bin/lsb_release; then LINUX_OS_RELEASE=`lsb_release -r | cut -f 2|cut -d '.' -f 1` diff --git a/include/GenericHash.h b/include/GenericHash.h index a528c992a1..f3e19b669b 100644 --- a/include/GenericHash.h +++ b/include/GenericHash.h @@ -42,7 +42,7 @@ class GenericHash { u_int32_t num_hashes; /**< Number of hash.*/ u_int32_t current_size; /**< Current size of hash (including idle or ready-to-purge elements).*/ u_int32_t max_hash_size; /**< Max size of hash.*/ - Mutex **locks; + RwLock **locks; NetworkInterface *iface; /**< Pointer of network interface for this generic hash.*/ u_int last_purged_hash; /**< Index of last purged hash.*/ u_int purge_step; diff --git a/include/RwLock.h b/include/RwLock.h index 24cc1d8be9..223d5bd3d4 100644 --- a/include/RwLock.h +++ b/include/RwLock.h @@ -28,18 +28,22 @@ class RwLock { private: +#ifndef HAVE_RW_LOCK + Mutex m; +#else pthread_rwlock_t the_rwlock; -#ifdef RWLOCK_DEBUG - char last_lock_file[64], last_unlock_file[64]; - int last_lock_line, last_unlock_line; - u_int num_locks, num_unlocks; #endif - void initialize(); + void lock(const char *filename, int line, bool readonly); + bool trylock(const char *filename, int line, bool readonly); public: RwLock(); - void lock(const char *filename, const int line, bool readonly); - void unlock(const char *filename, const int line); + ~RwLock(); + + void rdlock(const char *filename, int line); + void wrlock(const char *filename, int line); + bool trywrlock(const char *filename, int line); + void unlock(const char *filename, int line); }; diff --git a/src/ArpStatsHashMatrix.cpp b/src/ArpStatsHashMatrix.cpp index 3408a0550e..6c6235b929 100644 --- a/src/ArpStatsHashMatrix.cpp +++ b/src/ArpStatsHashMatrix.cpp @@ -41,7 +41,7 @@ ArpStatsMatrixElement* ArpStatsHashMatrix::get(const u_int8_t _src_mac[6], } else { ArpStatsMatrixElement *head; - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->wrlock(__FILE__, __LINE__); head = (ArpStatsMatrixElement*)table[hash]; while(head != NULL) { diff --git a/src/AutonomousSystemHash.cpp b/src/AutonomousSystemHash.cpp index 9f63c8cd50..d3f08f4ce4 100644 --- a/src/AutonomousSystemHash.cpp +++ b/src/AutonomousSystemHash.cpp @@ -45,7 +45,7 @@ AutonomousSystem* AutonomousSystemHash::get(IpAddress *ipa, bool is_inline_call, AutonomousSystem *head; if(!is_inline_call) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); head = (AutonomousSystem*)table[hash]; diff --git a/src/CountriesHash.cpp b/src/CountriesHash.cpp index b55856330e..9c5927faf8 100644 --- a/src/CountriesHash.cpp +++ b/src/CountriesHash.cpp @@ -42,7 +42,7 @@ Country* CountriesHash::get(const char *country_name, bool is_inline_call) { Country *head; if(!is_inline_call) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); head = (Country*)table[hash]; diff --git a/src/FlowHash.cpp b/src/FlowHash.cpp index e0c0e55a8b..6993553ad4 100644 --- a/src/FlowHash.cpp +++ b/src/FlowHash.cpp @@ -48,7 +48,7 @@ Flow* FlowHash::find(IpAddress *src_ip, IpAddress *dst_ip, return(NULL); if(!is_inline_call) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); // ntop->getTrace()->traceEvent(TRACE_NORMAL, "%u:%u / %u:%u [icmp: %u][key: %u][icmp info key: %u][head: 0x%x]", src_ip->key(), src_port, dst_ip->key(), dst_port, icmp_info ? 1 : 0, hash, icmp_info ? icmp_info->key() : 0, head); diff --git a/src/GenericHash.cpp b/src/GenericHash.cpp index 6771a784a9..d6c34950d8 100644 --- a/src/GenericHash.cpp +++ b/src/GenericHash.cpp @@ -34,8 +34,8 @@ GenericHash::GenericHash(NetworkInterface *_iface, u_int _num_hashes, for(u_int i = 0; i < num_hashes; i++) table[i] = NULL; - locks = new Mutex*[num_hashes]; - for(u_int i = 0; i < num_hashes; i++) locks[i] = new Mutex(); + locks = new RwLock*[num_hashes]; + for(u_int i = 0; i < num_hashes; i++) locks[i] = new RwLock(); last_purged_hash = _num_hashes - 1; } @@ -77,7 +77,7 @@ bool GenericHash::add(GenericHashEntry *h, bool do_lock) { u_int32_t hash = (h->key() % num_hashes); if(do_lock) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->wrlock(__FILE__, __LINE__); h->set_next(table[hash]); table[hash] = h; @@ -107,7 +107,7 @@ bool GenericHash::walk(u_int32_t *begin_slot, ntop->getTrace()->traceEvent(TRACE_NORMAL, "[walk] Locking %d [%p]", hash_id, locks[hash_id]); #endif - locks[hash_id]->lock(__FILE__, __LINE__); + locks[hash_id]->rdlock(__FILE__, __LINE__); head = table[hash_id]; while(head) { @@ -192,7 +192,9 @@ u_int GenericHash::purgeIdle(bool force_idle) { GenericHashEntry *head, *prev = NULL; // ntop->getTrace()->traceEvent(TRACE_NORMAL, "[purge] Locking %d", i); - locks[i]->lock(__FILE__, __LINE__); + if(!locks[i]->trywrlock(__FILE__, __LINE__)) + continue; /* Busy, will retry next round */ + head = table[i]; while(head) { @@ -253,7 +255,8 @@ GenericHashEntry* GenericHash::findByKey(u_int32_t key) { if(head == NULL) return(NULL); - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); + while(head) { if(!head->idle() && head->key() == key) break; diff --git a/src/HostHash.cpp b/src/HostHash.cpp index c021c619bb..11c6adde96 100644 --- a/src/HostHash.cpp +++ b/src/HostHash.cpp @@ -39,7 +39,7 @@ Host* HostHash::get(u_int16_t vlanId, IpAddress *key, bool is_inline_call) { Host *head; if(!is_inline_call) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); head = (Host*)table[hash]; diff --git a/src/MacHash.cpp b/src/MacHash.cpp index eae2ef82d0..f12e1a5846 100644 --- a/src/MacHash.cpp +++ b/src/MacHash.cpp @@ -44,7 +44,7 @@ Mac* MacHash::get(const u_int8_t mac[6], bool is_inline_call) { Mac *head; if(!is_inline_call) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); head = (Mac*)table[hash]; diff --git a/src/RwLock.cpp b/src/RwLock.cpp index e9221dbb54..debac70421 100644 --- a/src/RwLock.cpp +++ b/src/RwLock.cpp @@ -24,55 +24,96 @@ /* ******************************* */ RwLock::RwLock() { +#ifndef HAVE_RW_LOCK + Mutex m; +#else pthread_rwlock_init(&the_rwlock, NULL); -#ifdef RWLOCK_DEBUG - num_locks = num_unlocks = 0; - last_lock_file[0] = '\0', last_unlock_file[0] = '\0'; - last_lock_line = last_unlock_line = 0; #endif } /* ******************************* */ -void RwLock::lock(const char *filename, const int line, bool readonly) { - int rc; +RwLock::~RwLock() { +#ifndef HAVE_RW_LOCK + /* Mutex destructor called automatically */ +#else + pthread_rwlock_destroy(&the_rwlock); +#endif +} - errno = 0; +/* ******************************* */ + +void RwLock::lock(const char *filename, int line, bool readonly) { +#ifndef HAVE_RW_LOCK + m.lock(filename, line); +#else + int rc; if(readonly) rc = pthread_rwlock_rdlock(&the_rwlock); else rc = pthread_rwlock_wrlock(&the_rwlock); - if(rc != 0) - ntop->getTrace()->traceEvent(TRACE_WARNING, - "Unable to acquire lock. Return code %d [%s][errno=%d]", - rc, strerror(rc), errno); - -#ifdef RWLOCK_DEBUG - snprintf(last_lock_file, sizeof(last_lock_file), "%s", filename); - last_lock_line = line, num_locks++; - // ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s(%p)", __FUNCTION__, this); + if(rc) + ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to acquire lock. Return code %d [%s]", rc, strerror(rc), errno); #endif } /* ******************************* */ -void RwLock::unlock(const char *filename, const int line) { +bool RwLock::trylock(const char *filename, int line, bool readonly) { +#ifndef HAVE_RW_LOCK + m.lock(filename, line); + return true; /* Pretend to be always successful - indeed, if here the lock is acquired even in a blocking fashion */ +#else int rc; - errno = 0; + if(readonly) + rc = pthread_rwlock_tryrdlock(&the_rwlock); + else + rc = pthread_rwlock_trywrlock(&the_rwlock); - rc = pthread_rwlock_unlock(&the_rwlock); - - if(rc != 0) - ntop->getTrace()->traceEvent(TRACE_WARNING, - "pthread_rwlock_unlock() returned %d [%s][errno=%d]", - rc, strerror(rc), errno); + if(!rc) + return true; /* Lock acquired successfully */ -#ifdef RWLOCK_DEBUG - snprintf(last_unlock_file, sizeof(last_unlock_file), "%s", filename); - last_unlock_line = line, num_unlocks++; - // ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s(%p)", __FUNCTION__, this); + if(rc == EBUSY) + ; /* Normal, lock is being held by someone else */ + else + ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to acquire lock. Return code %d [%s]", rc, strerror(rc)); + + return false; /* Lock not acquired, held by someone else or there was an error */ +#endif +} + +/* ******************************* */ + +void RwLock::rdlock(const char *filename, int line) { + lock(filename, line, true /* readonly */); +} + +/* ******************************* */ + +void RwLock::wrlock(const char *filename, int line) { + lock(filename, line, false /* write */); +} + +/* ******************************* */ + +bool RwLock::trywrlock(const char *filename, int line) { + return trylock(filename, line, false /* write */); +} + +/* ******************************* */ + +void RwLock::unlock(const char *filename, int line) { +#ifndef HAVE_RW_LOCK + m.unlock(filename, line); +#else + int rc; + + rc = pthread_rwlock_unlock(&the_rwlock); + + if(rc) + ntop->getTrace()->traceEvent(TRACE_WARNING, "pthread_rwlock_unlock() returned %d [%s]", rc, strerror(rc), errno); #endif } diff --git a/src/ViewInterface.cpp b/src/ViewInterface.cpp index aae661fe6c..1aa0f81ebc 100644 --- a/src/ViewInterface.cpp +++ b/src/ViewInterface.cpp @@ -278,7 +278,7 @@ static bool viewed_flows_walker(GenericHashEntry *flow, void *user_data, bool *m if(acked_to_purge) { /* We can set the 'ready to be purged' state on behalf of the underlying viewed interface. - It is safe as this view is in sync with the viewed interfaces by bean of acked_to_purge */ + It is safe as this view is in sync with the viewed interfaces by mean of acked_to_purge */ f->set_hash_entry_state_ready_to_be_purged(); } diff --git a/src/VirtualHostHash.cpp b/src/VirtualHostHash.cpp index 7ddc2b107e..5f29be50fd 100644 --- a/src/VirtualHostHash.cpp +++ b/src/VirtualHostHash.cpp @@ -38,7 +38,7 @@ VirtualHost* VirtualHostHash::get(char *key) { } else { VirtualHost *head; - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->wrlock(__FILE__, __LINE__); head = (VirtualHost*)table[hash]; while(head != NULL) { diff --git a/src/VlanHash.cpp b/src/VlanHash.cpp index 4c01d4c79f..e0dbaac642 100644 --- a/src/VlanHash.cpp +++ b/src/VlanHash.cpp @@ -41,7 +41,7 @@ Vlan* VlanHash::get(u_int16_t _vlan_id, bool is_inline_call) { Vlan *head; if(!is_inline_call) - locks[hash]->lock(__FILE__, __LINE__); + locks[hash]->rdlock(__FILE__, __LINE__); head = (Vlan*)table[hash];