Unverified Commit 7868cb5a authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #2207

"define and implement (datasrc::memory::)ZoneUpdater class"

It is now ZoneWriter.

No changelog.

Conflicts:
	src/lib/datasrc/memory/Makefile.am
parents c13d57b5 08e8d762
......@@ -24,6 +24,9 @@ libdatasrc_memory_la_SOURCES += zone_table_segment_local.h zone_table_segment_lo
libdatasrc_memory_la_SOURCES += zone_data_updater.h zone_data_updater.cc
libdatasrc_memory_la_SOURCES += zone_data_loader.h zone_data_loader.cc
libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
libdatasrc_memory_la_SOURCES += zone_writer.h
libdatasrc_memory_la_SOURCES += zone_writer_local.h zone_writer_local.cc
libdatasrc_memory_la_SOURCES += load_action.h
nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
......
// 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 LOAD_ACTION_H
#define LOAD_ACTION_H
#include <boost/function.hpp>
namespace isc {
// Forward declarations
namespace util{
class MemorySegment;
}
namespace datasrc {
namespace memory {
class ZoneData;
/// \brief Callback to load data into the memory
///
/// This is called from the ZoneWriter whenever there's need to load the
/// zone data. The callback should allocate new ZoneData and fill it with
/// the zone content. It is up to the callback to know where or how to
/// load the data, or even the origin and class of the zone (it is assumed
/// the callback will be some kind of functor).
///
/// All data should be allocated from the passed MemorySegment. The ownership
/// is passed onto the caller.
///
/// It must not return NULL.
typedef boost::function<ZoneData*(util::MemorySegment&)> LoadAction;
}
}
}
#endif
......@@ -16,6 +16,7 @@
#define __ZONE_TABLE_SEGMENT_H__
#include <datasrc/memory/zone_table.h>
#include "load_action.h"
#include <cc/data.h>
#include <util/memory_segment.h>
......@@ -24,8 +25,14 @@
#include <stdlib.h>
namespace isc {
// Some forward declarations
namespace dns {
class Name;
class RRClass;
}
namespace datasrc {
namespace memory {
class ZoneWriter;
/// \brief Memory-management independent entry point that contains a
/// pointer to a zone table in memory.
......@@ -45,6 +52,26 @@ public:
return (table.get());
}
/// \brief Method to set the internal table
///
/// The interface is tentative, we don't know if this is the correct place
/// and way to set the data. But for now, we need something to be there
/// at least for the tests. So we have this. For this reason, there are
/// no tests for this method directly. Do not use in actual
/// implementation.
///
/// It can be used only once, to initially set it. It can't replace the
/// one already there.
///
/// \param table Pointer to the table to use.
/// \throw isc::Unexpected if called the second time.
void setTable(ZoneTable* table) {
if (this->table.get() != NULL) {
isc_throw(isc::Unexpected, "Replacing table");
}
this->table = table;
}
private:
boost::interprocess::offset_ptr<ZoneTable> table;
};
......@@ -101,6 +128,21 @@ public:
///
/// \param segment The segment to destroy.
static void destroy(ZoneTableSegment* segment);
/// \brief Create a zone write corresponding to this segment
///
/// This creates a new write that can be used to update zones
/// inside this zone table segment.
///
/// \param loadAction Callback to provide the actual data.
/// \param origin The origin of the zone to reload.
/// \param rrclass The class of the zone to reload.
/// \return New instance of a zone writer. The ownership is passed
/// onto the caller and the caller needs to \c delete it when
/// it's done with the writer.
virtual ZoneWriter* getZoneWriter(const LoadAction& load_action,
const dns::Name& origin,
const dns::RRClass& rrclass) = 0;
};
} // namespace memory
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <datasrc/memory/zone_table_segment_local.h>
#include "zone_writer_local.h"
using namespace isc::util;
......@@ -38,6 +39,14 @@ ZoneTableSegmentLocal::getMemorySegment() {
return (mem_sgmt_);
}
ZoneWriter*
ZoneTableSegmentLocal::getZoneWriter(const LoadAction& load_action,
const dns::Name& name,
const dns::RRClass& rrclass)
{
return (new ZoneWriterLocal(this, load_action, name, rrclass));
}
} // namespace memory
} // namespace datasrc
} // namespace isc
......@@ -54,6 +54,10 @@ public:
/// implementation (a MemorySegmentLocal instance).
virtual isc::util::MemorySegment& getMemorySegment();
/// \brief Concrete implementation of ZoneTableSegment::getZoneWriter
virtual ZoneWriter* getZoneWriter(const LoadAction& load_action,
const dns::Name& origin,
const dns::RRClass& rrclass);
private:
ZoneTableHeader header_;
isc::util::MemorySegmentLocal mem_sgmt_;
......
// 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 MEM_ZONE_WRITER_H
#define MEM_ZONE_WRITER_H
#include "load_action.h"
namespace isc {
namespace datasrc {
namespace memory {
/// \brief Does an update to a zone.
///
/// This abstract base class represents the work of a reload of a zone.
/// The work is divided into three stages -- load(), install() and cleanup().
/// They should be called in this order for the effect to take place.
///
/// We divide them so the update of zone data can be done asynchronously,
/// in a different thread. The install() operation is the only one that needs
/// to be done in a critical section.
///
/// Each derived class implementation must provide the strong exception
/// guarantee for each public method. That is, when any of the methods
/// throws, the entire state should stay the same as before the call
/// (how to achieve that may be implementation dependant).
class ZoneWriter {
public:
/// \brief Get the zone data into memory.
///
/// This is the part that does the time-consuming loading into the memory.
/// This can be run in a separate thread, for example. It has no effect on
/// the data actually served, it only prepares them for future use.
///
/// This is the first method you should call on the object. Never call it
/// multiple times.
///
/// \note As this contains reading of files or other data sources, or with
/// some other source of the data to load, it may throw quite anything.
/// If it throws, do not call any other methods on the object and
/// discard it.
/// \note After successful load(), you have to call cleanup() some time
/// later.
/// \throw isc::InvalidOperation if called second time.
virtual void load() = 0;
/// \brief Put the changes to effect.
///
/// This replaces the old version of zone with the one previously prepared
/// by load(). It takes ownership of the old zone data, if any.
///
/// You may call it only after successful load() and at most once.
///
/// The operation is expected to be fast and is meant to be used inside
/// a critical section.
///
/// This may throw in rare cases, depending on the concrete implementation.
/// If it throws, you still need to call cleanup().
///
/// \throw isc::InvalidOperation if called without previous load() or for
/// the second time or cleanup() was called already.
virtual void install() = 0;
/// \brief Clean up resources.
///
/// This releases all resources held by owned zone data. That means the
/// one loaded by load() in case install() was not called or was not
/// successful, or the one replaced in install().
///
/// Generally, this should never throw.
virtual void cleanup() = 0;
};
}
}
}
#endif
// 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 "zone_writer_local.h"
#include "zone_data.h"
#include "zone_table_segment_local.h"
#include <memory>
using std::auto_ptr;
namespace isc {
namespace datasrc {
namespace memory {
ZoneWriterLocal::ZoneWriterLocal(ZoneTableSegmentLocal* segment,
const LoadAction& load_action,
const dns::Name& origin,
const dns::RRClass& rrclass) :
segment_(segment),
load_action_(load_action),
origin_(origin),
rrclass_(rrclass),
zone_data_(NULL),
state_(ZW_UNUSED)
{}
ZoneWriterLocal::~ZoneWriterLocal() {
// Clean up everything there might be left if someone forgot, just
// in case.
cleanup();
}
void
ZoneWriterLocal::load() {
if (state_ != ZW_UNUSED) {
isc_throw(isc::InvalidOperation, "Trying to load twice");
}
zone_data_ = load_action_(segment_->getMemorySegment());
if (zone_data_ == NULL) {
// Bug inside load_action_.
isc_throw(isc::InvalidOperation, "No data returned from load action");
}
state_ = ZW_LOADED;
}
void
ZoneWriterLocal::install() {
if (state_ != ZW_LOADED) {
isc_throw(isc::InvalidOperation, "No data to install");
}
ZoneTable* table(segment_->getHeader().getTable());
if (table == NULL) {
isc_throw(isc::Unexpected, "No zone table present");
}
const ZoneTable::AddResult result(table->addZone(
segment_->getMemorySegment(),
rrclass_, origin_, zone_data_));
state_ = ZW_INSTALLED;
zone_data_ = result.zone_data;
}
void
ZoneWriterLocal::cleanup() {
// We eat the data (if any) now.
if (zone_data_ != NULL) {
ZoneData::destroy(segment_->getMemorySegment(), zone_data_, rrclass_);
zone_data_ = NULL;
state_ = ZW_CLEANED;
}
}
}
}
}
// 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 MEM_ZONE_WRITER_LOCAL_H
#define MEM_ZONE_WRITER_LOCAL_H
#include "zone_writer.h"
#include <dns/rrclass.h>
#include <dns/name.h>
namespace isc {
namespace datasrc {
namespace memory {
class ZoneData;
class ZoneTableSegmentLocal;
/// \brief Writer implementation which loads data locally.
///
/// This implementation prepares a clean zone data and lets one callback
/// to fill it and another to install it somewhere. The class does mostly
/// nothing (and delegates the work to the callbacks), just stores little bit
/// of state between the calls.
class ZoneWriterLocal : public ZoneWriter {
public:
/// \brief Constructor
///
/// \param segment The zone table segment to store the zone into.
/// \param load_action The callback used to load data.
/// \param install_action The callback used to install the loaded zone.
/// \param rrclass The class of the zone.
ZoneWriterLocal(ZoneTableSegmentLocal* segment,
const LoadAction& load_action, const dns::Name& name,
const dns::RRClass& rrclass);
/// \brief Destructor
~ZoneWriterLocal();
/// \brief Loads the data.
///
/// This calls the load_action (passed to constructor) and stores the
/// data for future use.
///
/// \throw isc::InvalidOperation if it is called the second time in
/// lifetime of the object.
/// \throw Whatever the load_action throws, it is propagated up.
virtual void load();
/// \brief Installs the zone.
///
/// It modifies the zone table accessible through the segment (passed to
/// constructor).
///
/// \throw isc::InvalidOperation if it is called the second time in
/// lifetime of the object or if load() was not called previously or if
/// cleanup() was already called.
virtual void install();
/// \brief Clean up memory.
///
/// Cleans up the memory used by load()ed zone if not yet installed, or
/// the old zone replaced by install().
virtual void cleanup();
private:
ZoneTableSegmentLocal* segment_;
LoadAction load_action_;
dns::Name origin_;
dns::RRClass rrclass_;
ZoneData* zone_data_;
enum State {
ZW_UNUSED,
ZW_LOADED,
ZW_INSTALLED,
ZW_CLEANED
};
State state_;
};
}
}
}
#endif
......@@ -33,6 +33,7 @@ run_unittests_SOURCES += memory_segment_test.h
run_unittests_SOURCES += segment_object_holder_unittest.cc
run_unittests_SOURCES += memory_client_unittest.cc
run_unittests_SOURCES += zone_table_segment_unittest.cc
run_unittests_SOURCES += zone_writer_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
......
......@@ -13,8 +13,14 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <datasrc/memory/zone_table_segment.h>
#include <datasrc/memory/zone_writer_local.h>
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
using boost::scoped_ptr;
using isc::dns::Name;
using isc::dns::RRClass;
using namespace isc::datasrc::memory;
using namespace isc::data;
using namespace isc::util;
......@@ -80,4 +86,22 @@ TEST_F(ZoneTableSegmentTest, getMemorySegment) {
EXPECT_TRUE(mem_sgmt.allMemoryDeallocated());
}
ZoneData*
loadAction(MemorySegment&) {
// The function won't be called, so this is OK
return (NULL);
}
// Test we can get a writer.
TEST_F(ZoneTableSegmentTest, getZoneWriter) {
scoped_ptr<ZoneWriter>
writer(segment_->getZoneWriter(loadAction, Name("example.org"),
RRClass::IN()));
// We have to get something
EXPECT_NE(static_cast<void*>(NULL), writer.get());
// And for now, it should be the local writer
EXPECT_NE(static_cast<void*>(NULL),
dynamic_cast<ZoneWriterLocal*>(writer.get()));
}
} // anonymous namespace
// 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 <datasrc/memory/zone_writer_local.h>
#include <datasrc/memory/zone_table_segment_local.h>
#include <datasrc/memory/zone_data.h>
#include <cc/data.h>
#include <dns/rrclass.h>
#include <dns/name.h>
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
#include <boost/bind.hpp>
using boost::scoped_ptr;
using boost::bind;
using isc::dns::RRClass;
using isc::dns::Name;
using namespace isc::datasrc::memory;
namespace {
class TestException {};
class ZoneWriterLocalTest : public ::testing::Test {
public:
ZoneWriterLocalTest() :
// FIXME: The NullElement probably isn't the best one, but we don't
// know how the config will look, so it just fills the argument
// (which is currently ignored)
segment_(ZoneTableSegment::create(isc::data::NullElement())),
writer_(new
ZoneWriterLocal(dynamic_cast<ZoneTableSegmentLocal*>(segment_.
get()),
bind(&ZoneWriterLocalTest::loadAction, this, _1),
Name("example.org"), RRClass::IN())),
load_called_(false),
load_throw_(false),
load_null_(false),
load_data_(false)
{
// TODO: The setTable is only a temporary interface
segment_->getHeader().
setTable(ZoneTable::create(segment_->getMemorySegment(),
RRClass::IN()));
}
void TearDown() {
// Release the writer
writer_.reset();
// Release the table we used
ZoneTable::destroy(segment_->getMemorySegment(),
segment_->getHeader().getTable(), RRClass::IN());
// And check we freed all memory
EXPECT_TRUE(segment_->getMemorySegment().allMemoryDeallocated());
}
protected:
scoped_ptr<ZoneTableSegment> segment_;
scoped_ptr<ZoneWriterLocal> writer_;
bool load_called_;
bool load_throw_;
bool load_null_;
bool load_data_;
private:
ZoneData* loadAction(isc::util::MemorySegment& segment) {
// Make sure it is the correct segment passed. We know the
// exact instance, can compare pointers to them.
EXPECT_EQ(&segment_->getMemorySegment(), &segment);
// We got called
load_called_ = true;
if (load_throw_) {
throw TestException();
}
if (load_null_) {
// Be nasty to the caller and return NULL, which is forbidden
return (NULL);
}
ZoneData* data = ZoneData::create(segment, Name("example.org"));
if (load_data_) {
// Put something inside. The node itself should be enough for
// the tests.
ZoneNode* node(NULL);
data->insertName(segment, Name("subdomain.example.org"), &node);
EXPECT_NE(static_cast<ZoneNode*>(NULL), node);
}
return (data);
}
};
// We call it the way we are supposed to, check every callback is called in the
// right moment.
TEST_F(ZoneWriterLocalTest, correctCall) {
// Nothing called before we call it
EXPECT_FALSE(load_called_);
// Just the load gets called now
EXPECT_NO_THROW(writer_->load());
EXPECT_TRUE(load_called_);
load_called_ = false;
EXPECT_NO_THROW(writer_->install());
EXPECT_FALSE(load_called_);
// We don't check explicitly how this works, but call it to free memory. If
// everything is freed should be checked inside the TearDown.
EXPECT_NO_THROW(writer_->cleanup());
}
TEST_F(ZoneWriterLocalTest, loadTwice) {
// Load it the first time
EXPECT_NO_THROW(writer_->load());