This commit 1) makes the StatBag 64 bit on 64 bit systems, 2) removes all locks and 3) has gone AtomicCounter 'native'
The upshot of this is that 64 bit users will suddenly get 64 bit counters. A second upshot is that multicore systems should now see consistent statistics again.
One important thing that changed is that the StatBag class used to be completely thread safe, and now it no longer is. You can't declare new counters while the StatBag is in use. This should not be happening, but you never know.
Finally, there is now a StatBag unit test.
dnswriter.cc base64.cc base32.cc dnsrecords.cc dnslabeltext.cc dnsparser.cc \
rcpgenerator.cc ednssubnet.cc nsecrecords.cc sillyrecords.cc dnssecinfra.cc \
test-base64_cc.cc test-iputils_hh.cc test-dns_random_hh.cc dns_random.cc \
- test-rcpgenerator_cc.cc responsestats.cc test-bindparser_cc.cc \
+ test-rcpgenerator_cc.cc responsestats.cc test-bindparser_cc.cc test-statbag_cc.cc \
bindparser.cc bindlexer.c test-zoneparser_tng_cc.cc zoneparser-tng.hh \
zoneparser-tng.cc dns.cc
DNSPacket question;
DNSPacket cached;
- unsigned int &numreceived=*S.getPointer("udp-queries");
- unsigned int &numreceiveddo=*S.getPointer("udp-do-queries");
+ AtomicCounter &numreceived=*S.getPointer("udp-queries");
+ AtomicCounter &numreceiveddo=*S.getPointer("udp-do-queries");
- unsigned int &numreceived4=*S.getPointer("udp4-queries");
+ AtomicCounter &numreceived4=*S.getPointer("udp4-queries");
- unsigned int &numreceived6=*S.getPointer("udp6-queries");
+ AtomicCounter &numreceived6=*S.getPointer("udp6-queries");
int diff;
bool logDNSQueries = ::arg().mustDo("log-dns-queries");
private:
NetmaskGroup d_ng;
int d_sock;
- unsigned int* d_resanswers;
- unsigned int* d_udpanswers;
- unsigned int* d_resquestions;
+ AtomicCounter* d_resanswers;
+ AtomicCounter* d_udpanswers;
+ AtomicCounter* d_resquestions;
pthread_mutex_t d_lock;
uint16_t d_xor;
int getID_locked();
return atomic_exchange_and_add( &value_, +1 );
}
+ native_t operator+=(int val)
+ {
+ return atomic_exchange_and_add( &value_, val );
+ }
+
+
native_t operator--()
{
return atomic_exchange_and_add( &value_, -1 ) - 1;
void UDPNameserver::send(DNSPacket *p)
{
const string& buffer=p->getString();
- static unsigned int &numanswered=*S.getPointer("udp-answers");
- static unsigned int &numanswered4=*S.getPointer("udp4-answers");
- static unsigned int &numanswered6=*S.getPointer("udp6-answers");
- static unsigned int &bytesanswered=*S.getPointer("udp-answers-bytes");
+ static AtomicCounter &numanswered=*S.getPointer("udp-answers");
+ static AtomicCounter &numanswered4=*S.getPointer("udp4-answers");
+ static AtomicCounter &numanswered6=*S.getPointer("udp6-answers");
+ static AtomicCounter &bytesanswered=*S.getPointer("udp-answers-bytes");
g_rs.submitResponse(p->qtype.getCode(), buffer.length(), true);
WriteLock l(&d_mut);
int delcount=d_map.size();
d_map.clear();
- *d_statnumentries=0;
+ *d_statnumentries=AtomicCounter(0);
return delcount;
}
pair<cmap_t::iterator, cmap_t::iterator> range = d_map.equal_range(tie(qname));
d_map.erase(range.first, range.second);
}
- *d_statnumentries=d_map.size();
+ *d_statnumentries=AtomicCounter(d_map.size());
return delcount;
}
// called from ueberbackend
{
WriteLock l(&d_mut);
- *d_statnumentries=d_map.size();
+ *d_statnumentries=AtomicCounter(d_map.size());
unsigned int maxCached=::arg().asNum("max-cache-entries");
unsigned int toTrim=0;
- unsigned int cacheSize=*d_statnumentries;
+ AtomicCounter::native_t cacheSize=*d_statnumentries;
if(maxCached && cacheSize > maxCached) {
toTrim = cacheSize - maxCached;
break;
}
// cerr<<"erased: "<<erased<<endl;
- *d_statnumentries=d_map.size();
+ *d_statnumentries=AtomicCounter(d_map.size());
DLOG(L<<"Done with cache clean"<<endl);
}
int d_ttl;
int d_recursivettl;
bool d_doRecursion;
- unsigned int *d_statnumhit;
- unsigned int *d_statnummiss;
- unsigned int *d_statnumentries;
+ AtomicCounter *d_statnumhit;
+ AtomicCounter *d_statnummiss;
+ AtomicCounter *d_statnumentries;
};
if(p->d.rd) {
- static unsigned int &rdqueries=*S.getPointer("rd-queries");
+ static AtomicCounter &rdqueries=*S.getPointer("rd-queries");
rdqueries++;
}
StatBag::StatBag()
{
d_doRings=false;
- pthread_mutex_init(&d_lock,0);
}
-
-
-/** this NEEDS TO HAVE THE LOCK held already! */
void StatBag::exists(const string &key)
{
if(!d_stats.count(key))
{
- unlock(); // it's the details that count
throw PDNSException("Trying to deposit into unknown StatBag key '"+key+"'");
}
}
{
string dir;
ostringstream o;
- lock();
- for(map<string, unsigned int *>::const_iterator i=d_stats.begin();
+
+ for(map<string, AtomicCounter *>::const_iterator i=d_stats.begin();
i!=d_stats.end();
i++)
{
o<<i->first<<"="<<*(i->second)<<",";
}
- unlock();
+
dir=o.str();
return dir;
}
vector<string>StatBag::getEntries()
{
vector<string> ret;
- lock();
- for(map<string, unsigned int *>::const_iterator i=d_stats.begin();
+
+ for(map<string, AtomicCounter *>::const_iterator i=d_stats.begin();
i!=d_stats.end();
i++)
ret.push_back(i->first);
- unlock();
+
return ret;
}
string StatBag::getDescrip(const string &item)
{
- lock();
- string tmp=d_keyDescrips[item];
- unlock();
- return tmp;
+ exists(item);
+ return d_keyDescrips[item];
}
void StatBag::declare(const string &key, const string &descrip)
{
- lock();
- unsigned int *i=new unsigned int(0);
+ AtomicCounter *i=new AtomicCounter(0);
d_stats[key]=i;
d_keyDescrips[key]=descrip;
- unlock();
}
-void StatBag::set(const string &key, unsigned int value)
+void StatBag::set(const string &key, AtomicCounter::native_t value)
{
- lock();
exists(key);
- *d_stats[key]=value;
-
- unlock();
+ *d_stats[key]=AtomicCounter(value);
}
-unsigned int StatBag::read(const string &key)
+AtomicCounter::native_t StatBag::read(const string &key)
{
- lock();
-
- if(!d_stats.count(key))
- {
- unlock();
- return 0;
- }
-
- unsigned int tmp=*d_stats[key];
+ exists(key);
- unlock();
- return tmp;
+ return *d_stats[key];
}
-unsigned int StatBag::readZero(const string &key)
+AtomicCounter::native_t StatBag::readZero(const string &key)
{
- lock();
-
- if(!d_stats.count(key))
- {
- unlock();
- return 0;
- }
-
- unsigned int tmp=*d_stats[key];
+ exists(key);
+ AtomicCounter::native_t tmp=*d_stats[key];
d_stats[key]=0;
-
- unlock();
return tmp;
}
return o.str();
}
-unsigned int *StatBag::getPointer(const string &key)
+AtomicCounter *StatBag::getPointer(const string &key)
{
exists(key);
return d_stats[key];
StatBag::~StatBag()
{
- for(map<string,unsigned int *>::const_iterator i=d_stats.begin();
+ for(map<string, AtomicCounter *>::const_iterator i=d_stats.begin();
i!=d_stats.end();
i++)
{
//! use this to gather and query statistics
class StatBag
{
- map<string, unsigned int *> d_stats;
+ map<string, AtomicCounter *> d_stats;
map<string, string> d_keyDescrips;
map<string,StatRing>d_rings;
bool d_doRings;
- pthread_mutex_t d_lock;
public:
StatBag(); //!< Naked constructor. You need to declare keys before this class becomes useful
void exists(const string &key); //!< call this function to throw an exception in case a key does not exist
inline void deposit(const string &key, int value); //!< increment the statistics behind this key by value amount
inline void inc(const string &key); //!< increase this key's value by one
- void set(const string &key, unsigned int value); //!< set this key's value
- unsigned int read(const string &key); //!< read the value behind this key
- unsigned int readZero(const string &key); //!< read the value behind this key, and zero it afterwards
- unsigned int *getPointer(const string &key); //!< get a direct pointer to the value behind a key. Use this for high performance increments
+ void set(const string &key, AtomicCounter::native_t value); //!< set this key's value
+ AtomicCounter::native_t read(const string &key); //!< read the value behind this key
+ AtomicCounter::native_t readZero(const string &key); //!< read the value behind this key, and zero it afterwards
+ AtomicCounter *getPointer(const string &key); //!< get a direct pointer to the value behind a key. Use this for high performance increments
string getValueStr(const string &key); //!< read a value behind a key, and return it as a string
string getValueStrZero(const string &key); //!< read a value behind a key, and return it as a string, and zero afterwards
-
-private:
- void lock(){pthread_mutex_lock(&d_lock);}
- void unlock(){pthread_mutex_unlock(&d_lock);}
};
inline void StatBag::deposit(const string &key, int value)
{
- lock();
exists(key);
*d_stats[key]+=value;
-
- unlock();
}
inline void StatBag::inc(const string &key)
--- /dev/null
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#include <boost/test/unit_test.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/tuple/tuple.hpp>
+#include "misc.hh"
+#include "dns.hh"
+#include "statbag.hh"
+
+using std::string;
+
+static void *threadMangler(void* a)
+{
+ AtomicCounter* ac = (AtomicCounter*)a;
+ for(unsigned int n=0; n < 10000000; ++n)
+ (*ac)++;
+ return 0;
+}
+
+BOOST_AUTO_TEST_SUITE(misc_hh)
+
+BOOST_AUTO_TEST_CASE(test_StatBagBasic) {
+ StatBag s;
+ s.declare("a", "description");
+ s.declare("b", "description");
+ s.declare("c", "description");
+ s.inc("a");
+ BOOST_CHECK_EQUAL(s.read("a"), 1);
+
+ int n;
+ for(n=0; n < 1000000; ++n)
+ s.inc("b");
+
+ BOOST_CHECK_EQUAL(s.read("b"), n);
+
+ AtomicCounter* ac = s.getPointer("a");
+ for(n=0; n < 1000000; ++n)
+ (*ac)++;
+
+ BOOST_CHECK_EQUAL(s.read("a"), n+1);
+
+ AtomicCounter* acc = s.getPointer("c");
+ pthread_t tid[4];
+ for(int i=0; i < 4; ++i)
+ pthread_create(&tid[i], 0, threadMangler, (void*)acc);
+ void* res;
+ for(int i=0; i < 4 ; ++i)
+ pthread_join(tid[i], &res);
+
+ BOOST_CHECK_EQUAL(s.read("c"), 40000000U);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
int UeberBackend::cacheHas(const Question &q, vector<DNSResourceRecord> &rrs)
{
extern PacketCache PC;
- static unsigned int *qcachehit=S.getPointer("query-cache-hit");
- static unsigned int *qcachemiss=S.getPointer("query-cache-miss");
+ static AtomicCounter *qcachehit=S.getPointer("query-cache-hit");
+ static AtomicCounter *qcachemiss=S.getPointer("query-cache-miss");
if(!d_cache_ttl && ! d_negcache_ttl) {
(*qcachemiss)++;