[svn] commit: r3516 - in /branches/trac408/src/lib/nsas: tests/zone_entry_unittest.cc zone_entry.cc zone_entry.h
BIND 10 source code commits
bind10-changes at lists.isc.org
Sat Nov 13 17:28:07 UTC 2010
Author: vorner
Date: Sat Nov 13 17:28:07 2010
New Revision: 3516
Log:
Interface and tests for ZoneEntry::Lock
Which will lock both the zone entry and the nameservers, so we can
manipulate with them a little bit.
Modified:
branches/trac408/src/lib/nsas/tests/zone_entry_unittest.cc
branches/trac408/src/lib/nsas/zone_entry.cc
branches/trac408/src/lib/nsas/zone_entry.h
Modified: branches/trac408/src/lib/nsas/tests/zone_entry_unittest.cc
==============================================================================
--- branches/trac408/src/lib/nsas/tests/zone_entry_unittest.cc (original)
+++ branches/trac408/src/lib/nsas/tests/zone_entry_unittest.cc Sat Nov 13 17:28:07 2010
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
#include <dns/rrclass.h>
@@ -36,6 +37,7 @@
// String constants. These should end in a dot.
static const std::string EXAMPLE_CO_UK("example.co.uk.");
+static const std::string EXAMPLE_NET("example.net.");
/// \brief Test Fixture Class
class ZoneEntryTest : public ::testing::Test {
@@ -95,5 +97,75 @@
EXPECT_TRUE(zone_const.begin() + 1 == zone_const.end());
}
+void lockAndWait(ZoneEntry* zone, barrier* when) {
+ ZoneEntry::Lock lock(zone->getLock());
+ when->wait();
+}
+
+void lockAndKeep(ZoneEntry* zone, bool* locked_self, bool* locked_other,
+ barrier* when)
+{
+ // Lock
+ ZoneEntry::Lock lock(zone->getLock());
+ *locked_self = true;
+ // Wait for the start of the other thread
+ when->wait();
+ // Make sure the other thread gets a chance to run
+ for (int i(0); i < 100; ++ i) {
+ this_thread::yield();
+ EXPECT_FALSE(*locked_other);
+ }
+ *locked_self = false;
+}
+
+TEST_F(ZoneEntryTest, Lock) {
+ // Create some testing data
+ ZoneEntry z1(EXAMPLE_CO_UK, RRClass::IN().getCode());
+ ZoneEntry z2(EXAMPLE_NET, RRClass::IN().getCode());
+ shared_ptr<NameserverEntry> ns1(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN().getCode()));
+ shared_ptr<NameserverEntry> ns2(new NameserverEntry(EXAMPLE_NET,
+ RRClass::IN().getCode()));
+ z1.nameserverAdd(ns1);
+ z2.nameserverAdd(ns2);
+
+ barrier both(2);
+
+ // This tries that both can lock right now.
+ // FIXME If they can't it will deadlock. Any idea how to do it better?
+ thread t1(lockAndWait, &z1, &both);
+ thread t2(lockAndWait, &z2, &both);
+ t1.join();
+ t2.join();
+
+ z1.nameserverAdd(ns2);
+ z2.nameserverAdd(ns1);
+ // Now check that they can't both lock at the same time.
+ barrier both_second(2);
+ bool l1(false), l2(false);
+ thread t3(lockAndKeep, &z1, &l1, &l2, &both);
+ thread t4(lockAndKeep, &z2, &l2, &l1, &both_second);
+ both.wait();
+ // Make sure one of them is started
+ for (int i(0); i < 100; ++ i) {
+ this_thread::yield();
+ }
+ both_second.wait();
+ t3.join();
+ t4.join();
+
+ // Try it the other way around (so it does not depend on the order of nameservers
+ thread t6(lockAndKeep, &z2, &l2, &l1, &both);
+ thread t5(lockAndKeep, &z1, &l1, &l2, &both_second);
+ both.wait();
+ // Make sure one of them is started
+ for (int i(0); i < 100; ++ i) {
+ this_thread::yield();
+ }
+ both_second.wait();
+ t5.join();
+ t6.join();
+}
+
} // namespace nsas
} // namespace isc
Modified: branches/trac408/src/lib/nsas/zone_entry.cc
==============================================================================
--- branches/trac408/src/lib/nsas/zone_entry.cc (original)
+++ branches/trac408/src/lib/nsas/zone_entry.cc Sat Nov 13 17:28:07 2010
@@ -22,25 +22,25 @@
namespace {
// Shorter aliases for frequently used types
-typedef boost::mutex::scoped_lock Lock;
+typedef boost::mutex::scoped_lock LLock; // Local lock, nameservers not locked
typedef boost::shared_ptr<AddressRequestCallback> CallbackPtr;
}
void
ZoneEntry::addCallback(CallbackPtr callback) {
- Lock lock(mutex_);
+ LLock lock(mutex_);
callbacks_.push_back(callback);
}
bool
ZoneEntry::hasCallbacks() const {
- Lock lock(mutex_);
+ LLock lock(mutex_);
return (!callbacks_.empty());
}
CallbackPtr
ZoneEntry::popCallback() {
- Lock lock(mutex_);
+ LLock lock(mutex_);
CallbackPtr result(callbacks_.front());
callbacks_.pop_front();
return (result);
Modified: branches/trac408/src/lib/nsas/zone_entry.h
==============================================================================
--- branches/trac408/src/lib/nsas/zone_entry.h (original)
+++ branches/trac408/src/lib/nsas/zone_entry.h Sat Nov 13 17:28:07 2010
@@ -116,6 +116,38 @@
iterator end() { return (nameservers_.end()); }
const_iterator begin() const { return (nameservers_.begin()); }
const_iterator end() const { return (nameservers_.end()); }
+
+ /**
+ * \short Lock of the zone entry.
+ *
+ * Something like a scope lock for the zone entry. It can be copyed (so
+ * the result of the getLock() can be assigned to a local variable). The
+ * lock is released once all copyes of the getLock result are destroyed.
+ * However, it is not reentrant (another call to getLock will block).
+ *
+ * This locks both the zone entry and all nameserver entries in a manner
+ * avoiding deadlocks (sorts the nameserver entries before trying to
+ * acquire them). However, it asumes noone does any other kind of locking
+ * of multiple muteces.
+ *
+ * Copy constructor, assignment operator and destructor are default.
+ * The constructor that creates a new lock is private, use getLock()
+ * to lock a zone entry.
+ */
+ class Lock {
+ private:
+ struct Impl;
+ boost::shared_ptr<Impl> impl_;
+ Lock(ZoneEntry&);
+ friend class ZoneEntry;
+ };
+
+ /**
+ * \short Acquire a lock.
+ *
+ * \see Lock
+ */
+ Lock getLock() { return Lock(*this); }
private:
mutable boost::mutex mutex_; ///< Mutex protecting this zone entry
std::string name_; ///< Canonical zone name
More information about the bind10-changes
mailing list