Commit 300c412c authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2100'

parents 3e963e68 48ff2069
......@@ -10,7 +10,7 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
noinst_LTLIBRARIES = libdatasrc_memory.la
libdatasrc_memory_la_SOURCES = \
rdata_encoder.h \
rdata_encoder.cc \
domaintree.h
libdatasrc_memory_la_SOURCES = rdata_encoder.h rdata_encoder.cc
libdatasrc_memory_la_SOURCES += domaintree.h
libdatasrc_memory_la_SOURCES += zone_data.h
libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
......@@ -229,37 +229,33 @@ public:
///
/// You should not delete the data, it is deleted when the tree is
/// destroyed.
T* getData() { return (data_); }
T* getData() { return (data_.get()); }
/// \brief Return the data stored in this node (const).
const T* getData() const { return (data_); }
const T* getData() const { return (data_.get()); }
/// \brief return whether the node has related data.
///
/// There can be empty nodes inside the DomainTree. They are usually the
/// non-terminal domains, but it is possible (yet probably meaningless)
/// empty nodes anywhere.
bool isEmpty() const { return (data_ == NULL); }
bool isEmpty() const { return (!data_); }
//@}
/// \name Setter functions.
//@{
/// \brief Set the data stored in the node. If there is old data, it
/// is either returned or destroyed based on what is passed in \c
/// old_data.
/// \brief Set the data stored in the node.
///
/// Any old data is destroyed.
///
/// \param mem_sgmt The \c MemorySegment that allocated memory for
/// the node data.
/// \param data The new data to set.
/// \param old_data If \c NULL is passed here, any old data is
/// destroyed. Otherwise, the old data is returned
/// in this location.
void setData(util::MemorySegment& mem_sgmt, T* data, T** old_data = NULL) {
if (old_data != NULL) {
*old_data = data;
} else {
void setData(util::MemorySegment& mem_sgmt, T* data) {
if (data_) {
const DT deleter;
deleter(mem_sgmt, data_);
deleter(mem_sgmt, data_.get());
}
data_ = data;
}
......@@ -476,7 +472,7 @@ private:
}
/// \brief Data stored here.
T* data_;
boost::interprocess::offset_ptr<T> data_;
/// \brief Internal or user-configurable flags of node's properties.
///
......@@ -1390,7 +1386,7 @@ DomainTree<T, DT>::deleteHelper(util::MemorySegment& mem_sgmt,
// free this one and go back to its parent.
DomainTreeNode<T, DT>* node = root;
root = root->getParent();
deleter(mem_sgmt, node->data_);
deleter(mem_sgmt, node->data_.get());
DomainTreeNode<T, DT>::destroy(mem_sgmt, node);
--node_count_;
}
......@@ -1652,8 +1648,12 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
isc::dns::LabelSequence target_labels(target_name);
int order = -1;
// For possible LabelSequence serialization we always store labels data
// in the separate local buffer.
uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH];
while (current != NULL) {
const dns::LabelSequence current_labels(current->getLabels());
const dns::LabelSequence current_labels(
dns::LabelSequence(current->getLabels(), labels_buf));
const isc::dns::NameComparisonResult compare_result =
target_labels.compare(current_labels);
const isc::dns::NameComparisonResult::NameRelation relation =
......
......@@ -20,6 +20,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += rdata_encoder_unittest.cc
run_unittests_SOURCES += domaintree_unittest.cc
run_unittests_SOURCES += zone_table_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <exceptions/exceptions.h>
#include <util/memory_segment_local.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <datasrc/result.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/zone_table.h>
#include <gtest/gtest.h>
#include <new> // for bad_alloc
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::datasrc::memory;
namespace {
// Memory segment specified for tests. It normally behaves like a "local"
// memory segment. If "throw count" is set to non 0 via setThrowCount(),
// it continues the normal behavior up to the specified number of calls to
// allocate(), and throws an exception at the next call.
class TestMemorySegment : public isc::util::MemorySegmentLocal {
public:
TestMemorySegment() : throw_count_(0) {}
virtual void* allocate(size_t size) {
if (throw_count_ > 0) {
if (--throw_count_ == 0) {
throw std::bad_alloc();
}
}
return (isc::util::MemorySegmentLocal::allocate(size));
}
void setThrowCount(size_t count) { throw_count_ = count; }
private:
size_t throw_count_;
};
class ZoneTableTest : public ::testing::Test {
protected:
ZoneTableTest() : zname1(Name("example.com")),
zname2(Name("example.net")),
zname3(Name("example")),
zone_table(ZoneTable::create(mem_sgmt_))
{}
~ZoneTableTest() {
if (zone_table != NULL) {
ZoneTable::destroy(mem_sgmt_, zone_table);
}
}
void TearDown() {
ZoneTable::destroy(mem_sgmt_, zone_table);
zone_table = NULL;
EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
}
const Name zname1, zname2, zname3;
TestMemorySegment mem_sgmt_;
ZoneTable* zone_table;
};
TEST_F(ZoneTableTest, create) {
// Test about creating a zone table. Normal case covers through other
// tests. We only check exception safety by letting the test memory
// segment throw.
mem_sgmt_.setThrowCount(2);
EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc);
// This shouldn't cause memory leak (that would be caught in TearDown()).
}
TEST_F(ZoneTableTest, addZone) {
// Normal successful case.
const ZoneTable::AddResult result1 =
zone_table->addZone(mem_sgmt_, zname1);
EXPECT_EQ(result::SUCCESS, result1.code);
// Duplicate add doesn't replace the existing data.
EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code);
EXPECT_EQ(result1.zone_data,
zone_table->addZone(mem_sgmt_, zname1).zone_data);
// names are compared in a case insensitive manner.
EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
Name("EXAMPLE.COM")).code);
// Add some more different ones. Should just succeed.
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
// Have the memory segment throw an exception in extending the internal
// tree. It still shouldn't cause memory leak (which would be detected
// in TearDown()).
mem_sgmt_.setThrowCount(2);
EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")),
std::bad_alloc);
}
TEST_F(ZoneTableTest, findZone) {
const ZoneTable::AddResult add_result1 =
zone_table->addZone(mem_sgmt_, zname1);
EXPECT_EQ(result::SUCCESS, add_result1.code);
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
const ZoneTable::FindResult find_result1 =
zone_table->findZone(Name("example.com"));
EXPECT_EQ(result::SUCCESS, find_result1.code);
EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
EXPECT_EQ(result::NOTFOUND,
zone_table->findZone(Name("example.org")).code);
EXPECT_EQ(NULL, zone_table->findZone(Name("example.org")).zone_data);
// there's no exact match. the result should be the longest match,
// and the code should be PARTIALMATCH.
EXPECT_EQ(result::PARTIALMATCH,
zone_table->findZone(Name("www.example.com")).code);
EXPECT_EQ(add_result1.zone_data,
zone_table->findZone(Name("www.example.com")).zone_data);
// make sure the partial match is indeed the longest match by adding
// a zone with a shorter origin and query again.
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_,
Name("com")).code);
EXPECT_EQ(add_result1.zone_data,
zone_table->findZone(Name("www.example.com")).zone_data);
}
}
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DATASRC_MEMORY_ZONE_DATA_H
#define DATASRC_MEMORY_ZONE_DATA_H 1
#include <util/memory_segment.h>
namespace isc {
namespace datasrc {
namespace memory {
/// \brief Data for a single zone.
///
/// It's currently empty and is only provided for the implementation of
/// ZoneTable. The actual implementation of this class is the subject of
/// Trac #2107.
class ZoneData {
private:
ZoneData() {}
~ZoneData() {}
public:
static ZoneData* create(util::MemorySegment& mem_sgmt) {
void* p = mem_sgmt.allocate(sizeof(ZoneData));
ZoneData* zone_data = new(p) ZoneData();
return (zone_data);
}
static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data) {
zone_data->~ZoneData();
mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
}
};
} // namespace memory
} // namespace datasrc
} // namespace isc
#endif // DATASRC_MEMORY_ZONE_DATA_H
// Local Variables:
// mode: c++
// End:
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/memory_segment.h>
#include <dns/name.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/zone_table.h>
#include <datasrc/memory/domaintree.h>
#include <cassert>
using namespace std;
using namespace isc::dns;
namespace isc {
namespace datasrc {
namespace memory {
namespace {
// A simple holder to create and use some objects in this implementation
// in an exception safe manner. It works like std::auto_ptr but much
// more simplified.
template <typename T>
class Holder {
public:
Holder(util::MemorySegment& mem_sgmt, T* obj) :
mem_sgmt_(mem_sgmt), obj_(obj)
{}
~Holder() {
if (obj_ != NULL) {
T::destroy(mem_sgmt_, obj_);
}
}
T* get() { return (obj_); }
T* release() {
T* ret = obj_;
obj_ = NULL;
return (ret);
}
private:
util::MemorySegment& mem_sgmt_;
T* obj_;
};
}
void
ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
ZoneData* zone_data) const
{
ZoneData::destroy(mem_sgmt, zone_data);
}
ZoneTable*
ZoneTable::create(util::MemorySegment& mem_sgmt) {
Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
void* p = mem_sgmt.allocate(sizeof(ZoneTable));
ZoneTable* zone_table = new(p) ZoneTable(holder.get());
holder.release();
return (zone_table);
}
void
ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get());
mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
}
ZoneTable::AddResult
ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
// Create a new ZoneData instance first. If the specified name already
// exists in the table, the new data will soon be destroyed, but we want
// to make sure if this allocation fails the tree won't be changed to
// provide as strong guarantee as possible. In practice, we generally
// expect the caller tries to add a zone only when it's a new one, so
// this should be a minor concern.
Holder<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
// Get the node where we put the zone
ZoneTableNode* node(NULL);
switch (zones_->insert(mem_sgmt, zone_name, &node)) {
case ZoneTableTree::SUCCESS:
case ZoneTableTree::ALREADYEXISTS:
// These are OK
break;
default:
// Can Not Happen
assert(false);
}
// Can Not Happen
assert(node != NULL);
// Is it empty? We either just created it or it might be nonterminal
if (node->isEmpty()) {
node->setData(mem_sgmt, holder.get());
return (AddResult(result::SUCCESS, holder.release()));
} else { // There's something there already
return (AddResult(result::EXIST, node->getData()));
}
}
ZoneTable::FindResult
ZoneTable::findZone(const Name& name) const {
ZoneTableNode* node(NULL);
result::Result my_result;
// Translate the return codes
switch (zones_->find(name, &node)) {
case ZoneTableTree::EXACTMATCH:
my_result = result::SUCCESS;
break;
case ZoneTableTree::PARTIALMATCH:
my_result = result::PARTIALMATCH;
break;
case ZoneTableTree::NOTFOUND:
// We have no data there, so translate the pointer to NULL as well
return (FindResult(result::NOTFOUND, NULL));
default:
// Can Not Happen
assert(0);
// Because of warning
return (FindResult(result::NOTFOUND, NULL));
}
// Can Not Happen (remember, NOTFOUND is handled)
assert(node != NULL);
return (FindResult(my_result, node->getData()));
}
} // end of namespace memory
} // end of namespace datasrc
} // end of namespace isc
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __DATASRC_MEMORY_ZONE_TABLE_H
#define __DATASRC_MEMORY_ZONE_TABLE_H 1
#include <util/memory_segment.h>
#include <datasrc/result.h>
#include <datasrc/memory/domaintree.h>
#include <boost/noncopyable.hpp>
#include <boost/interprocess/offset_ptr.hpp>
namespace isc {
namespace dns {
class Name;
class RRClass;
}
namespace datasrc {
namespace memory {
// forward declaration: in this header it's mostly an opaque type.
class ZoneData;
/// \brief A conceptual table of authoritative zones.
///
/// This class is actually a simple wrapper for a \c DomainTree whose data is
/// of \c ZoneData, and provides allocator, deallocator, and some basic
/// manipulation methods.
///
/// A single \c ZoneData object is intended to be used for a single specific
/// RR class, and provides a mapping from a name to a \c ZoneData (using the
/// best matching search semantics). The \c ZoneData class itself does not
/// maintain the information of the RR class; the user of this class is
/// responsible for associating a specific RR class to a corresponding
/// \c ZoneData object.
///
/// This class is designed so an instance can be stored in a shared memory
/// region. So it only contains straightforward data (e.g., it doesn't hold
/// a pointer to an object of some base class that contains virtual methods),
/// and some pointers (either as a direct or indirect member variable) are
/// represented as offset pointers. For the same reason this class should
/// never has virtual methods (and as a result, should never be inherited
/// in practice). When this class is extended these properties must be
/// retained.
///
/// This class is intended to be used as a backend for the \c MemoryDataSrc
/// class, and is not intended to be used for other general purposes.
class ZoneTable : boost::noncopyable {
private:
// The deleter for the zone data stored in the table.
struct ZoneDataDeleter {
ZoneDataDeleter() {}
void operator()(util::MemorySegment& mem_sgmt,
ZoneData* zone_data) const;
};
// Type aliases to make it shorter
typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
typedef DomainTreeNode<ZoneData, ZoneDataDeleter> ZoneTableNode;
public:
/// \brief Result data of addZone() method.
struct AddResult {
AddResult(result::Result param_code, ZoneData* param_zone_data) :
code(param_code), zone_data(param_zone_data)
{}
const result::Result code;
ZoneData* const zone_data;
};
/// \brief Result data of findZone() method.
struct FindResult {
FindResult(result::Result param_code,
const ZoneData* param_zone_data) :
code(param_code), zone_data(param_zone_data)
{}
const result::Result code;
const ZoneData* const zone_data;
};
private:
/// Constructor.
///
/// An object of this class is always expected to be created by the
/// allocator (\c create()), so the constructor is hidden as private.
///
/// This constructor internally involves resource allocation, and if
/// it fails, a corresponding standard exception will be thrown.
/// It never throws an exception otherwise.
ZoneTable(ZoneTableTree* zones) : zones_(zones)
{}
public:
/// \brief Allocate and construct \c ZoneTable
///
/// This static method allocates memory for a new \c ZoneTable object
/// from the given memory segment, constructs the object, and returns
/// a pointer to it.
///
/// \throw std::bad_alloc Memory allocation fails.
///
/// \param mem_sgmt A \c MemorySegment from which memory for the new
/// \c ZoneTable is allocated.
static ZoneTable* create(util::MemorySegment& mem_sgmt);
/// \brief Destruct and deallocate \c ZoneTable
///
/// \throw none
///
/// \param mem_sgmt The \c MemorySegment that allocated memory for
/// \c ztable.
/// \param ztable A non NULL pointer to a valid \c ZoneTable object
/// that was originally created by the \c create() method (the behavior
/// is undefined if this condition isn't met).
static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
/// Add a new zone to the \c ZoneTable.
///
/// This method creates a new \c ZoneData for the given zone name and
/// holds it in the internal table. The newly created zone data will be
/// returned via the \c zone_data member of the return value. If the given
/// zone name already exists in the table, a new data object won't be
/// created; instead, the existing corresponding data will be returned.
///
/// \throw std::bad_alloc Internal resource allocation fails.
///
/// \param zone_name The name of the zone to be added.
/// \return \c result::SUCCESS If the zone is successfully
/// added to the zone table.
/// \return \c result::EXIST The zone table already contains
/// zone of the same origin.
AddResult addZone(util::MemorySegment& mem_sgmt,
const dns::Name& zone_name);
/// Find a zone that best matches the given name in the \c ZoneTable.
///
/// It searches the internal storage for a zone that gives the
/// longest match against \c name, and returns the result in the
/// form of a \c FindResult object as follows:
/// - \c code: The result code of the operation.
/// - \c result::SUCCESS: A zone that gives an exact match
/// is found
/// - \c result::PARTIALMATCH: A zone whose origin is a
/// super domain of \c name is found (but there is no exact match)
/// - \c result::NOTFOUND: For all other cases.
/// - \c zone_data: corresponding zone data of the found zone; NULL if
/// no matching zone is found.
///
/// \throw none
///
/// \param name A domain name for which the search is performed.
/// \return A \c FindResult object enclosing the search result (see above).
FindResult findZone(const isc::dns::Name& name) const;
private:
boost::interprocess::offset_ptr<ZoneTableTree> zones_;